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/UIScreen.cpp b/Common/UI/UIScreen.cpp index 3a852dc2ee..33b49683a4 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -296,6 +296,10 @@ void PopupScreen::SetPopupOrigin(const UI::View *view) { popupOrigin_ = view->GetBounds().Center(); } +void PopupScreen::SetPopupOffset(float y) { + offsetY_ = y; +} + void PopupScreen::TriggerFinish(DialogResult result) { if (CanComplete(result)) { finishFrame_ = frames_; @@ -320,7 +324,7 @@ void PopupScreen::CreateViews() { float yres = screenManager()->getUIContext()->GetBounds().h; box_ = new LinearLayout(ORIENT_VERTICAL, - new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT, dc.GetBounds().centerX(), dc.GetBounds().centerY(), NONE, NONE, true)); + new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT, dc.GetBounds().centerX(), dc.GetBounds().centerY() + offsetY_, NONE, NONE, true)); root_->Add(box_); box_->SetBG(dc.theme->popupStyle.background); diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 3e1fa8fdc9..9541309523 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -81,6 +81,7 @@ public: virtual void TriggerFinish(DialogResult result) override; void SetPopupOrigin(const UI::View *view); + void SetPopupOffset(float y); protected: virtual bool FillVertical() const { return false; } @@ -109,6 +110,7 @@ private: DialogResult finishResult_; bool hasPopupOrigin_ = false; Point popupOrigin_; + float offsetY_ = 0.0f; }; class ListPopupScreen : public PopupScreen { 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/Core/KeyMap.cpp b/Core/KeyMap.cpp index 9adb9b0324..78efca5c41 100644 --- a/Core/KeyMap.cpp +++ b/Core/KeyMap.cpp @@ -43,7 +43,8 @@ namespace KeyMap { KeyDef AxisDef(int deviceId, int axisId, int direction); KeyMapping g_controllerMap; -int g_controllerMapGeneration = 0; // Just used to check if we need to update the Windows menu or not. +// Incremented on modification, so we know when to update menus. +int g_controllerMapGeneration = 0; std::set g_seenPads; std::set g_seenDeviceIds; @@ -622,6 +623,7 @@ void SetAxisMapping(int btn, int deviceId, int axisId, int direction, bool repla void RestoreDefault() { g_controllerMap.clear(); + g_controllerMapGeneration++; #if PPSSPP_PLATFORM(WINDOWS) SetDefaultKeyMap(DEFAULT_MAPPING_KEYBOARD, true); SetDefaultKeyMap(DEFAULT_MAPPING_XINPUT, false); @@ -765,4 +767,12 @@ void SwapAxis() { g_swapped_keys = !g_swapped_keys; } +bool HasChanged(int &prevGeneration) { + if (prevGeneration != g_controllerMapGeneration) { + prevGeneration = g_controllerMapGeneration; + return true; + } + return false; +} + } // KeyMap diff --git a/Core/KeyMap.h b/Core/KeyMap.h index 16f4b5d37c..01da7e9596 100644 --- a/Core/KeyMap.h +++ b/Core/KeyMap.h @@ -158,4 +158,6 @@ namespace KeyMap { void AutoConfForPad(const std::string &name); bool IsKeyMapped(int device, int key); + + bool HasChanged(int &prevGeneration); } diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 574e795ddd..8e0471643b 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); @@ -261,17 +264,26 @@ void ControlMappingScreen::CreateViews() { new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); mappers_.push_back(mapper); } + + keyMapGeneration_ = KeyMap::g_controllerMapGeneration; +} + +void ControlMappingScreen::update() { + if (KeyMap::HasChanged(keyMapGeneration_)) { + RecreateViews(); + } + + UIDialogScreenWithBackground::update(); } UI::EventReturn ControlMappingScreen::OnClearMapping(UI::EventParams ¶ms) { KeyMap::g_controllerMap.clear(); - RecreateViews(); + KeyMap::g_controllerMapGeneration++; return UI::EVENT_DONE; } UI::EventReturn ControlMappingScreen::OnDefaultMapping(UI::EventParams ¶ms) { KeyMap::RestoreDefault(); - RecreateViews(); return UI::EVENT_DONE; } @@ -289,11 +301,16 @@ 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; KeyMap::AutoConfForPad(popup->GetChoiceString()); - RecreateViews(); } } @@ -325,7 +342,7 @@ bool KeyMappingNewKeyDialog::key(const KeyInput &key) { mapped_ = true; KeyDef kdf(key.deviceId, key.keyCode); - TriggerFinish(DR_OK); + TriggerFinish(DR_YES); if (callback_) callback_(kdf); } @@ -352,7 +369,7 @@ bool KeyMappingNewMouseKeyDialog::key(const KeyInput &key) { mapped_ = true; KeyDef kdf(key.deviceId, key.keyCode); - TriggerFinish(DR_OK); + TriggerFinish(DR_YES); g_Config.bMapMouse = false; if (callback_) callback_(kdf); @@ -390,7 +407,7 @@ bool KeyMappingNewKeyDialog::axis(const AxisInput &axis) { if (axis.value > AXIS_BIND_THRESHOLD) { mapped_ = true; KeyDef kdf(axis.deviceId, KeyMap::TranslateKeyCodeFromAxis(axis.axisId, 1)); - TriggerFinish(DR_OK); + TriggerFinish(DR_YES); if (callback_) callback_(kdf); } @@ -398,7 +415,7 @@ bool KeyMappingNewKeyDialog::axis(const AxisInput &axis) { if (axis.value < -AXIS_BIND_THRESHOLD) { mapped_ = true; KeyDef kdf(axis.deviceId, KeyMap::TranslateKeyCodeFromAxis(axis.axisId, -1)); - TriggerFinish(DR_OK); + TriggerFinish(DR_YES); if (callback_) callback_(kdf); } @@ -414,7 +431,7 @@ bool KeyMappingNewMouseKeyDialog::axis(const AxisInput &axis) { if (axis.value > AXIS_BIND_THRESHOLD) { mapped_ = true; KeyDef kdf(axis.deviceId, KeyMap::TranslateKeyCodeFromAxis(axis.axisId, 1)); - TriggerFinish(DR_OK); + TriggerFinish(DR_YES); if (callback_) callback_(kdf); } @@ -422,7 +439,7 @@ bool KeyMappingNewMouseKeyDialog::axis(const AxisInput &axis) { if (axis.value < -AXIS_BIND_THRESHOLD) { mapped_ = true; KeyDef kdf(axis.deviceId, KeyMap::TranslateKeyCodeFromAxis(axis.axisId, -1)); - TriggerFinish(DR_OK); + TriggerFinish(DR_YES); if (callback_) callback_(kdf); } @@ -840,3 +857,343 @@ 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::Clickable { +public: + MockButton(int button, ImageID img, ImageID bg, float angle, UI::LayoutParams *layoutParams = nullptr) + : Clickable(layoutParams), button_(button), img_(img), bgImg_(bg), angle_(angle) { + } + + void Draw(UIContext &dc) override { + uint32_t c = 0xFFFFFFFF; + if (HasFocus() || Selected()) + 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 *SetSelectedButton(int *s) { + selectedButton_ = s; + return this; + } + + bool Selected() { + return selectedButton_ && *selectedButton_ == button_; + } + + int Button() { + return button_; + } + +private: + int button_; + ImageID img_; + ImageID bgImg_; + float angle_; + float scale_ = 1.0f; + float offsetX_ = 0.0f; + float offsetY_ = 0.0f; + bool flipHBG_ = false; + int *selectedButton_ = nullptr; +}; + +class MockPSP : public UI::AnchorLayout { +public: + static constexpr float SCALE = 1.4f; + + MockPSP(UI::LayoutParams *layoutParams = nullptr); + void SelectButton(int btn); + float GetPopupOffset(); + + UI::Event ButtonClick; + +private: + UI::AnchorLayoutParams *LayoutAt(float l, float t, float r, float b); + UI::AnchorLayoutParams *LayoutSize(float w, float h, float l, float t); + MockButton *AddButton(int button, ImageID img, ImageID bg, float angle, UI::LayoutParams *lp); + + UI::EventReturn OnSelectButton(UI::EventParams &e); + + std::unordered_map buttons_; + int selectedButton_ = 0; +}; + +MockPSP::MockPSP(UI::LayoutParams *layoutParams) : AnchorLayout(layoutParams) { + Add(new Backplate(SCALE)); + Add(new MockScreen(LayoutAt(99.0f, 13.0f, 97.0f, 33.0f))); + + // Left side. + AddButton(VIRTKEY_AXIS_Y_MAX, 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); +} + +void MockPSP::SelectButton(int btn) { + selectedButton_ = btn; +} + +float MockPSP::GetPopupOffset() { + MockButton *view = buttons_[selectedButton_]; + if (!view) + return 0.0f; + + float ypos = view->GetBounds().centerY(); + if (ypos > bounds_.centerY()) { + return -0.25f; + } + return 0.25f; +} + +UI::AnchorLayoutParams *MockPSP::LayoutAt(float l, float t, float r, float b) { + return new UI::AnchorLayoutParams(l * SCALE, t * SCALE, r * SCALE, b * SCALE); +} +UI::AnchorLayoutParams *MockPSP::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 *MockPSP::AddButton(int button, ImageID img, ImageID bg, float angle, UI::LayoutParams *lp) { + MockButton *view = Add(new MockButton(button, img, bg, angle, lp)); + view->OnClick.Handle(this, &MockPSP::OnSelectButton); + view->SetSelectedButton(&selectedButton_); + buttons_[button] = view; + return view; +} + +UI::EventReturn MockPSP::OnSelectButton(UI::EventParams &e) { + auto view = (MockButton *)e.v; + e.a = view->Button(); + return ButtonClick.Dispatch(e); +} + +static std::vector bindAllOrder{ + CTRL_LTRIGGER, + CTRL_RTRIGGER, + CTRL_UP, + CTRL_DOWN, + CTRL_LEFT, + CTRL_RIGHT, + VIRTKEY_AXIS_Y_MAX, + VIRTKEY_AXIS_Y_MIN, + VIRTKEY_AXIS_X_MIN, + VIRTKEY_AXIS_X_MAX, + CTRL_HOME, + CTRL_SELECT, + CTRL_START, + CTRL_CROSS, + CTRL_CIRCLE, + CTRL_TRIANGLE, + CTRL_SQUARE, +}; + +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 Choice(km->T("Bind All")))->OnClick.Handle(this, &VisualMappingScreen::OnBindAll); + leftColumn->Add(new CheckBox(&replace_, km->T("Replace"), "")); + + 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)); + psp_ = rightColumn->Add(new MockPSP(new AnchorLayoutParams(bounds.centerX(), bounds.centerY(), NONE, NONE, true))); + psp_->ButtonClick.Handle(this, &VisualMappingScreen::OnMapButton); + + root_->Add(leftColumn); + root_->Add(rightColumn); +} + +void VisualMappingScreen::resized() { + UIDialogScreenWithBackground::resized(); + RecreateViews(); +} + +UI::EventReturn VisualMappingScreen::OnMapButton(UI::EventParams &e) { + nextKey_ = e.a; + MapNext(); + return UI::EVENT_DONE; +} + +UI::EventReturn VisualMappingScreen::OnBindAll(UI::EventParams &e) { + bindAll_ = 0; + nextKey_ = bindAllOrder[bindAll_]; + MapNext(); + return UI::EVENT_DONE; +} + +void VisualMappingScreen::HandleKeyMapping(KeyDef key) { + KeyMap::SetKeyMapping(nextKey_, key, replace_); + + if (bindAll_ < 0) { + // For analog, we do each direction in a row. + if (nextKey_ == VIRTKEY_AXIS_Y_MAX) + nextKey_ = VIRTKEY_AXIS_Y_MIN; + else if (nextKey_ == VIRTKEY_AXIS_Y_MIN) + nextKey_ = VIRTKEY_AXIS_X_MIN; + else if (nextKey_ == VIRTKEY_AXIS_X_MIN) + nextKey_ = VIRTKEY_AXIS_X_MAX; + else + nextKey_ = 0; + } else if ((size_t)bindAll_ + 1 < bindAllOrder.size()) { + bindAll_++; + nextKey_ = bindAllOrder[bindAll_]; + } else { + bindAll_ = -1; + nextKey_ = 0; + } +} + +void VisualMappingScreen::dialogFinished(const Screen *dialog, DialogResult result) { + if (result == DR_YES && nextKey_ != 0) { + MapNext(); + } else { + nextKey_ = 0; + bindAll_ = -1; + psp_->SelectButton(0); + } +} + +void VisualMappingScreen::MapNext() { + auto km = GetI18NCategory("KeyMapping"); + + if (nextKey_ == VIRTKEY_AXIS_Y_MIN || nextKey_ == VIRTKEY_AXIS_X_MIN || nextKey_ == VIRTKEY_AXIS_X_MAX) { + psp_->SelectButton(VIRTKEY_AXIS_Y_MAX); + } else { + psp_->SelectButton(nextKey_); + } + auto dialog = new KeyMappingNewKeyDialog(nextKey_, true, std::bind(&VisualMappingScreen::HandleKeyMapping, this, std::placeholders::_1), km); + + Bounds bounds = screenManager()->getUIContext()->GetLayoutBounds(); + dialog->SetPopupOffset(psp_->GetPopupOffset() * bounds.h); + screenManager()->push(dialog); +} diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index 025d4bc40c..496fab41f3 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -39,16 +39,20 @@ public: std::string tag() const override { return "control mapping"; } protected: - virtual void CreateViews() override; + void CreateViews() override; + void update() override; + 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; UI::ScrollView *rightScroll_; std::vector mappers_; + int keyMapGeneration_ = -1; }; class KeyMappingNewKeyDialog : public PopupScreen { @@ -158,3 +162,27 @@ protected: UI::EventReturn OnRenderingBackend(UI::EventParams &e); UI::EventReturn OnRecreateActivity(UI::EventParams &e); }; + +class MockPSP; + +class VisualMappingScreen : public UIDialogScreenWithBackground { +public: + VisualMappingScreen() {} + +protected: + void CreateViews() override; + + void dialogFinished(const Screen *dialog, DialogResult result) override; + void resized() override; + +private: + UI::EventReturn OnMapButton(UI::EventParams &e); + UI::EventReturn OnBindAll(UI::EventParams &e); + void HandleKeyMapping(KeyDef key); + void MapNext(); + + MockPSP *psp_ = nullptr; + int nextKey_ = 0; + int bindAll_ = -1; + bool replace_ = false; +}; diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index ac186ad09c..026540f443 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -351,7 +351,7 @@ namespace MainWindow { bool changed = false; const std::string curLanguageID = i18nrepo.LanguageID(); - if (curLanguageID != menuLanguageID || menuKeymapGeneration != KeyMap::g_controllerMapGeneration) { + if (curLanguageID != menuLanguageID || KeyMap::HasChanged(menuKeymapGeneration)) { DoTranslateMenus(hWnd, menu); menuLanguageID = curLanguageID; changed = true;