From 202a9db885a995520b43c94d15bb677a4ff3d93e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Aug 2021 13:57:22 -0700 Subject: [PATCH] UI: Add a screen to visualize PSP controls. Currently, doesn't do anything. But looks about right. --- Common/Render/DrawBuffer.cpp | 50 ++++++++ Common/Render/DrawBuffer.h | 1 + Common/UI/ViewGroup.cpp | 3 +- UI/ControlMappingScreen.cpp | 218 +++++++++++++++++++++++++++++++++++ UI/ControlMappingScreen.h | 9 ++ 5 files changed, 280 insertions(+), 1 deletion(-) diff --git a/Common/Render/DrawBuffer.cpp b/Common/Render/DrawBuffer.cpp index b29513d725..b932c62493 100644 --- a/Common/Render/DrawBuffer.cpp +++ b/Common/Render/DrawBuffer.cpp @@ -311,6 +311,56 @@ void DrawBuffer::DrawImageRotated(ImageID atlas_image, float x, float y, float s } } +void DrawBuffer::DrawImageRotatedStretch(ImageID atlas_image, const Bounds &bounds, float scales[2], float angle, Color color, bool mirror_h) { + const AtlasImage *image = atlas->getImage(atlas_image); + if (!image) + return; + + if (scales[0] == 0.0f || scales[1] == 0.0f) { + float rotatedSize[2]{ (float)image->w, (float)image->h }; + rot(rotatedSize, angle, 0.0f, 0.0f); + + // With that, we calculate the scale to stretch to, and rotate it back. + scales[0] = bounds.w / rotatedSize[0]; + scales[1] = bounds.h / rotatedSize[1]; + rot(scales, -angle, 0.0f, 0.0f); + } + + float w = (float)image->w * scales[0]; + float h = (float)image->h * scales[1]; + float x1 = bounds.centerX() - w / 2; + float x2 = bounds.centerX() + w / 2; + float y1 = bounds.centerY() - h / 2; + float y2 = bounds.centerY() + h / 2; + float v[6][2] = { + {x1, y1}, + {x2, y1}, + {x2, y2}, + {x1, y1}, + {x2, y2}, + {x1, y2}, + }; + float u1 = image->u1; + float u2 = image->u2; + if (mirror_h) { + float temp = u1; + u1 = u2; + u2 = temp; + } + const float uv[6][2] = { + {u1, image->v1}, + {u2, image->v1}, + {u2, image->v2}, + {u1, image->v1}, + {u2, image->v2}, + {u1, image->v2}, + }; + for (int i = 0; i < 6; i++) { + rot(v[i], angle, bounds.centerX(), bounds.centerY()); + V(v[i][0], v[i][1], 0, color, uv[i][0], uv[i][1]); + } +} + // TODO: add arc support void DrawBuffer::Circle(float xc, float yc, float radius, float thickness, int segments, float startAngle, uint32_t color, float u_mul) { float angleDelta = PI * 2 / segments; diff --git a/Common/Render/DrawBuffer.h b/Common/Render/DrawBuffer.h index f88308cf97..07aa344ca7 100644 --- a/Common/Render/DrawBuffer.h +++ b/Common/Render/DrawBuffer.h @@ -116,6 +116,7 @@ public: DrawImageStretch(atlas_image, bounds.x, bounds.y, bounds.x2(), bounds.y2(), color); } void DrawImageRotated(ImageID atlas_image, float x, float y, float scale, float angle, Color color = COLOR(0xFFFFFF), bool mirror_h = false); // Always centers + void DrawImageRotatedStretch(ImageID atlas_image, const Bounds &bounds, float scales[2], float angle, Color color = COLOR(0xFFFFFF), bool mirror_h = false); void DrawTexRect(float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, Color color); void DrawTexRect(const Bounds &bounds, float u1, float v1, float u2, float v2, Color color) { DrawTexRect(bounds.x, bounds.y, bounds.x2(), bounds.y2(), u1, v1, u2, v2, color); diff --git a/Common/UI/ViewGroup.cpp b/Common/UI/ViewGroup.cpp index e2802aeb2f..2b1ba991bd 100644 --- a/Common/UI/ViewGroup.cpp +++ b/Common/UI/ViewGroup.cpp @@ -1197,7 +1197,8 @@ void AnchorLayout::Layout() { if (vBounds.w > bounds_.w) vBounds.w = bounds_.w; if (vBounds.h > bounds_.h) vBounds.h = bounds_.h; - float left = 0, top = 0, right = 0, bottom = 0, center = false; + float left = 0, top = 0, right = 0, bottom = 0; + bool center = false; if (params) { left = params->left; top = params->top; diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 574e795ddd..1fba4709ab 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "Common/Render/TextureAtlas.h" #include "Common/UI/Root.h" @@ -242,6 +243,8 @@ void ControlMappingScreen::CreateViews() { leftColumn->Add(new Choice(km->T("Autoconfigure")))->OnClick.Handle(this, &ControlMappingScreen::OnAutoConfigure); } + leftColumn->Add(new Choice(km->T("Show PSP")))->OnClick.Handle(this, &ControlMappingScreen::OnVisualizeMapping); + leftColumn->Add(new Spacer(new LinearLayoutParams(1.0f))); AddStandardBack(leftColumn); @@ -289,6 +292,12 @@ UI::EventReturn ControlMappingScreen::OnAutoConfigure(UI::EventParams ¶ms) { return UI::EVENT_DONE; } +UI::EventReturn ControlMappingScreen::OnVisualizeMapping(UI::EventParams ¶ms) { + VisualMappingScreen *visualMapping = new VisualMappingScreen(); + screenManager()->push(visualMapping); + return UI::EVENT_DONE; +} + void ControlMappingScreen::dialogFinished(const Screen *dialog, DialogResult result) { if (result == DR_OK && dialog->tag() == "listpopup") { ListPopupScreen *popup = (ListPopupScreen *)dialog; @@ -840,3 +849,212 @@ UI::EventReturn TouchTestScreen::OnRecreateActivity(UI::EventParams &e) { RecreateActivity(); return UI::EVENT_DONE; } + +class Backplate : public UI::InertView { +public: + Backplate(float scale, UI::LayoutParams *layoutParams = nullptr) : InertView(layoutParams), scale_(scale) { + } + + void Draw(UIContext &dc) override { + using namespace UI; + + const AtlasImage *whiteImage = dc.Draw()->GetAtlas()->getImage(dc.theme->whiteImage); + float centerU = (whiteImage->u1 + whiteImage->u2) * 0.5f; + float centerV = (whiteImage->v1 + whiteImage->v2) * 0.5f; + const uint32_t color = 0xB01C1818; + + auto V = [&](float x, float y) { + dc.Draw()->V(bounds_.x + x * scale_, bounds_.y + y * scale_, color, centerU, centerV); + }; + auto R = [&](float x1, float y1, float x2, float y2) { + V(x1, y1); V(x2, y1); V(x2, y2); + V(x1, y1); V(x2, y2); V(x1, y2); + }; + + // Curved left side. + V(12.0f, 44.0f); V(30.0f, 16.0f); V(30.0f, 44.0f); + V(0.0f, 80.0f); V(12.0f, 44.0f); V(12.0f, 80.0f); + R(12.0f, 44.0f, 30.0f, 80.0f); + R(0.0f, 80.0f, 30.0f, 114.0f); + V(0.0f, 114.0f); V(12.0f, 114.0f); V(12.0f, 154.0f); + R(12.0f, 114.0f, 30.0f, 154.0f); + V(12.0f, 154.0f); V(30.0f, 154.0f); V(30.0f, 180.0f); + // Left side. + V(30.0f, 16.0f); V(64.0f, 13.0f); V(64.0f, 184.0f); + V(30.0f, 16.0f); V(64.0f, 184.0f); V(30.0f, 180.0f); + V(64.0f, 13.0f); V(76.0f, 0.0f); V(76.0f, 13.0f); + V(64.0f, 184.0f); V(76.0f, 200.0f); V(76.0f, 184.0f); + R(64.0f, 13.0f, 76.0f, 184.0f); + // Center. + V(76.0f, 0.0f); V(400.0f, 0.0f); V(400.0f, 200.0f); + V(76.0f, 0.0f); V(400.0f, 200.0f); V(76.0f, 200.0f); + // Right side. + V(400.0f, 0.0f); V(412.0f, 13.0f); V(400.0f, 13.0f); + V(400.0f, 184.0f); V(412.0f, 184.0f); V(400.0f, 200.0f); + R(400.0f, 13.0f, 412.0f, 184.0f); + V(412.0f, 13.0f); V(446.0f, 16.0f); V(446.0f, 180.0f); + V(412.0f, 13.0f); V(446.0f, 180.0f); V(412.0f, 184.0f); + // Curved right side. + V(446.0f, 16.0f); V(462.0f, 44.0f); V(446.0f, 44.0f); + V(462.0f, 44.0f); V(474.0f, 80.0f); V(462.0f, 80.0f); + R(446.0f, 44.0f, 462.0f, 80.0f); + R(446.0f, 80.0f, 474.0f, 114.0f); + V(462.0f, 114.0f); V(474.0f, 114.0f); V(462.0f, 154.0f); + R(446.0f, 114.0f, 462.0f, 154.0f); + V(446.0f, 154.0f); V(462.0f, 154.0f); V(446.0f, 180.0f); + } + + void GetContentDimensions(const UIContext &dc, float &w, float &h) const override { + w = 474.0f * scale_; + h = 200.0f * scale_; + } + +protected: + float scale_ = 1.0f; +}; + +class MockScreen : public UI::InertView { +public: + MockScreen(UI::LayoutParams *layoutParams = nullptr) : InertView(layoutParams) { + } + + void Draw(UIContext &dc) override { + ImageID bg = ImageID("I_BG"); + dc.Draw()->DrawImageStretch(bg, bounds_, 0xFFFFFFFF); + + if (System_GetPropertyBool(SYSPROP_APP_GOLD)) { + dc.Draw()->DrawImage(ImageID("I_ICONGOLD"), bounds_.centerX() - 120, bounds_.centerY() - 30, 1.2f, 0xFFFFFFFF, ALIGN_CENTER); + } else { + dc.Draw()->DrawImage(ImageID("I_ICON"), bounds_.centerX() - 120, bounds_.centerY() - 30, 1.2f, 0xFFFFFFFF, ALIGN_CENTER); + } + dc.Draw()->DrawImage(ImageID("I_LOGO"), bounds_.centerX() + 40, bounds_.centerY() - 30, 1.5f, 0xFFFFFFFF, ALIGN_CENTER); + } +}; + +class MockButton : public UI::View { +public: + MockButton(int button, ImageID img, ImageID bg, float angle, UI::LayoutParams *layoutParams = nullptr) + : View(layoutParams), button_(button), img_(img), bgImg_(bg), angle_(angle) { + } + + void Draw(UIContext &dc) override { + uint32_t c = 0xFFFFFFFF; + if (selected_) { + c = dc.theme->buttonDownStyle.background.color; + } else if (HasFocus()) { + c = dc.theme->buttonFocusedStyle.background.color; + } + + float scales[2]{}; + if (bgImg_.isValid()) + dc.Draw()->DrawImageRotatedStretch(bgImg_, bounds_, scales, angle_, c, flipHBG_); + if (img_.isValid()) { + scales[0] *= scale_; + scales[1] *= scale_; + dc.Draw()->DrawImageRotatedStretch(img_, bounds_.Offset(offsetX_, offsetY_), scales, angle_, c); + } + } + + MockButton *SetScale(float s) { + scale_ = s; + return this; + } + + MockButton *SetFlipHBG(float f) { + flipHBG_ = f; + return this; + } + + MockButton *SetOffset(float x, float y) { + offsetX_ = x; + offsetY_ = y; + return this; + } + + MockButton *SetSelected(bool s) { + selected_ = s; + return this; + } + +protected: + int button_; + ImageID img_; + ImageID bgImg_; + float angle_; + float scale_ = 1.0f; + float flipHBG_ = false; + float offsetX_ = 0.0f; + float offsetY_ = 0.0f; + bool selected_ = false; +}; + +class MockPSP : public UI::AnchorLayout { +public: + static constexpr float SCALE = 1.4f; + + MockPSP(UI::LayoutParams *layoutParams = nullptr) : AnchorLayout(layoutParams) { + Add(new Backplate(SCALE)); + Add(new MockScreen(LayoutAt(99.0f, 13.0f, 97.0f, 33.0f))); + + // Left side. + AddButton(0, ImageID("I_STICK_LINE"), ImageID("I_STICK_BG_LINE"), 0.0f, LayoutSize(34.0f, 34.0f, 35.0f, 133.0f)); + AddButton(CTRL_LEFT, ImageID("I_ARROW"), ImageID("I_DIR_LINE"), M_PI * 0.0f, LayoutSize(28.0f, 20.0f, 14.0f, 75.0f))->SetOffset(-4.0f * SCALE, 0.0f); + AddButton(CTRL_UP, ImageID("I_ARROW"), ImageID("I_DIR_LINE"), M_PI * 0.5f, LayoutSize(20.0f, 28.0f, 40.0f, 50.0f))->SetOffset(0.0f, -4.0f * SCALE); + AddButton(CTRL_RIGHT, ImageID("I_ARROW"), ImageID("I_DIR_LINE"), M_PI * 1.0f, LayoutSize(28.0f, 20.0f, 58.0f, 75.0f))->SetOffset(4.0f * SCALE, 0.0f); + AddButton(CTRL_DOWN, ImageID("I_ARROW"), ImageID("I_DIR_LINE"), M_PI * 1.5f, LayoutSize(20.0f, 28.0f, 40.0f, 92.0f))->SetOffset(0.0f, 4.0f * SCALE); + + // Top. + AddButton(CTRL_LTRIGGER, ImageID("I_L"), ImageID("I_SHOULDER_LINE"), 0.0f, LayoutSize(50.0f, 16.0f, 29.0f, 0.0f)); + AddButton(CTRL_RTRIGGER, ImageID("I_R"), ImageID("I_SHOULDER_LINE"), 0.0f, LayoutSize(50.0f, 16.0f, 397.0f, 0.0f))->SetFlipHBG(true); + + // Bottom. + AddButton(CTRL_HOME, ImageID("I_ICON"), ImageID("I_RECT_LINE"), 0.0f, LayoutSize(28.0f, 14.0f, 88.0f, 181.0f))->SetScale(0.4f); + AddButton(CTRL_SELECT, ImageID("I_SELECT"), ImageID("I_RECT_LINE"), 0.0f, LayoutSize(28.0f, 14.0f, 330.0f, 181.0f)); + AddButton(CTRL_START, ImageID("I_START"), ImageID("I_RECT_LINE"), 0.0f, LayoutSize(28.0f, 14.0f, 361.0f, 181.0f)); + + // Right side. + AddButton(CTRL_TRIANGLE, ImageID("I_TRIANGLE"), ImageID("I_ROUND_LINE"), 0.0f, LayoutSize(23.0f, 23.0f, 419.0f, 46.0f))->SetScale(0.7f)->SetOffset(0.0f, -1.0f * SCALE); + AddButton(CTRL_CIRCLE, ImageID("I_CIRCLE"), ImageID("I_ROUND_LINE"), 0.0f, LayoutSize(23.0f, 23.0f, 446.0f, 74.0f))->SetScale(0.7f); + AddButton(CTRL_CROSS, ImageID("I_CROSS"), ImageID("I_ROUND_LINE"), 0.0f, LayoutSize(23.0f, 23.0f, 419.0f, 102.0f))->SetScale(0.7f); + AddButton(CTRL_SQUARE, ImageID("I_SQUARE"), ImageID("I_ROUND_LINE"), 0.0f, LayoutSize(23.0f, 23.0f, 392.0f, 74.0f))->SetScale(0.7f); + } + +private: + UI::AnchorLayoutParams *LayoutAt(float l, float t, float r, float b) { + return new UI::AnchorLayoutParams(l * SCALE, t * SCALE, r * SCALE, b * SCALE); + } + UI::AnchorLayoutParams *LayoutSize(float w, float h, float l, float t) { + return new UI::AnchorLayoutParams(w * SCALE, h * SCALE, l * SCALE, t * SCALE, UI::NONE, UI::NONE); + } + + MockButton *AddButton(int button, ImageID img, ImageID bg, float angle, UI::LayoutParams *lp) { + buttons_[button] = Add(new MockButton(button, img, bg, angle, lp)); + return buttons_[button]; + } + + std::unordered_map buttons_; +}; + +void VisualMappingScreen::CreateViews() { + using namespace UI; + + auto km = GetI18NCategory("KeyMapping"); + + root_ = new LinearLayout(ORIENT_HORIZONTAL); + + constexpr float leftColumnWidth = 200.0f; + LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(leftColumnWidth, FILL_PARENT, Margins(10, 0, 0, 10))); + + leftColumn->Add(new Spacer(new LinearLayoutParams(1.0f))); + AddStandardBack(leftColumn); + + Bounds bounds = screenManager()->getUIContext()->GetLayoutBounds(); + // Account for left side. + bounds.w -= leftColumnWidth + 10.0f; + + AnchorLayout *rightColumn = new AnchorLayout(new LinearLayoutParams(bounds.w, FILL_PARENT, 1.0f)); + rightColumn->Add(new MockPSP(new AnchorLayoutParams(bounds.centerX(), bounds.centerY(), NONE, NONE, true))); + + root_->Add(leftColumn); + root_->Add(rightColumn); +} diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index 025d4bc40c..aa9a51c8b8 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -44,6 +44,7 @@ private: UI::EventReturn OnDefaultMapping(UI::EventParams ¶ms); UI::EventReturn OnClearMapping(UI::EventParams ¶ms); UI::EventReturn OnAutoConfigure(UI::EventParams ¶ms); + UI::EventReturn OnVisualizeMapping(UI::EventParams ¶ms); virtual void dialogFinished(const Screen *dialog, DialogResult result) override; @@ -158,3 +159,11 @@ protected: UI::EventReturn OnRenderingBackend(UI::EventParams &e); UI::EventReturn OnRecreateActivity(UI::EventParams &e); }; + +class VisualMappingScreen : public UIDialogScreenWithBackground { +public: + VisualMappingScreen() {} + +protected: + void CreateViews() override; +};