diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 2aa40c8dc8..bd4e9b444c 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -46,7 +46,6 @@ GameScreen::~GameScreen() { void GameScreen::CreateViews() { GameInfo *info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); - g_gameInfoCache->WaitUntilDone(info); I18NCategory *di = GetI18NCategory("Dialog"); I18NCategory *ga = GetI18NCategory("Game"); @@ -67,18 +66,31 @@ void GameScreen::CreateViews() { leftColumn->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &GameScreen::OnSwitchBack); if (info) { texvGameIcon_ = leftColumn->Add(new Thin3DTextureView(0, IS_DEFAULT, new AnchorLayoutParams(144 * 2, 80 * 2, 10, 10, NONE, NONE))); - tvTitle_ = leftColumn->Add(new TextView(info->GetTitle(), ALIGN_LEFT, false, new AnchorLayoutParams(10, 200, NONE, NONE))); + + LinearLayout *infoLayout = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(10, 200, NONE, NONE)); + leftColumn->Add(infoLayout); + + tvTitle_ = infoLayout->Add(new TextView(info->GetTitle(), ALIGN_LEFT | FLAG_WRAP_TEXT, false, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvTitle_->SetShadow(true); + infoLayout->Add(new Spacer(12)); // This one doesn't need to be updated. - leftColumn->Add(new TextView(gamePath_, ALIGN_LEFT, true, new AnchorLayoutParams(10, 250, NONE, NONE)))->SetShadow(true); - tvGameSize_ = leftColumn->Add(new TextView("...", ALIGN_LEFT, true, new AnchorLayoutParams(10, 290, NONE, NONE))); + infoLayout->Add(new TextView(gamePath_, ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetShadow(true); + tvGameSize_ = infoLayout->Add(new TextView("...", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvGameSize_->SetShadow(true); - tvSaveDataSize_ = leftColumn->Add(new TextView("...", ALIGN_LEFT, true, new AnchorLayoutParams(10, 320, NONE, NONE))); + tvSaveDataSize_ = infoLayout->Add(new TextView("...", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvSaveDataSize_->SetShadow(true); - tvInstallDataSize_ = leftColumn->Add(new TextView("", ALIGN_LEFT, true, new AnchorLayoutParams(10, 350, NONE, NONE))); + tvInstallDataSize_ = infoLayout->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvInstallDataSize_->SetShadow(true); - tvRegion_ = leftColumn->Add(new TextView("", ALIGN_LEFT, true, new AnchorLayoutParams(10, 380, NONE, NONE))); + tvInstallDataSize_->SetVisibility(V_GONE); + tvRegion_ = infoLayout->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvRegion_->SetShadow(true); + } else { + texvGameIcon_ = nullptr; + tvTitle_ = nullptr; + tvGameSize_ = nullptr; + tvSaveDataSize_ = nullptr; + tvInstallDataSize_ = nullptr; + tvRegion_ = nullptr; } ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); @@ -87,39 +99,54 @@ void GameScreen::CreateViews() { LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL); rightColumnItems->SetSpacing(0.0f); rightColumn->Add(rightColumnItems); - Choice *play = new Choice(ga->T("Play")); - rightColumnItems->Add(play)->OnClick.Handle(this, &GameScreen::OnPlay); - if (info && !info->id.empty()) - { - if (info->hasConfig) - { - rightColumnItems->Add(new Choice(ga->T("Game Settings")))->OnClick.Handle(this, &GameScreen::OnGameSettings); - rightColumnItems->Add(new Choice(ga->T("Delete Game Config")))->OnClick.Handle(this, &GameScreen::OnDeleteConfig); - } - else - { - rightColumnItems->Add(new Choice(ga->T("Create Game Config")))->OnClick.Handle(this, &GameScreen::OnCreateConfig); - } + + rightColumnItems->Add(new Choice(ga->T("Play")))->OnClick.Handle(this, &GameScreen::OnPlay); + + if (info) { + btnGameSettings_ = rightColumnItems->Add(new Choice(ga->T("Game Settings"))); + btnGameSettings_->OnClick.Handle(this, &GameScreen::OnGameSettings); + btnDeleteGameConfig_ = rightColumnItems->Add(new Choice(ga->T("Delete Game Config"))); + btnDeleteGameConfig_->OnClick.Handle(this, &GameScreen::OnDeleteConfig); + btnCreateGameConfig_ = rightColumnItems->Add(new Choice(ga->T("Create Game Config"))); + btnCreateGameConfig_->OnClick.Handle(this, &GameScreen::OnCreateConfig); + + btnGameSettings_->SetVisibility(V_GONE); + btnDeleteGameConfig_->SetVisibility(V_GONE); + btnCreateGameConfig_->SetVisibility(V_GONE); + + btnDeleteSaveData_ = new Choice(ga->T("Delete Save Data")); + rightColumnItems->Add(btnDeleteSaveData_)->OnClick.Handle(this, &GameScreen::OnDeleteSaveData); + btnDeleteSaveData_->SetVisibility(V_GONE); + } else { + btnGameSettings_ = nullptr; + btnCreateGameConfig_ = nullptr; + btnDeleteGameConfig_ = nullptr; + btnDeleteSaveData_ = nullptr; } - std::vector saveDirs = info->GetSaveDataDirectories(); - if (saveDirs.size()) { - rightColumnItems->Add(new Choice(ga->T("Delete Save Data")))->OnClick.Handle(this, &GameScreen::OnDeleteSaveData); - } - rightColumnItems->Add(new Choice(ga->T("Delete Game")))->OnClick.Handle(this, &GameScreen::OnDeleteGame); + + + rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Delete Game"))))->OnClick.Handle(this, &GameScreen::OnDeleteGame); if (host->CanCreateShortcut()) { - rightColumnItems->Add(new Choice(ga->T("Create Shortcut")))->OnClick.Handle(this, &GameScreen::OnCreateShortcut); + rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Create Shortcut"))))->OnClick.Handle(this, &GameScreen::OnCreateShortcut); } if (isRecentGame(gamePath_)) { - rightColumnItems->Add(new Choice(ga->T("Remove From Recent")))->OnClick.Handle(this, &GameScreen::OnRemoveFromRecent); + rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Remove From Recent"))))->OnClick.Handle(this, &GameScreen::OnRemoveFromRecent); } #ifdef _WIN32 - rightColumnItems->Add(new Choice(ga->T("Show In Folder")))->OnClick.Handle(this, &GameScreen::OnShowInFolder); + rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Show In Folder"))))->OnClick.Handle(this, &GameScreen::OnShowInFolder); #endif if (g_Config.bEnableCheats) { - rightColumnItems->Add(new Choice(pa->T("Cheats")))->OnClick.Handle(this, &GameScreen::OnCwCheat); + rightColumnItems->Add(AddOtherChoice(new Choice(pa->T("Cheats"))))->OnClick.Handle(this, &GameScreen::OnCwCheat); } } +UI::Choice *GameScreen::AddOtherChoice(UI::Choice *choice) { + otherChoices_.push_back(choice); + // While loading. + choice->SetVisibility(UI::V_GONE); + return choice; +} + UI::EventReturn GameScreen::OnCreateConfig(UI::EventParams &e) { GameInfo *info = g_gameInfoCache->GetInfo(NULL, gamePath_,0); @@ -187,6 +214,7 @@ void GameScreen::update(InputState &input) { if (info->installDataSize > 0) { sprintf(temp, "%s: %1.2f %s", ga->T("InstallData"), (float) (info->installDataSize) / 1024.f / 1024.f, ga->T("MB")); tvInstallDataSize_->SetText(temp); + tvInstallDataSize_->SetVisibility(UI::V_VISIBLE); } } @@ -200,6 +228,23 @@ void GameScreen::update(InputState &input) { }; tvRegion_->SetText(ga->T(regionNames[info->region])); } + + if (!info->id.empty()) { + btnGameSettings_->SetVisibility(info->hasConfig ? UI::V_VISIBLE : UI::V_GONE); + btnDeleteGameConfig_->SetVisibility(info->hasConfig ? UI::V_VISIBLE : UI::V_GONE); + btnCreateGameConfig_->SetVisibility(info->hasConfig ? UI::V_GONE : UI::V_VISIBLE); + + std::vector saveDirs = info->GetSaveDataDirectories(); + if (saveDirs.size()) { + btnDeleteSaveData_->SetVisibility(UI::V_VISIBLE); + } + } + if (!info->IsPending()) { + // At this point, the above buttons won't become visible. We can show these now. + for (UI::Choice *choice : otherChoices_) { + choice->SetVisibility(UI::V_VISIBLE); + } + } } UI::EventReturn GameScreen::OnShowInFolder(UI::EventParams &e) { diff --git a/UI/GameScreen.h b/UI/GameScreen.h index a97e808569..601df037ac 100644 --- a/UI/GameScreen.h +++ b/UI/GameScreen.h @@ -44,6 +44,8 @@ protected: bool isRecentGame(const std::string &gamePath); private: + UI::Choice *AddOtherChoice(UI::Choice *choice); + // Event handlers UI::EventReturn OnPlay(UI::EventParams &e); UI::EventReturn OnGameSettings(UI::EventParams &e); @@ -64,4 +66,10 @@ private: UI::TextView *tvSaveDataSize_; UI::TextView *tvInstallDataSize_; UI::TextView *tvRegion_; + + UI::Choice *btnGameSettings_; + UI::Choice *btnCreateGameConfig_; + UI::Choice *btnDeleteGameConfig_; + UI::Choice *btnDeleteSaveData_; + std::vector otherChoices_; }; diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index ec68e3f410..5d30ce364c 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -284,7 +284,7 @@ void GameButton::Draw(UIContext &dc) { title_ = ReplaceAll(title_, "\n", " "); } - dc.MeasureText(dc.GetFontStyle(), title_.c_str(), &tw, &th, 0); + dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, title_.c_str(), &tw, &th, 0); int availableWidth = bounds_.w - 150; float sineWidth = std::max(0.0f, (tw - availableWidth)) / 2.0f; @@ -367,7 +367,7 @@ void DirButton::Draw(UIContext &dc) { } float tw, th; - dc.MeasureText(dc.GetFontStyle(), text.c_str(), &tw, &th, 0); + dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, text.c_str(), &tw, &th, 0); bool compact = bounds_.w < 180; @@ -484,15 +484,14 @@ void GameBrowser::Refresh() { LinearLayout *topBar = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); if (allowBrowsing_) { topBar->Add(new Spacer(2.0f)); - Margins pathMargins(5, 0); - topBar->Add(new TextView(path_.GetFriendlyPath().c_str(), ALIGN_VCENTER, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f))); + topBar->Add(new TextView(path_.GetFriendlyPath().c_str(), ALIGN_VCENTER | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f))); #if defined(_WIN32) || defined(USING_QT_UI) - topBar->Add(new Choice(mm->T("Browse", "Browse...")))->OnClick.Handle(this, &GameBrowser::HomeClick); + topBar->Add(new Choice(mm->T("Browse", "Browse..."), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick); #else - topBar->Add(new Choice(mm->T("Home")))->OnClick.Handle(this, &GameBrowser::HomeClick); + topBar->Add(new Choice(mm->T("Home"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick); #endif } else { - topBar->Add(new Spacer(new LinearLayoutParams(1.0f))); + topBar->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f))); } ChoiceStrip *layoutChoice = topBar->Add(new ChoiceStrip(ORIENT_HORIZONTAL)); diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index eb4646e5a2..cd9152d837 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -16,7 +16,7 @@ void OnScreenMessagesView::Draw(UIContext &dc) { // Get height float w, h; - dc.MeasureText(dc.theme->uiFont, "Wg", &w, &h); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, "Wg", &w, &h); float y = 10.0f; // Then draw them all. @@ -27,7 +27,7 @@ void OnScreenMessagesView::Draw(UIContext &dc) { if (alpha < 0.0) alpha = 0.0f; // Messages that are wider than the screen are left-aligned instead of centered. float tw, th; - dc.MeasureText(dc.theme->uiFont, iter->text.c_str(), &tw, &th); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, iter->text.c_str(), &tw, &th); float x = bounds_.centerX(); int align = ALIGN_TOP | ALIGN_HCENTER; if (tw > bounds_.w) { diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 290ae9ac63..75ee3974b5 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -51,20 +51,28 @@ std::string GetFileDateAsString(std::string filename) { return ""; } +static std::string TrimString(const std::string &str) { + size_t pos = str.find_last_not_of(" \r\n\t"); + if (pos != str.npos) { + return str.substr(0, pos + 1); + } + return str; +} + class SavedataPopupScreen : public PopupScreen { public: - SavedataPopupScreen(std::string savePath, std::string title) : PopupScreen(title), savePath_(savePath) { + SavedataPopupScreen(std::string savePath, std::string title) : PopupScreen(TrimString(title)), savePath_(savePath) { } void CreatePopupContents(UI::ViewGroup *parent) override { using namespace UI; GameInfo *ginfo = g_gameInfoCache->GetInfo(screenManager()->getThin3DContext(), savePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); - LinearLayout *root = new LinearLayout(ORIENT_VERTICAL); - parent->Add(root); + LinearLayout *content = new LinearLayout(ORIENT_VERTICAL); + parent->Add(content); if (!ginfo) return; LinearLayout *toprow = new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); - root->Add(toprow); + content->Add(toprow); I18NCategory *sa = GetI18NCategory("Savedata"); if (ginfo->fileType == FILETYPE_PSP_SAVEDATA_DIRECTORY) { @@ -74,15 +82,15 @@ public: if (ginfo->iconTexture) { toprow->Add(new Thin3DTextureView(ginfo->iconTexture, IS_FIXED, new LinearLayoutParams(Margins(10, 5)))); } - LinearLayout *topright = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0)); + LinearLayout *topright = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1.0f)); topright->SetSpacing(1.0f); - topright->Add(new TextView(savedata_title, 0, false)); + topright->Add(new TextView(savedata_title, ALIGN_LEFT | FLAG_WRAP_TEXT, false)); topright->Add(new TextView(StringFromFormat("%d kB", ginfo->gameSize / 1024), 0, true)); topright->Add(new TextView(GetFileDateAsString(savePath_ + "/PARAM.SFO"), 0, true)); toprow->Add(topright); - root->Add(new Spacer(3.0)); - root->Add(new TextView(savedata_detail, 0, true, new LinearLayoutParams(Margins(10, 0)))); - root->Add(new Spacer(3.0)); + content->Add(new Spacer(3.0)); + content->Add(new TextView(ReplaceAll(savedata_detail, "\r", ""), ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(Margins(10, 0)))); + content->Add(new Spacer(3.0)); } else { std::string image_path = ReplaceAll(savePath_, ".ppst", ".jpg"); if (File::Exists(image_path)) { @@ -91,14 +99,14 @@ public: } else { toprow->Add(new TextView(sa->T("No screenshot"), new LinearLayoutParams(Margins(10, 5)))); } - root->Add(new TextView(GetFileDateAsString(savePath_), 0, true, new LinearLayoutParams(Margins(10, 5)))); + content->Add(new TextView(GetFileDateAsString(savePath_), 0, true, new LinearLayoutParams(Margins(10, 5)))); } I18NCategory *di = GetI18NCategory("Dialog"); LinearLayout *buttons = new LinearLayout(ORIENT_HORIZONTAL); buttons->Add(new Button(di->T("Back"), new LinearLayoutParams(1.0)))->OnClick.Handle(this, &UIScreen::OnBack); buttons->Add(new Button(di->T("Delete"), new LinearLayoutParams(1.0)))->OnClick.Handle(this, &SavedataPopupScreen::OnDeleteButtonClick); - root->Add(buttons); + content->Add(buttons); } protected: @@ -243,7 +251,7 @@ void SavedataButton::Draw(UIContext &dc) { subtitle_ = CleanSaveString(savedata_title) + StringFromFormat(" (%d kB)", ginfo->gameSize / 1024); } - dc.MeasureText(dc.GetFontStyle(), title_.c_str(), &tw, &th, 0); + dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, title_.c_str(), &tw, &th, 0); int availableWidth = bounds_.w - 150; float sineWidth = std::max(0.0f, (tw - availableWidth)) / 2.0f; diff --git a/ext/native/ui/ui_context.cpp b/ext/native/ui/ui_context.cpp index 979b1afe6a..bb48e5ab04 100644 --- a/ext/native/ui/ui_context.cpp +++ b/ext/native/ui/ui_context.cpp @@ -127,36 +127,31 @@ void UIContext::SetFontStyle(const UI::FontStyle &fontStyle) { } } -void UIContext::MeasureText(const UI::FontStyle &style, const char *str, float *x, float *y, int align) const { - MeasureTextCount(style, str, (int)strlen(str), x, y, align); +void UIContext::MeasureText(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, float *x, float *y, int align) const { + MeasureTextCount(style, scaleX, scaleY, str, (int)strlen(str), x, y, align); } -void UIContext::MeasureTextCount(const UI::FontStyle &style, const char *str, int count, float *x, float *y, int align) const { +void UIContext::MeasureTextCount(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, int count, float *x, float *y, int align) const { if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) { float sizeFactor = (float)style.sizePts / 24.0f; - Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor); + Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor); Draw()->MeasureTextCount(style.atlasFont, str, count, x, y); } else { textDrawer_->SetFont(style.fontName.c_str(), style.sizePts, style.flags); - textDrawer_->SetFontScale(fontScaleX_, fontScaleY_); + textDrawer_->SetFontScale(scaleX, scaleY); textDrawer_->MeasureString(str, count, x, y); textDrawer_->SetFont(fontStyle_->fontName.c_str(), fontStyle_->sizePts, fontStyle_->flags); } } -void UIContext::MeasureTextRect(const UI::FontStyle &style, const char *str, int count, const Bounds &bounds, float *x, float *y, int align) const { - if ((align & FLAG_WRAP_TEXT) == 0) { - MeasureTextCount(style, str, count, x, y, align); - return; - } - +void UIContext::MeasureTextRect(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, int count, const Bounds &bounds, float *x, float *y, int align) const { if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) { float sizeFactor = (float)style.sizePts / 24.0f; - Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor); + Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor); Draw()->MeasureTextRect(style.atlasFont, str, count, bounds, x, y, align); } else { textDrawer_->SetFont(style.fontName.c_str(), style.sizePts, style.flags); - textDrawer_->SetFontScale(fontScaleX_, fontScaleY_); + textDrawer_->SetFontScale(scaleX, scaleY); textDrawer_->MeasureStringRect(str, count, bounds, x, y, align); textDrawer_->SetFont(fontStyle_->fontName.c_str(), fontStyle_->sizePts, fontStyle_->flags); } diff --git a/ext/native/ui/ui_context.h b/ext/native/ui/ui_context.h index 22f944148c..ccb0a7ddca 100644 --- a/ext/native/ui/ui_context.h +++ b/ext/native/ui/ui_context.h @@ -59,9 +59,9 @@ public: void SetFontStyle(const UI::FontStyle &style); const UI::FontStyle &GetFontStyle() { return *fontStyle_; } void SetFontScale(float scaleX, float scaleY); - void MeasureTextCount(const UI::FontStyle &style, const char *str, int count, float *x, float *y, int align = 0) const; - void MeasureText(const UI::FontStyle &style, const char *str, float *x, float *y, int align = 0) const; - void MeasureTextRect(const UI::FontStyle &style, const char *str, int count, const Bounds &bounds, float *x, float *y, int align = 0) const; + void MeasureTextCount(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, int count, float *x, float *y, int align = 0) const; + void MeasureText(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, float *x, float *y, int align = 0) const; + void MeasureTextRect(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, int count, const Bounds &bounds, float *x, float *y, int align = 0) const; void DrawText(const char *str, float x, float y, uint32_t color, int align = 0); void DrawTextShadow(const char *str, float x, float y, uint32_t color, int align = 0); void DrawTextRect(const char *str, const Bounds &bounds, uint32_t color, int align = 0); diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index e4acf73df6..3914296e89 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -313,6 +313,14 @@ UI::EventReturn ListPopupScreen::OnListChoice(UI::EventParams &e) { namespace UI { +std::string ChopTitle(const std::string &title) { + size_t pos = title.find('\n'); + if (pos != title.npos) { + return title.substr(0, pos); + } + return title; +} + UI::EventReturn PopupMultiChoice::HandleClick(UI::EventParams &e) { restoreFocus_ = HasFocus(); @@ -323,7 +331,7 @@ UI::EventReturn PopupMultiChoice::HandleClick(UI::EventParams &e) { choices.push_back(category ? category->T(choices_[i]) : choices_[i]); } - ListPopupScreen *popupScreen = new ListPopupScreen(text_, choices, *value_ - minVal_, + ListPopupScreen *popupScreen = new ListPopupScreen(ChopTitle(text_), choices, *value_ - minVal_, std::bind(&PopupMultiChoice::ChoiceCallback, this, placeholder::_1)); popupScreen->SetHiddenChoices(hidden_); screenManager_->push(popupScreen); @@ -369,7 +377,7 @@ void PopupMultiChoice::Draw(UIContext &dc) { dc.SetFontStyle(dc.theme->uiFont); float ignore; - dc.MeasureText(dc.theme->uiFont, valueText_.c_str(), &textPadding_.right, &ignore, ALIGN_RIGHT | ALIGN_VCENTER); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, valueText_.c_str(), &textPadding_.right, &ignore, ALIGN_RIGHT | ALIGN_VCENTER); textPadding_.right += paddingX; Choice::Draw(dc); @@ -403,7 +411,7 @@ PopupSliderChoiceFloat::PopupSliderChoiceFloat(float *value, float minValue, flo EventReturn PopupSliderChoice::HandleClick(EventParams &e) { restoreFocus_ = HasFocus(); - SliderPopupScreen *popupScreen = new SliderPopupScreen(value_, minValue_, maxValue_, text_, step_, units_); + SliderPopupScreen *popupScreen = new SliderPopupScreen(value_, minValue_, maxValue_, ChopTitle(text_), step_, units_); popupScreen->OnChange.Handle(this, &PopupSliderChoice::HandleChange); screenManager_->push(popupScreen); return EVENT_DONE; @@ -435,7 +443,7 @@ void PopupSliderChoice::Draw(UIContext &dc) { } float ignore; - dc.MeasureText(dc.theme->uiFont, temp, &textPadding_.right, &ignore, ALIGN_RIGHT | ALIGN_VCENTER); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, temp, &textPadding_.right, &ignore, ALIGN_RIGHT | ALIGN_VCENTER); textPadding_.right += paddingX; Choice::Draw(dc); @@ -445,7 +453,7 @@ void PopupSliderChoice::Draw(UIContext &dc) { EventReturn PopupSliderChoiceFloat::HandleClick(EventParams &e) { restoreFocus_ = HasFocus(); - SliderFloatPopupScreen *popupScreen = new SliderFloatPopupScreen(value_, minValue_, maxValue_, text_, step_, units_); + SliderFloatPopupScreen *popupScreen = new SliderFloatPopupScreen(value_, minValue_, maxValue_, ChopTitle(text_), step_, units_); popupScreen->OnChange.Handle(this, &PopupSliderChoiceFloat::HandleChange); screenManager_->push(popupScreen); return EVENT_DONE; @@ -477,7 +485,7 @@ void PopupSliderChoiceFloat::Draw(UIContext &dc) { } float ignore; - dc.MeasureText(dc.theme->uiFont, temp, &textPadding_.right, &ignore, ALIGN_RIGHT | ALIGN_VCENTER); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, temp, &textPadding_.right, &ignore, ALIGN_RIGHT | ALIGN_VCENTER); textPadding_.right += paddingX; Choice::Draw(dc); @@ -652,7 +660,7 @@ PopupTextInputChoice::PopupTextInputChoice(std::string *value, const std::string EventReturn PopupTextInputChoice::HandleClick(EventParams &e) { restoreFocus_ = HasFocus(); - TextEditPopupScreen *popupScreen = new TextEditPopupScreen(value_, placeHolder_, text_, maxLen_); + TextEditPopupScreen *popupScreen = new TextEditPopupScreen(value_, placeHolder_, ChopTitle(text_), maxLen_); popupScreen->OnChange.Handle(this, &PopupTextInputChoice::HandleChange); screenManager_->push(popupScreen); return EVENT_DONE; @@ -667,7 +675,7 @@ void PopupTextInputChoice::Draw(UIContext &dc) { dc.SetFontStyle(dc.theme->uiFont); float ignore; - dc.MeasureText(dc.theme->uiFont, value_->c_str(), &textPadding_.right, &ignore, ALIGN_RIGHT | ALIGN_VCENTER); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, value_->c_str(), &textPadding_.right, &ignore, ALIGN_RIGHT | ALIGN_VCENTER); textPadding_.right += paddingX; Choice::Draw(dc); @@ -726,7 +734,7 @@ void ChoiceWithValueDisplay::Draw(UIContext &dc) { } float ignore; - dc.MeasureText(dc.theme->uiFont, valueText.str().c_str(), &textPadding_.right, &ignore, ALIGN_RIGHT | ALIGN_VCENTER); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, valueText.str().c_str(), &textPadding_.right, &ignore, ALIGN_RIGHT | ALIGN_VCENTER); textPadding_.right += paddingX; Choice::Draw(dc); diff --git a/ext/native/ui/view.cpp b/ext/native/ui/view.cpp index b63fcd7862..113101820d 100644 --- a/ext/native/ui/view.cpp +++ b/ext/native/ui/view.cpp @@ -24,7 +24,7 @@ static recursive_mutex mutex_; const float ITEM_HEIGHT = 64.f; const float MIN_TEXT_SCALE = 0.8f; - +const float MAX_ITEM_SIZE = 65535.0f; struct DispatchQueueItem { Event *e; @@ -102,9 +102,10 @@ void MeasureBySpec(Size sz, float contentWidth, MeasureSpec spec, float *measure *measured = contentWidth < spec.size ? contentWidth : spec.size; else if (spec.type == EXACTLY) *measured = spec.size; - } else if (sz == FILL_PARENT) { + } else if (sz == FILL_PARENT) { + // UNSPECIFIED may have a minimum size of the parent. Let's use it to fill. if (spec.type == UNSPECIFIED) - *measured = contentWidth; // We have no value to set + *measured = std::max(spec.size, contentWidth); else *measured = spec.size; } else if (spec.type == EXACTLY || (spec.type == AT_MOST && *measured > spec.size)) { @@ -112,6 +113,24 @@ void MeasureBySpec(Size sz, float contentWidth, MeasureSpec spec, float *measure } } +void ApplyBoundBySpec(float &bound, MeasureSpec spec) { + switch (spec.type) { + case AT_MOST: + bound = bound < spec.size ? bound : spec.size; + break; + case EXACTLY: + bound = spec.size; + break; + case UNSPECIFIED: + break; + } +} + +void ApplyBoundsBySpec(Bounds &bounds, MeasureSpec horiz, MeasureSpec vert) { + ApplyBoundBySpec(bounds.w, horiz); + ApplyBoundBySpec(bounds.h, vert); +} + void Event::Add(std::function func) { HandlerRegistration reg; reg.func = func; @@ -142,7 +161,7 @@ View::~View() { void View::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) { float contentW = 0.0f, contentH = 0.0f; - GetContentDimensions(dc, contentW, contentH); + GetContentDimensionsBySpec(dc, horiz, vert, contentW, contentH); MeasureBySpec(layoutParams_->width, contentW, horiz, &measuredWidth_); MeasureBySpec(layoutParams_->height, contentH, vert, &measuredHeight_); } @@ -154,6 +173,10 @@ void View::GetContentDimensions(const UIContext &dc, float &w, float &h) const { h = 10.0f; } +void View::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const { + GetContentDimensions(dc, w, h); +} + void View::Query(float x, float y, std::vector &list) { if (bounds_.Contains(x, y)) { list.push_back(this); @@ -390,14 +413,13 @@ void Item::GetContentDimensions(const UIContext &dc, float &w, float &h) const { void ClickableItem::GetContentDimensions(const UIContext &dc, float &w, float &h) const { w = 0.0f; - h = 0.0f; + h = ITEM_HEIGHT; } ClickableItem::ClickableItem(LayoutParams *layoutParams) : Clickable(layoutParams) { if (!layoutParams) { if (layoutParams_->width == WRAP_CONTENT) layoutParams_->width = FILL_PARENT; - layoutParams_->height = ITEM_HEIGHT; } } @@ -414,16 +436,35 @@ void ClickableItem::Draw(UIContext &dc) { dc.FillRect(style.background, bounds_); } -void Choice::GetContentDimensions(const UIContext &dc, float &w, float &h) const { +void Choice::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const { if (atlasImage_ != -1) { const AtlasImage &img = dc.Draw()->GetAtlas()->images[atlasImage_]; w = img.w; h = img.h; } else { - dc.MeasureText(dc.theme->uiFont, text_.c_str(), &w, &h); + const int paddingX = 12; + float availWidth = horiz.size - paddingX * 2 - textPadding_.horiz(); + if (availWidth < 0.0f) { + // Let it have as much space as it needs. + availWidth = MAX_ITEM_SIZE; + } + float scale = CalculateTextScale(dc, availWidth); + Bounds availBounds(0, 0, availWidth, vert.size); + dc.MeasureTextRect(dc.theme->uiFont, scale, scale, text_.c_str(), (int)text_.size(), availBounds, &w, &h, FLAG_WRAP_TEXT); } w += 24; h += 16; + h = std::max(h, ITEM_HEIGHT); +} + +float Choice::CalculateTextScale(const UIContext &dc, float availWidth) const { + float actualWidth, actualHeight; + Bounds availBounds(0, 0, availWidth, bounds_.h); + dc.MeasureTextRect(dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), (int)text_.size(), availBounds, &actualWidth, &actualHeight); + if (actualWidth > availWidth) { + return std::max(MIN_TEXT_SCALE, availWidth / actualWidth); + } + return 1.0f; } void Choice::HighlightChanged(bool highlighted){ @@ -459,23 +500,18 @@ void Choice::Draw(UIContext &dc) { const int paddingX = 12; const float availWidth = bounds_.w - paddingX * 2 - textPadding_.horiz(); - float scale = 1.0f; - - float actualWidth, actualHeight; - dc.MeasureText(dc.theme->uiFont, text_.c_str(), &actualWidth, &actualHeight, ALIGN_VCENTER); - if (actualWidth > availWidth) { - scale = std::max(MIN_TEXT_SCALE, availWidth / actualWidth); - } + float scale = CalculateTextScale(dc, availWidth); dc.SetFontScale(scale, scale); if (centered_) { - dc.DrawText(text_.c_str(), bounds_.centerX(), bounds_.centerY(), style.fgColor, ALIGN_CENTER); + dc.DrawTextRect(text_.c_str(), bounds_, style.fgColor, ALIGN_CENTER | FLAG_WRAP_TEXT); } else { if (iconImage_ != -1) { dc.Draw()->DrawImage(iconImage_, bounds_.x2() - 32 - paddingX, bounds_.centerY(), 0.5f, style.fgColor, ALIGN_CENTER); } - dc.DrawText(text_.c_str(), bounds_.x + paddingX + textPadding_.left, bounds_.centerY(), style.fgColor, ALIGN_VCENTER); + Bounds textBounds(bounds_.x + paddingX + textPadding_.left, bounds_.y, availWidth, bounds_.h); + dc.DrawTextRect(text_.c_str(), textBounds, style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT); } dc.SetFontScale(1.0f, 1.0f); } @@ -518,7 +554,7 @@ void PopupHeader::Draw(UIContext &dc) { float tw, th; dc.SetFontStyle(dc.theme->uiFont); - dc.MeasureText(dc.GetFontStyle(), text_.c_str(), &tw, &th, 0); + dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, text_.c_str(), &tw, &th, 0); float sineWidth = std::max(0.0f, (tw - availableWidth)) / 2.0f; @@ -565,27 +601,54 @@ void CheckBox::Draw(UIContext &dc) { const int paddingX = 12; // Padding right of the checkbox image too. const float availWidth = bounds_.w - paddingX * 2 - imageW - paddingX; - float scale = 1.0f; - - float actualWidth, actualHeight; - dc.MeasureText(dc.theme->uiFont, text_.c_str(), &actualWidth, &actualHeight, ALIGN_VCENTER); - if (actualWidth > availWidth) { - scale = std::max(MIN_TEXT_SCALE, availWidth / actualWidth); - } + float scale = CalculateTextScale(dc, availWidth); dc.SetFontScale(scale, scale); - dc.DrawText(text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), style.fgColor, ALIGN_VCENTER); + Bounds textBounds(bounds_.x + paddingX, bounds_.y, availWidth, bounds_.h); + dc.DrawTextRect(text_.c_str(), textBounds, style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT); dc.Draw()->DrawImage(image, bounds_.x2() - paddingX, bounds_.centerY(), 1.0f, style.fgColor, ALIGN_RIGHT | ALIGN_VCENTER); dc.SetFontScale(1.0f, 1.0f); } +float CheckBox::CalculateTextScale(const UIContext &dc, float availWidth) const { + float actualWidth, actualHeight; + Bounds availBounds(0, 0, availWidth, bounds_.h); + dc.MeasureTextRect(dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), (int)text_.size(), availBounds, &actualWidth, &actualHeight, ALIGN_VCENTER); + if (actualWidth > availWidth) { + return std::max(MIN_TEXT_SCALE, availWidth / actualWidth); + } + return 1.0f; +} + +void CheckBox::GetContentDimensions(const UIContext &dc, float &w, float &h) const { + int image = *toggle_ ? dc.theme->checkOn : dc.theme->checkOff; + float imageW, imageH; + dc.Draw()->MeasureImage(image, &imageW, &imageH); + + const int paddingX = 12; + // Padding right of the checkbox image too. + float availWidth = bounds_.w - paddingX * 2 - imageW - paddingX; + if (availWidth < 0.0f) { + // Let it have as much space as it needs. + availWidth = MAX_ITEM_SIZE; + } + float scale = CalculateTextScale(dc, availWidth); + + float actualWidth, actualHeight; + Bounds availBounds(0, 0, availWidth, bounds_.h); + dc.MeasureTextRect(dc.theme->uiFont, scale, scale, text_.c_str(), (int)text_.size(), availBounds, &actualWidth, &actualHeight, ALIGN_VCENTER | FLAG_WRAP_TEXT); + + w = bounds_.w; + h = std::max(actualHeight, ITEM_HEIGHT); +} + void Button::GetContentDimensions(const UIContext &dc, float &w, float &h) const { if (imageID_ != -1) { const AtlasImage *img = &dc.Draw()->GetAtlas()->images[imageID_]; w = img->w; h = img->h; } else { - dc.MeasureText(dc.theme->uiFont, text_.c_str(), &w, &h); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), &w, &h); } // Add some internal padding to not look totally ugly w += 16; @@ -602,7 +665,7 @@ void Button::Draw(UIContext &dc) { // dc.Draw()->DrawImage4Grid(style.image, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y2(), style.bgColor); dc.FillRect(style.background, bounds_); float tw, th; - dc.MeasureText(dc.theme->uiFont, text_.c_str(), &tw, &th); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), &tw, &th); if (tw > bounds_.w || imageID_ != -1) { dc.PushScissor(bounds_); } @@ -657,17 +720,22 @@ void Thin3DTextureView::Draw(UIContext &dc) { } } -void TextView::GetContentDimensions(const UIContext &dc, float &w, float &h) const { - // We don't have the bounding w/h yet, so stick with hardset layout params. +void TextView::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const { Bounds bounds(0, 0, layoutParams_->width, layoutParams_->height); - dc.MeasureTextRect(small_ ? dc.theme->uiFontSmall : dc.theme->uiFont, text_.c_str(), (int)text_.length(), bounds, &w, &h, textAlign_); + if (bounds.w < 0) { + // If there's no size, let's grow as big as we want. + bounds.w = horiz.size == 0 ? MAX_ITEM_SIZE : horiz.size; + } + if (bounds.h < 0) { + bounds.h = vert.size == 0 ? MAX_ITEM_SIZE : vert.size; + } + ApplyBoundsBySpec(bounds, horiz, vert); + dc.MeasureTextRect(small_ ? dc.theme->uiFontSmall : dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), (int)text_.length(), bounds, &w, &h, textAlign_); } void TextView::Draw(UIContext &dc) { - float w, h; - GetContentDimensions(dc, w, h); bool clip = false; - if (w > bounds_.w || h > bounds_.h) + if (measuredWidth_ > bounds_.w || measuredHeight_ > bounds_.h) clip = true; if (bounds_.w < 0 || bounds_.h < 0 || !clip_) { // We have a layout but, but try not to screw up rendering. @@ -721,7 +789,7 @@ void TextEdit::Draw(UIContext &dc) { if (HasFocus()) { // Hack to find the caret position. Might want to find a better way... - dc.MeasureTextCount(dc.theme->uiFont, text_.c_str(), caret_, &w, &h, ALIGN_VCENTER | ALIGN_LEFT); + dc.MeasureTextCount(dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), caret_, &w, &h, ALIGN_VCENTER | ALIGN_LEFT); float caretX = w; caretX += textX; @@ -735,7 +803,7 @@ void TextEdit::Draw(UIContext &dc) { } void TextEdit::GetContentDimensions(const UIContext &dc, float &w, float &h) const { - dc.MeasureText(dc.theme->uiFont, text_.size() ? text_.c_str() : "Wj", &w, &h); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, text_.size() ? text_.c_str() : "Wj", &w, &h); w += 2; h += 2; } @@ -904,7 +972,7 @@ void TextEdit::InsertAtCaret(const char *text) { } void ProgressBar::GetContentDimensions(const UIContext &dc, float &w, float &h) const { - dc.MeasureText(dc.theme->uiFont, " 100% ", &w, &h); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, " 100% ", &w, &h); } void ProgressBar::Draw(UIContext &dc) { diff --git a/ext/native/ui/view.h b/ext/native/ui/view.h index 370b5b0483..ec2222cbeb 100644 --- a/ext/native/ui/view.h +++ b/ext/native/ui/view.h @@ -273,10 +273,10 @@ struct Margins { Margins(int8_t horiz, int8_t vert) : top(vert), bottom(vert), left(horiz), right(horiz) {} Margins(int8_t l, int8_t t, int8_t r, int8_t b) : top(t), bottom(b), left(l), right(r) {} - int horiz() { + int horiz() const { return left + right; } - int vert() { + int vert() const { return top + bottom; } @@ -292,10 +292,10 @@ struct Padding { Padding(float horiz, float vert) : top(vert), bottom(vert), left(horiz), right(horiz) {} Padding(float l, float t, float r, float b) : top(t), bottom(b), left(l), right(r) {} - float horiz() { + float horiz() const { return left + right; } - float vert() { + float vert() const { return top + bottom; } @@ -388,6 +388,7 @@ public: // Override this for easy standard behaviour. No need to override Measure. virtual void GetContentDimensions(const UIContext &dc, float &w, float &h) const; + virtual void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const; // Called when the layout is done. void SetBounds(Bounds bounds) { bounds_ = bounds; } @@ -608,7 +609,7 @@ public: : ClickableItem(layoutParams), atlasImage_(image), iconImage_(-1), centered_(false), highlighted_(false), selected_(false) {} virtual void HighlightChanged(bool highlighted); - void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; + void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override; void Draw(UIContext &dc) override; virtual void SetCentered(bool c) { centered_ = c; @@ -620,6 +621,7 @@ public: protected: // hackery virtual bool IsSticky() const { return false; } + virtual float CalculateTextScale(const UIContext &dc, float availWidth) const; std::string text_; std::string smallText_; @@ -708,11 +710,14 @@ public: } void Draw(UIContext &dc) override; + void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; EventReturn OnClicked(EventParams &e); //allow external agents to toggle the checkbox void Toggle(); private: + float CalculateTextScale(const UIContext &dc, float availWidth) const; + bool *toggle_; std::string text_; std::string smallText_; @@ -742,7 +747,7 @@ public: TextView(const std::string &text, int textAlign, bool small, LayoutParams *layoutParams = 0) : InertView(layoutParams), text_(text), textAlign_(textAlign), textColor_(0xFFFFFFFF), small_(small), shadow_(false), focusable_(false), clip_(true) {} - void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; + void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override; void Draw(UIContext &dc) override; void SetText(const std::string &text) { text_ = text; } diff --git a/ext/native/ui/viewgroup.cpp b/ext/native/ui/viewgroup.cpp index 93ec72a6e4..bf3a5e3645 100644 --- a/ext/native/ui/viewgroup.cpp +++ b/ext/native/ui/viewgroup.cpp @@ -405,12 +405,12 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v int numVisible = 0; - for (size_t i = 0; i < views_.size(); i++) { - if (views_[i]->GetVisibility() == V_GONE) + for (View *view : views_) { + if (view->GetVisibility() == V_GONE) continue; numVisible++; - const LinearLayoutParams *linLayoutParams = views_[i]->GetLayoutParams()->As(); + const LinearLayoutParams *linLayoutParams = view->GetLayoutParams()->As(); Margins margins = defaultMargins_; @@ -422,22 +422,23 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v if (orientation_ == ORIENT_HORIZONTAL) { MeasureSpec v = vert; - if (v.type == UNSPECIFIED && measuredHeight_ != 0.0) + if (v.type == UNSPECIFIED && measuredHeight_ != 0.0f) v = MeasureSpec(AT_MOST, measuredHeight_); - views_[i]->Measure(dc, MeasureSpec(UNSPECIFIED, measuredWidth_), v - (float)margins.vert()); + view->Measure(dc, MeasureSpec(UNSPECIFIED, measuredWidth_), v - (float)margins.vert()); } else if (orientation_ == ORIENT_VERTICAL) { MeasureSpec h = horiz; - if (h.type == UNSPECIFIED && measuredWidth_ != 0) h = MeasureSpec(AT_MOST, measuredWidth_); - views_[i]->Measure(dc, h - (float)margins.horiz(), MeasureSpec(UNSPECIFIED, measuredHeight_)); + if (h.type == UNSPECIFIED && measuredWidth_ != 0.0f) + h = MeasureSpec(AT_MOST, measuredWidth_); + view->Measure(dc, h - (float)margins.horiz(), MeasureSpec(UNSPECIFIED, measuredHeight_)); } float amount; if (orientation_ == ORIENT_HORIZONTAL) { - amount = views_[i]->GetMeasuredWidth() + margins.horiz(); - maxOther = std::max(maxOther, views_[i]->GetMeasuredHeight() + margins.vert()); + amount = view->GetMeasuredWidth() + margins.horiz(); + maxOther = std::max(maxOther, view->GetMeasuredHeight() + margins.vert()); } else { - amount = views_[i]->GetMeasuredHeight() + margins.vert(); - maxOther = std::max(maxOther, views_[i]->GetMeasuredWidth() + margins.horiz()); + amount = view->GetMeasuredHeight() + margins.vert(); + maxOther = std::max(maxOther, view->GetMeasuredWidth() + margins.horiz()); } sum += amount; @@ -457,7 +458,6 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v // and distribute among the weighted ones. if (orientation_ == ORIENT_HORIZONTAL) { MeasureBySpec(layoutParams_->width, weightZeroSum, horiz, &measuredWidth_); - MeasureBySpec(layoutParams_->height, maxOther, vert, &measuredHeight_); // If we've got stretch, allow growing to fill the parent. float allowedWidth = measuredWidth_; @@ -469,33 +469,36 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v 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) + for (View *view : views_) { + if (view->GetVisibility() == V_GONE) continue; - const LinearLayoutParams *linLayoutParams = views_[i]->GetLayoutParams()->As(); + const LinearLayoutParams *linLayoutParams = view->GetLayoutParams()->As(); if (linLayoutParams && linLayoutParams->weight > 0.0f) { Margins margins = defaultMargins_; if (linLayoutParams->HasMargins()) margins = linLayoutParams->margins; MeasureSpec v = vert; - if (v.type == UNSPECIFIED && measuredHeight_ != 0.0) + if (v.type == UNSPECIFIED && measuredHeight_ != 0.0f) v = MeasureSpec(AT_MOST, measuredHeight_); MeasureSpec h(AT_MOST, unit * linLayoutParams->weight - margins.horiz()); if (horiz.type == EXACTLY) { h.type = EXACTLY; } - views_[i]->Measure(dc, h, v - (float)margins.vert()); - usedWidth += views_[i]->GetMeasuredWidth(); + view->Measure(dc, h, v - (float)margins.vert()); + usedWidth += view->GetMeasuredWidth(); + maxOther = std::max(maxOther, view->GetMeasuredHeight() + margins.vert()); } } if (horiz.type == AT_MOST && measuredWidth_ < horiz.size) { measuredWidth_ += usedWidth; } + + // Measure here in case maxOther moved (can happen due to word wrap.) + MeasureBySpec(layoutParams_->height, maxOther, vert, &measuredHeight_); } else { MeasureBySpec(layoutParams_->height, weightZeroSum, vert, &measuredHeight_); - MeasureBySpec(layoutParams_->width, maxOther, horiz, &measuredWidth_); // If we've got stretch, allow growing to fill the parent. float allowedHeight = measuredHeight_; @@ -507,30 +510,34 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v 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) + for (View *view : views_) { + if (view->GetVisibility() == V_GONE) continue; - const LinearLayoutParams *linLayoutParams = views_[i]->GetLayoutParams()->As(); + const LinearLayoutParams *linLayoutParams = view->GetLayoutParams()->As(); if (linLayoutParams && linLayoutParams->weight > 0.0f) { Margins margins = defaultMargins_; if (linLayoutParams->HasMargins()) margins = linLayoutParams->margins; MeasureSpec h = horiz; - if (h.type == UNSPECIFIED && measuredWidth_ != 0.0) + if (h.type == UNSPECIFIED && measuredWidth_ != 0.0f) h = MeasureSpec(AT_MOST, measuredWidth_); MeasureSpec v(AT_MOST, unit * linLayoutParams->weight - margins.vert()); if (vert.type == EXACTLY) { v.type = EXACTLY; } - views_[i]->Measure(dc, h - (float)margins.horiz(), v); - usedHeight += views_[i]->GetMeasuredHeight(); + view->Measure(dc, h - (float)margins.horiz(), v); + usedHeight += view->GetMeasuredHeight(); + maxOther = std::max(maxOther, view->GetMeasuredWidth() + margins.horiz()); } } if (vert.type == AT_MOST && measuredHeight_ < vert.size) { measuredHeight_ += usedHeight; } + + // Measure here in case maxOther moved (can happen due to word wrap.) + MeasureBySpec(layoutParams_->width, maxOther, horiz, &measuredWidth_); } } @@ -627,15 +634,15 @@ void ScrollView::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec ver } // The scroll view itself simply obeys its parent - but also tries to fit the child if possible. - MeasureBySpec(layoutParams_->width, 0.0f, horiz, &measuredWidth_); - MeasureBySpec(layoutParams_->height, 0.0f, vert, &measuredHeight_); + MeasureBySpec(layoutParams_->width, horiz.size, horiz, &measuredWidth_); + MeasureBySpec(layoutParams_->height, vert.size, vert, &measuredHeight_); if (views_.size()) { if (orientation_ == ORIENT_HORIZONTAL) { - views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED), MeasureSpec(UNSPECIFIED)); + views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED, measuredWidth_), MeasureSpec(AT_MOST, measuredHeight_ - margins.vert())); MeasureBySpec(layoutParams_->height, views_[0]->GetMeasuredHeight(), vert, &measuredHeight_); } else { - views_[0]->Measure(dc, MeasureSpec(AT_MOST, measuredWidth_ - margins.horiz()), MeasureSpec(UNSPECIFIED)); + views_[0]->Measure(dc, MeasureSpec(AT_MOST, measuredWidth_ - margins.horiz()), MeasureSpec(UNSPECIFIED, measuredHeight_)); MeasureBySpec(layoutParams_->width, views_[0]->GetMeasuredWidth(), horiz, &measuredWidth_); } if (orientation_ == ORIENT_VERTICAL && vert.type != EXACTLY) { @@ -966,8 +973,8 @@ void AnchorLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v Size width = WRAP_CONTENT; Size height = WRAP_CONTENT; - MeasureSpec specW(UNSPECIFIED, 0.0f); - MeasureSpec specH(UNSPECIFIED, 0.0f); + MeasureSpec specW(UNSPECIFIED, measuredWidth_); + MeasureSpec specH(UNSPECIFIED, measuredHeight_); if (!overflow_) { if (horiz.type != UNSPECIFIED) { @@ -1106,9 +1113,9 @@ TabHolder::TabHolder(Orientation orientation, float stripSize, LayoutParams *lay currentTab_(0) { SetSpacing(0.0f); if (orientation == ORIENT_HORIZONTAL) { - tabStrip_ = new ChoiceStrip(orientation, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); + tabStrip_ = new ChoiceStrip(orientation, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); tabStrip_->SetTopTabs(true); - tabScroll_ = new ScrollView(orientation, new LayoutParams(FILL_PARENT, FILL_PARENT)); + tabScroll_ = new ScrollView(orientation, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); tabScroll_->Add(tabStrip_); Add(tabScroll_); } else { @@ -1162,7 +1169,7 @@ ChoiceStrip::ChoiceStrip(Orientation orientation, LayoutParams *layoutParams) void ChoiceStrip::AddChoice(const std::string &title) { StickyChoice *c = new StickyChoice(title, "", orientation_ == ORIENT_HORIZONTAL ? - 0 : + nullptr : new LinearLayoutParams(FILL_PARENT, ITEM_HEIGHT)); c->OnClick.Handle(this, &ChoiceStrip::OnChoiceClick); Add(c); @@ -1173,7 +1180,7 @@ void ChoiceStrip::AddChoice(const std::string &title) { void ChoiceStrip::AddChoice(ImageID buttonImage) { StickyChoice *c = new StickyChoice(buttonImage, orientation_ == ORIENT_HORIZONTAL ? - 0 : + nullptr : new LinearLayoutParams(FILL_PARENT, ITEM_HEIGHT)); c->OnClick.Handle(this, &ChoiceStrip::OnChoiceClick); Add(c);