From bab66e8542c9f6ccfe7d19dda8e44fd7b4d7bb83 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Dec 2017 14:05:49 -0800 Subject: [PATCH 1/6] Windows: Fix UI text measurements with &. These APIs don't take into account &&, which caused Homebrew && Demos to be too wide. --- ext/native/gfx_es2/draw_text_win.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/native/gfx_es2/draw_text_win.cpp b/ext/native/gfx_es2/draw_text_win.cpp index 491e906625..0476bd564e 100644 --- a/ext/native/gfx_es2/draw_text_win.cpp +++ b/ext/native/gfx_es2/draw_text_win.cpp @@ -132,7 +132,7 @@ void TextDrawerWin32::MeasureString(const char *str, size_t len, float *w, float } SIZE size; - std::wstring wstr = ConvertUTF8ToWString(ReplaceAll(std::string(str, len), "\n", "\r\n")); + std::wstring wstr = ConvertUTF8ToWString(ReplaceAll(ReplaceAll(std::string(str, len), "\n", "\r\n"), "&&", "&")); GetTextExtentPoint32(ctx_->hDC, wstr.c_str(), (int)wstr.size(), &size); entry = new TextMeasureEntry(); @@ -171,7 +171,7 @@ void TextDrawerWin32::MeasureStringRect(const char *str, size_t len, const Bound entry = iter->second.get(); } else { SIZE size; - std::wstring wstr = ConvertUTF8ToWString(lines[i].length() == 0 ? " " : lines[i]); + std::wstring wstr = ConvertUTF8ToWString(lines[i].length() == 0 ? " " : ReplaceAll(lines[i], "&&", "&")); GetTextExtentPoint32(ctx_->hDC, wstr.c_str(), (int)wstr.size(), &size); entry = new TextMeasureEntry(); From 6783394ae1011cdffa597d857a7b5c5d537e2514 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Dec 2017 14:07:40 -0800 Subject: [PATCH 2/6] UI: Add tween for translating anchor offsets. --- ext/native/math/geom2d.h | 4 ++++ ext/native/ui/ui_tween.cpp | 17 +++++++++++++++++ ext/native/ui/ui_tween.h | 10 ++++++++++ 3 files changed, 31 insertions(+) diff --git a/ext/native/math/geom2d.h b/ext/native/math/geom2d.h index e937acafbc..185aaa5275 100644 --- a/ext/native/math/geom2d.h +++ b/ext/native/math/geom2d.h @@ -14,6 +14,10 @@ struct Point { return sqrtf(dx*dx + dy*dy); } + bool operator ==(const Point &other) const { + return x == other.x && y == other.y; + } + /* FocusDirection directionTo(const Point &other) const { int angle = atan2f(other.y - y, other.x - x) / (2 * M_PI) - 0.125; diff --git a/ext/native/ui/ui_tween.cpp b/ext/native/ui/ui_tween.cpp index f786f0e711..4c3194d309 100644 --- a/ext/native/ui/ui_tween.cpp +++ b/ext/native/ui/ui_tween.cpp @@ -1,6 +1,7 @@ #include "base/colorutil.h" #include "ui/ui_tween.h" #include "ui/view.h" +#include "ui/viewgroup.h" namespace UI { @@ -66,6 +67,7 @@ void TweenBase::PersistData(PersistStatus status, std::string anonId, Per template void TweenBase::PersistData(PersistStatus status, std::string anonId, PersistMap &storage); template void TweenBase::PersistData(PersistStatus status, std::string anonId, PersistMap &storage); +template void TweenBase::PersistData(PersistStatus status, std::string anonId, PersistMap &storage); uint32_t ColorTween::Current(float pos) { return colorBlend(to_, from_, pos); @@ -96,4 +98,19 @@ Visibility VisibilityTween::Current(float p) { return p >= 1.0f ? to_ : from_; } +void AnchorTranslateTween::DoApply(View *view, float pos) { + Point cur = Current(pos); + + auto prev = view->GetLayoutParams()->As(); + auto lp = new AnchorLayoutParams(prev ? *prev : AnchorLayoutParams(FILL_PARENT, FILL_PARENT)); + lp->left = cur.x; + lp->top = cur.y; + view->ReplaceLayoutParams(lp); +} + +Point AnchorTranslateTween::Current(float p) { + float inv = 1.0f - p; + return Point(from_.x * inv + to_.x * p, from_.y * inv + to_.y * p); +} + } // namespace diff --git a/ext/native/ui/ui_tween.h b/ext/native/ui/ui_tween.h index 86509e9b09..aad4fbfa5d 100644 --- a/ext/native/ui/ui_tween.h +++ b/ext/native/ui/ui_tween.h @@ -182,4 +182,14 @@ protected: Visibility Current(float pos) override; }; +class AnchorTranslateTween : public TweenBase { +public: + using TweenBase::TweenBase; + +protected: + void DoApply(View *view, float pos) override; + + Point Current(float pos) override; +}; + } // namespace From 753e59c0273fdbbcf095518e81fd1f013f082712 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Dec 2017 14:08:20 -0800 Subject: [PATCH 3/6] UI: Allow negative left/right for anchor layouts. --- ext/native/ui/viewgroup.cpp | 12 ++++++------ ext/native/ui/viewgroup.h | 5 ++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/ext/native/ui/viewgroup.cpp b/ext/native/ui/viewgroup.cpp index cc983ccd62..49436b5613 100644 --- a/ext/native/ui/viewgroup.cpp +++ b/ext/native/ui/viewgroup.cpp @@ -994,10 +994,10 @@ void AnchorLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v height = params->height; if (!params->center) { - if (params->left >= 0 && params->right >= 0) { + if (params->left > NONE && params->right > NONE) { width = measuredWidth_ - params->left - params->right; } - if (params->top >= 0 && params->bottom >= 0) { + if (params->top > NONE && params->bottom > NONE) { height = measuredHeight_ - params->top - params->bottom; } } @@ -1034,22 +1034,22 @@ void AnchorLayout::Layout() { center = params->center; } - if (left >= 0) { + if (left > NONE) { vBounds.x = bounds_.x + left; if (center) vBounds.x -= vBounds.w * 0.5f; - } else if (right >= 0) { + } else if (right > NONE) { vBounds.x = bounds_.x2() - right - vBounds.w; if (center) { vBounds.x += vBounds.w * 0.5f; } } - if (top >= 0) { + if (top > NONE) { vBounds.y = bounds_.y + top; if (center) vBounds.y -= vBounds.h * 0.5f; - } else if (bottom >= 0) { + } else if (bottom > NONE) { vBounds.y = bounds_.y2() - bottom - vBounds.h; if (center) vBounds.y += vBounds.h * 0.5f; diff --git a/ext/native/ui/viewgroup.h b/ext/native/ui/viewgroup.h index 65d89a5a1f..58c82d84c9 100644 --- a/ext/native/ui/viewgroup.h +++ b/ext/native/ui/viewgroup.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -95,9 +96,7 @@ public: void Layout() override; }; -enum { - NONE = -1, -}; +const float NONE = -FLT_MAX; class AnchorLayoutParams : public LayoutParams { public: From f3c01cf19c1837754e62ffbb041145717aaeae2a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Dec 2017 14:08:49 -0800 Subject: [PATCH 4/6] UI: Transition tab strips on select. Following their orientation setting. --- ext/native/ui/viewgroup.cpp | 68 +++++++++++++++++++++++++++++++------ ext/native/ui/viewgroup.h | 18 +++++----- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/ext/native/ui/viewgroup.cpp b/ext/native/ui/viewgroup.cpp index 49436b5613..d85434b368 100644 --- a/ext/native/ui/viewgroup.cpp +++ b/ext/native/ui/viewgroup.cpp @@ -6,7 +6,9 @@ #include "base/stringutil.h" #include "base/timeutil.h" #include "input/keycodes.h" +#include "math/curves.h" #include "ui/ui_context.h" +#include "ui/ui_tween.h" #include "ui/view.h" #include "ui/viewgroup.h" #include "gfx_es2/draw_buffer.h" @@ -143,6 +145,7 @@ void ViewGroup::Draw(UIContext &dc) { } void ViewGroup::Update() { + View::Update(); for (View *view : views_) { if (view->GetVisibility() != V_GONE) view->Update(); @@ -1110,10 +1113,7 @@ void GridLayout::Layout() { } TabHolder::TabHolder(Orientation orientation, float stripSize, LayoutParams *layoutParams) - : LinearLayout(Opposite(orientation), layoutParams), - tabStrip_(nullptr), tabScroll_(nullptr), - stripSize_(stripSize), - currentTab_(0) { + : LinearLayout(Opposite(orientation), layoutParams), stripSize_(stripSize) { SetSpacing(0.0f); if (orientation == ORIENT_HORIZONTAL) { tabStrip_ = new ChoiceStrip(orientation, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); @@ -1125,9 +1125,23 @@ TabHolder::TabHolder(Orientation orientation, float stripSize, LayoutParams *lay tabStrip_ = new ChoiceStrip(orientation, new LayoutParams(stripSize, WRAP_CONTENT)); tabStrip_->SetTopTabs(true); Add(tabStrip_); - tabScroll_ = nullptr; } tabStrip_->OnChoice.Handle(this, &TabHolder::OnTabClick); + + contents_ = new AnchorLayout(new LinearLayoutParams(1.0f)); + Add(contents_); +} + +void TabHolder::AddTabContents(const std::string &title, View *tabContents) { + tabContents->ReplaceLayoutParams(new AnchorLayoutParams(FILL_PARENT, FILL_PARENT)); + tabs_.push_back(tabContents); + tabStrip_->AddChoice(title); + contents_->Add(tabContents); + if (tabs_.size() > 1) + tabContents->SetVisibility(V_GONE); + + // Will be filled in later. + tabTweens_.push_back(nullptr); } void TabHolder::SetCurrentTab(int tab) { @@ -1135,10 +1149,44 @@ void TabHolder::SetCurrentTab(int tab) { // Ignore return; } + + auto setupTween = [&](View *view, AnchorTranslateTween *&tween) { + if (tween) + return; + + tween = new AnchorTranslateTween(0.15f, bezierEaseInOut); + tween->Finish.Add([&](EventParams &e) { + e.v->SetVisibility(tabs_[currentTab_] == e.v ? V_VISIBLE : V_GONE); + return EVENT_DONE; + }); + view->AddTween(tween)->Persist(); + }; + if (tab != currentTab_) { - tabs_[currentTab_]->SetVisibility(V_GONE); + Orientation orient = Opposite(orientation_); + // Direction from which the new tab will come. + float dir = tab < currentTab_ ? -1.0f : 1.0f; + + // First, setup any missing tweens. + setupTween(tabs_[currentTab_], tabTweens_[currentTab_]); + setupTween(tabs_[tab], tabTweens_[tab]); + + // Currently displayed, so let's reset it. + tabTweens_[currentTab_]->Reset(Point(0.0f, 0.0f)); + + if (orient == ORIENT_HORIZONTAL) { + tabTweens_[tab]->Reset(Point(bounds_.w * dir, 0.0f)); + tabTweens_[currentTab_]->Divert(Point(bounds_.w * -dir, 0.0f)); + } else { + tabTweens_[tab]->Reset(Point(0.0f, bounds_.h * dir)); + tabTweens_[currentTab_]->Divert(Point(0.0f, bounds_.h * -dir)); + } + // Actually move it to the initial position now, just to avoid any flicker. + tabTweens_[tab]->Apply(tabs_[tab]); + tabTweens_[tab]->Divert(Point(0.0f, 0.0f)); + tabs_[tab]->SetVisibility(V_VISIBLE); + currentTab_ = tab; - tabs_[currentTab_]->SetVisibility(V_VISIBLE); } tabStrip_->SetSelection(tab); } @@ -1146,10 +1194,8 @@ void TabHolder::SetCurrentTab(int tab) { EventReturn TabHolder::OnTabClick(EventParams &e) { // We have e.b set when it was an explicit click action. // In that case, we make the view gone and then visible - this scrolls scrollviews to the top. - if (currentTab_ != (int)e.a || e.b != 0) { - tabs_[currentTab_]->SetVisibility(V_GONE); - currentTab_ = e.a; - tabs_[currentTab_]->SetVisibility(V_VISIBLE); + if (e.b != 0) { + SetCurrentTab((int)e.a); } return EVENT_DONE; } diff --git a/ext/native/ui/viewgroup.h b/ext/native/ui/viewgroup.h index 58c82d84c9..e6d057c1a4 100644 --- a/ext/native/ui/viewgroup.h +++ b/ext/native/ui/viewgroup.h @@ -12,6 +12,8 @@ namespace UI { +class AnchorTranslateTween; + struct NeighborResult { NeighborResult() : view(0), score(0) {} NeighborResult(View *v, float s) : view(v), score(s) {} @@ -303,12 +305,7 @@ public: template T *AddTab(const std::string &title, T *tabContents) { - tabContents->ReplaceLayoutParams(new LinearLayoutParams(1.0f)); - tabs_.push_back(tabContents); - tabStrip_->AddChoice(title); - Add(tabContents); - if (tabs_.size() > 1) - tabContents->SetVisibility(V_GONE); + AddTabContents(title, (View *)tabContents); return tabContents; } @@ -320,14 +317,17 @@ public: void PersistData(PersistStatus status, std::string anonId, PersistMap &storage) override; private: + void AddTabContents(const std::string &title, View *tabContents); EventReturn OnTabClick(EventParams &e); - ChoiceStrip *tabStrip_; - ScrollView *tabScroll_; + ChoiceStrip *tabStrip_ = nullptr; + ScrollView *tabScroll_ = nullptr; + AnchorLayout *contents_ = nullptr; float stripSize_; - int currentTab_; + int currentTab_ = 0; std::vector tabs_; + std::vector tabTweens_; }; // Yes, this feels a bit Java-ish... From 21272a4c24ca7d91a51854140b2aadf11fe9cf38 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Dec 2017 14:20:32 -0800 Subject: [PATCH 5/6] UI: Clip scissor to screen. --- ext/native/ui/ui_context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/native/ui/ui_context.cpp b/ext/native/ui/ui_context.cpp index c0f4020af1..671765a552 100644 --- a/ext/native/ui/ui_context.cpp +++ b/ext/native/ui/ui_context.cpp @@ -70,6 +70,8 @@ void UIContext::PushScissor(const Bounds &bounds) { Bounds clipped = TransformBounds(bounds); if (scissorStack_.size()) clipped.Clip(scissorStack_.back()); + else + clipped.Clip(bounds_); scissorStack_.push_back(clipped); ActivateTopScissor(); } From ed2ac95bbbd30dab23ccece102fafe7a1d96ef4d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Dec 2017 14:30:21 -0800 Subject: [PATCH 6/6] UI: Always clip tab contents. So they don't overlap tabs or etc. on transition. --- 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 d85434b368..470b6c8f89 100644 --- a/ext/native/ui/viewgroup.cpp +++ b/ext/native/ui/viewgroup.cpp @@ -1129,7 +1129,7 @@ TabHolder::TabHolder(Orientation orientation, float stripSize, LayoutParams *lay tabStrip_->OnChoice.Handle(this, &TabHolder::OnTabClick); contents_ = new AnchorLayout(new LinearLayoutParams(1.0f)); - Add(contents_); + Add(contents_)->SetClip(true); } void TabHolder::AddTabContents(const std::string &title, View *tabContents) {