From d00a2bbc040e250e6d4f58d6bfb1f924753e7241 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Aug 2016 11:56:27 -0700 Subject: [PATCH 01/10] UI: Specify max height for horizontal scroll. --- ext/native/ui/viewgroup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/native/ui/viewgroup.cpp b/ext/native/ui/viewgroup.cpp index 93ec72a6e4..94f093d297 100644 --- a/ext/native/ui/viewgroup.cpp +++ b/ext/native/ui/viewgroup.cpp @@ -632,7 +632,7 @@ void ScrollView::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec ver if (views_.size()) { if (orientation_ == ORIENT_HORIZONTAL) { - views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED), MeasureSpec(UNSPECIFIED)); + views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED), 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)); From f7231686bd8538f1fa7fa271f2cde32b0b5de852 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Aug 2016 13:34:47 -0700 Subject: [PATCH 02/10] UI: Make it easy to measure with spec info. This still allows skipping a Measure() override, so that it can still clamp properly. --- UI/MainScreen.cpp | 9 +++--- ext/native/ui/view.cpp | 44 +++++++++++++++++++++----- ext/native/ui/view.h | 3 +- ext/native/ui/viewgroup.cpp | 61 +++++++++++++++++++------------------ 4 files changed, 73 insertions(+), 44 deletions(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index ec68e3f410..30001e539c 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -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, 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/ext/native/ui/view.cpp b/ext/native/ui/view.cpp index 88bdab3619..6f09ad0b22 100644 --- a/ext/native/ui/view.cpp +++ b/ext/native/ui/view.cpp @@ -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); @@ -657,17 +680,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); + if (bounds.w < 0) { + // If there's no size, let's grow as big as we want. + bounds.w = horiz.size == 0 ? 1000.f : horiz.size; + } + if (bounds.h < 0) { + bounds.h = vert.size == 0 ? 1000.f : vert.size; + } + ApplyBoundsBySpec(bounds, horiz, vert); dc.MeasureTextRect(small_ ? dc.theme->uiFontSmall : dc.theme->uiFont, 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. diff --git a/ext/native/ui/view.h b/ext/native/ui/view.h index 370b5b0483..69a7a908c6 100644 --- a/ext/native/ui/view.h +++ b/ext/native/ui/view.h @@ -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; } @@ -742,7 +743,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 94f093d297..6e4e0e73e6 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_; @@ -424,20 +424,21 @@ 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(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) + 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; @@ -469,10 +470,10 @@ 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_; @@ -485,8 +486,8 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v 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(); } } @@ -507,10 +508,10 @@ 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_; @@ -523,8 +524,8 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v 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(); } } @@ -627,15 +628,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(AT_MOST, measuredHeight_ - margins.vert())); + 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 +967,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 +1107,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 +1163,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 +1174,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); From 64403e520f5815a77df524f24c632b43cc5bb664 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Aug 2016 13:37:20 -0700 Subject: [PATCH 03/10] UI: Wrap browsing path if long. --- UI/MainScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 30001e539c..628213770e 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -484,7 +484,7 @@ void GameBrowser::Refresh() { LinearLayout *topBar = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); if (allowBrowsing_) { topBar->Add(new Spacer(2.0f)); - topBar->Add(new TextView(path_.GetFriendlyPath().c_str(), ALIGN_VCENTER, true, new LinearLayoutParams(FILL_PARENT, 64.0f, 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..."), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick); #else From 838de5b5c004945b424390f6d134cf33d4c37b78 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Aug 2016 13:58:56 -0700 Subject: [PATCH 04/10] UI: Asynchronously load the game info screen. No reason to wait, just need to keep the buttons hidden. Intentionally hiding even the buttons below the possibly added buttons, so we don't accidentally play "button keep away" with the user. --- UI/GameScreen.cpp | 84 ++++++++++++++++++++++++++++++++++------------- UI/GameScreen.h | 8 +++++ 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 2aa40c8dc8..4783c48bdc 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"); @@ -79,6 +78,13 @@ void GameScreen::CreateViews() { tvInstallDataSize_->SetShadow(true); tvRegion_ = leftColumn->Add(new TextView("", ALIGN_LEFT, true, new AnchorLayoutParams(10, 380, NONE, NONE))); 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 +93,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); @@ -200,6 +221,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_; }; From 49f96917c626c0f0f36d01ba3ebfeab20df4571a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Aug 2016 14:31:54 -0700 Subject: [PATCH 05/10] UI: Allow title, etc. to wrap on game info screen. --- UI/GameScreen.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 4783c48bdc..bd4e9b444c 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -66,17 +66,23 @@ 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; @@ -208,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); } } From ab3ed7d2efa9b888da4338cf796e31eb98151b6b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Aug 2016 15:22:27 -0700 Subject: [PATCH 06/10] UI: Account for flexible things changing on wrap. If something has a flexible width, that may affect its final calculated height. This allows it to grow in such a situation. --- ext/native/ui/viewgroup.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ext/native/ui/viewgroup.cpp b/ext/native/ui/viewgroup.cpp index 6e4e0e73e6..bf3a5e3645 100644 --- a/ext/native/ui/viewgroup.cpp +++ b/ext/native/ui/viewgroup.cpp @@ -422,12 +422,12 @@ 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_); view->Measure(dc, MeasureSpec(UNSPECIFIED, measuredWidth_), v - (float)margins.vert()); } else if (orientation_ == ORIENT_VERTICAL) { MeasureSpec h = horiz; - if (h.type == UNSPECIFIED && measuredWidth_ != 0) + if (h.type == UNSPECIFIED && measuredWidth_ != 0.0f) h = MeasureSpec(AT_MOST, measuredWidth_); view->Measure(dc, h - (float)margins.horiz(), MeasureSpec(UNSPECIFIED, measuredHeight_)); } @@ -458,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_; @@ -480,7 +479,7 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v 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) { @@ -488,15 +487,18 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v } 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_; @@ -518,7 +520,7 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v 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) { @@ -526,12 +528,16 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v } 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_); } } From 2076d66663adc0eec9ec1dd6c88241dcb9a79228 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Aug 2016 15:22:38 -0700 Subject: [PATCH 07/10] UI: Wrap descriptions in the savedata manager. Sometimes the savedata info is long, better to wrap than overflow the box. --- UI/SavedataScreen.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 290ae9ac63..d7bdd87c7b 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: From d6820f3cc36c16650f18d2ec02f0d9d5f10cd3ec Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Aug 2016 16:49:50 -0700 Subject: [PATCH 08/10] UI: Allow measuring text with a scale. --- UI/MainScreen.cpp | 4 ++-- UI/OnScreenDisplay.cpp | 4 ++-- UI/SavedataScreen.cpp | 2 +- ext/native/ui/ui_context.cpp | 18 +++++++++--------- ext/native/ui/ui_context.h | 6 +++--- ext/native/ui/ui_screen.cpp | 10 +++++----- ext/native/ui/view.cpp | 20 ++++++++++---------- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 628213770e..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; 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 d7bdd87c7b..75ee3974b5 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -251,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..def2ac7017 100644 --- a/ext/native/ui/ui_context.cpp +++ b/ext/native/ui/ui_context.cpp @@ -127,36 +127,36 @@ 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 { +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 ((align & FLAG_WRAP_TEXT) == 0) { - MeasureTextCount(style, str, count, x, y, align); + MeasureTextCount(style, scaleX, scaleY, str, count, x, y, align); return; } 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..a2bf128087 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -369,7 +369,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); @@ -435,7 +435,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); @@ -477,7 +477,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); @@ -667,7 +667,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 +726,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 6f09ad0b22..2191358f83 100644 --- a/ext/native/ui/view.cpp +++ b/ext/native/ui/view.cpp @@ -443,7 +443,7 @@ void Choice::GetContentDimensions(const UIContext &dc, float &w, float &h) const 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); } w += 24; h += 16; @@ -485,7 +485,7 @@ void Choice::Draw(UIContext &dc) { float scale = 1.0f; float actualWidth, actualHeight; - dc.MeasureText(dc.theme->uiFont, text_.c_str(), &actualWidth, &actualHeight, ALIGN_VCENTER); + dc.MeasureText(dc.theme->uiFont, scale, scale, text_.c_str(), &actualWidth, &actualHeight, ALIGN_VCENTER); if (actualWidth > availWidth) { scale = std::max(MIN_TEXT_SCALE, availWidth / actualWidth); } @@ -541,7 +541,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; @@ -591,7 +591,7 @@ void CheckBox::Draw(UIContext &dc) { float scale = 1.0f; float actualWidth, actualHeight; - dc.MeasureText(dc.theme->uiFont, text_.c_str(), &actualWidth, &actualHeight, ALIGN_VCENTER); + dc.MeasureText(dc.theme->uiFont, scale, scale, text_.c_str(), &actualWidth, &actualHeight, ALIGN_VCENTER); if (actualWidth > availWidth) { scale = std::max(MIN_TEXT_SCALE, availWidth / actualWidth); } @@ -608,7 +608,7 @@ void Button::GetContentDimensions(const UIContext &dc, float &w, float &h) const 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; @@ -625,7 +625,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_); } @@ -690,7 +690,7 @@ void TextView::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz bounds.h = vert.size == 0 ? 1000.f : vert.size; } ApplyBoundsBySpec(bounds, horiz, vert); - dc.MeasureTextRect(small_ ? dc.theme->uiFontSmall : dc.theme->uiFont, text_.c_str(), (int)text_.length(), bounds, &w, &h, textAlign_); + 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) { @@ -749,7 +749,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; @@ -763,7 +763,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; } @@ -932,7 +932,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) { From 604d6113c17eb66bd2c6b39b1b5fb09c10baf30f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Aug 2016 17:35:41 -0700 Subject: [PATCH 09/10] UI: Allow choices and checkboxes to wrap. If the text is too long, they will scroll down first, then wrap. --- ext/native/ui/ui_context.cpp | 5 -- ext/native/ui/view.cpp | 88 ++++++++++++++++++++++++++---------- ext/native/ui/view.h | 14 ++++-- 3 files changed, 73 insertions(+), 34 deletions(-) diff --git a/ext/native/ui/ui_context.cpp b/ext/native/ui/ui_context.cpp index def2ac7017..bb48e5ab04 100644 --- a/ext/native/ui/ui_context.cpp +++ b/ext/native/ui/ui_context.cpp @@ -145,11 +145,6 @@ void UIContext::MeasureTextCount(const UI::FontStyle &style, float scaleX, float } 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 ((align & FLAG_WRAP_TEXT) == 0) { - MeasureTextCount(style, scaleX, scaleY, str, count, x, y, align); - return; - } - if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) { float sizeFactor = (float)style.sizePts / 24.0f; Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor); diff --git a/ext/native/ui/view.cpp b/ext/native/ui/view.cpp index 2191358f83..c36383b4d3 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; @@ -413,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; } } @@ -437,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, 1.0f, 1.0f, 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){ @@ -482,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, scale, scale, 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); } @@ -588,20 +601,47 @@ 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, scale, scale, 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_]; @@ -684,10 +724,10 @@ void TextView::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz Bounds bounds(0, 0, layoutParams_->width, layoutParams_->height); if (bounds.w < 0) { // If there's no size, let's grow as big as we want. - bounds.w = horiz.size == 0 ? 1000.f : horiz.size; + bounds.w = horiz.size == 0 ? MAX_ITEM_SIZE : horiz.size; } if (bounds.h < 0) { - bounds.h = vert.size == 0 ? 1000.f : vert.size; + 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_); diff --git a/ext/native/ui/view.h b/ext/native/ui/view.h index 69a7a908c6..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; } @@ -609,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; @@ -621,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_; @@ -709,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_; From 192d6ca60a21ad28e0c33b0e0d747e076edb2744 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Aug 2016 17:44:02 -0700 Subject: [PATCH 10/10] UI: Allow explicit newlines in popup choice titles. --- ext/native/ui/ui_screen.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index a2bf128087..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); @@ -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; @@ -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; @@ -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;