Improve scrolling in list windows and fix a crash in a few menus when no items are selectable

This commit is contained in:
Andrew Lanzone 2025-05-26 23:07:20 -07:00
parent 274434e0d6
commit f67aae086a
8 changed files with 45 additions and 24 deletions

View File

@ -473,7 +473,8 @@ namespace MWGui
{ {
if (arg.button == SDL_CONTROLLER_BUTTON_A) if (arg.button == SDL_CONTROLLER_BUTTON_A)
{ {
onButtonClicked(mButtons[mControllerFocus]); if (mControllerFocus >= 0 && mControllerFocus < mButtons.size())
onButtonClicked(mButtons[mControllerFocus]);
} }
else if (arg.button == SDL_CONTROLLER_BUTTON_B) else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{ {

View File

@ -446,6 +446,7 @@ namespace MWGui
{ {
if (arg.button == SDL_CONTROLLER_BUTTON_A) if (arg.button == SDL_CONTROLLER_BUTTON_A)
{ {
mControllerFocus = std::clamp(mControllerFocus, 0, (int)mButtons.size() - 1);
buttonActivated(mButtons[mControllerFocus]); buttonActivated(mButtons[mControllerFocus]);
} }
else if (arg.button == SDL_CONTROLLER_BUTTON_B) else if (arg.button == SDL_CONTROLLER_BUTTON_B)

View File

@ -78,7 +78,7 @@ namespace MWGui
toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onSpellButtonClick); toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onSpellButtonClick);
mSpellsWidgetMap.insert(std::make_pair(toAdd, spell.mId)); mSpellsWidgetMap.insert(std::make_pair(toAdd, spell.mId));
if (price <= playerGold) if (price <= playerGold)
mSpellButtons.emplace_back(toAdd); mSpellButtons.emplace_back(std::make_pair(toAdd, mSpellsWidgetMap.size()));
} }
void SpellBuyingWindow::clearSpells() void SpellBuyingWindow::clearSpells()
@ -143,7 +143,7 @@ namespace MWGui
{ {
mControllerFocus = 0; mControllerFocus = 0;
if (mSpellButtons.size() > 0) if (mSpellButtons.size() > 0)
mSpellButtons[0]->setStateSelected(true); mSpellButtons[0].first->setStateSelected(true);
} }
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the
@ -221,7 +221,8 @@ namespace MWGui
{ {
if (arg.button == SDL_CONTROLLER_BUTTON_A) if (arg.button == SDL_CONTROLLER_BUTTON_A)
{ {
onSpellButtonClick(mSpellButtons[mControllerFocus]); if (mControllerFocus >= 0 && mControllerFocus < mSpellButtons.size())
onSpellButtonClick(mSpellButtons[mControllerFocus].first);
} }
else if (arg.button == SDL_CONTROLLER_BUTTON_B) else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{ {
@ -232,25 +233,29 @@ namespace MWGui
if (mSpellButtons.size() <= 1) if (mSpellButtons.size() <= 1)
return true; return true;
mSpellButtons[mControllerFocus]->setStateSelected(false); mSpellButtons[mControllerFocus].first->setStateSelected(false);
mControllerFocus = wrap(mControllerFocus - 1, mSpellButtons.size()); mControllerFocus = wrap(mControllerFocus - 1, mSpellButtons.size());
mSpellButtons[mControllerFocus]->setStateSelected(true); mSpellButtons[mControllerFocus].first->setStateSelected(true);
} }
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{ {
if (mSpellButtons.size() <= 1) if (mSpellButtons.size() <= 1)
return true; return true;
mSpellButtons[mControllerFocus]->setStateSelected(false); mSpellButtons[mControllerFocus].first->setStateSelected(false);
mControllerFocus = wrap(mControllerFocus + 1, mSpellButtons.size()); mControllerFocus = wrap(mControllerFocus + 1, mSpellButtons.size());
mSpellButtons[mControllerFocus]->setStateSelected(true); mSpellButtons[mControllerFocus].first->setStateSelected(true);
} }
// Scroll the list to keep the active item in view // Scroll the list to keep the active item in view
if (mControllerFocus <= 5) int line = mSpellButtons[mControllerFocus].second;
if (line <= 5)
mSpellsView->setViewOffset(MyGUI::IntPoint(0, 0)); mSpellsView->setViewOffset(MyGUI::IntPoint(0, 0));
else else
mSpellsView->setViewOffset(MyGUI::IntPoint(0, -10 * (mControllerFocus - 5))); {
const int lineHeight = Settings::gui().mFontSize + 2;
mSpellsView->setViewOffset(MyGUI::IntPoint(0, -lineHeight * (line - 5)));
}
return true; return true;
} }

View File

@ -39,7 +39,8 @@ namespace MWGui
MyGUI::ScrollView* mSpellsView; MyGUI::ScrollView* mSpellsView;
std::map<MyGUI::Widget*, ESM::RefId> mSpellsWidgetMap; std::map<MyGUI::Widget*, ESM::RefId> mSpellsWidgetMap;
std::vector<MyGUI::Button*> mSpellButtons; /// List of enabled/purchasable spells and their index in the full list.
std::vector<std::pair<MyGUI::Button*, int>> mSpellButtons;
void onCancelButtonClicked(MyGUI::Widget* _sender); void onCancelButtonClicked(MyGUI::Widget* _sender);
void onSpellButtonClick(MyGUI::Widget* _sender); void onSpellButtonClick(MyGUI::Widget* _sender);

View File

@ -122,7 +122,7 @@ namespace MWGui
t->setCaption(spell.mName + captionSuffix); t->setCaption(spell.mName + captionSuffix);
t->setTextAlign(MyGUI::Align::Left); t->setTextAlign(MyGUI::Align::Left);
adjustSpellWidget(spell, i, t); adjustSpellWidget(spell, i, t);
mButtons.emplace_back(t); mButtons.emplace_back(std::make_pair(t, i));
if (!spell.mCostColumn.empty() && mShowCostColumn) if (!spell.mCostColumn.empty() && mShowCostColumn)
{ {
@ -339,7 +339,7 @@ namespace MWGui
// Select the focused item, if any. // Select the focused item, if any.
if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) if (mControllerFocus >= 0 && mControllerFocus < mButtons.size())
{ {
onSpellSelected(mButtons.at(mControllerFocus)); onSpellSelected(mButtons[mControllerFocus].first);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
} }
} }
@ -348,12 +348,16 @@ namespace MWGui
// Toggle info tooltip // Toggle info tooltip
mControllerTooltip = !mControllerTooltip; mControllerTooltip = !mControllerTooltip;
if (mControllerTooltip && mControllerFocus >= 0 && mControllerFocus < mButtons.size()) if (mControllerTooltip && mControllerFocus >= 0 && mControllerFocus < mButtons.size())
MWBase::Environment::get().getInputManager()->warpMouseToWidget(mButtons.at(mControllerFocus)); MWBase::Environment::get().getInputManager()->warpMouseToWidget(mButtons[mControllerFocus].first);
} }
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
mControllerFocus--; mControllerFocus--;
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
mControllerFocus++; mControllerFocus++;
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
mControllerFocus = std::max(0, mControllerFocus - 10);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
mControllerFocus = std::min(mControllerFocus + 10, (int)mButtons.size() - 1);
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
{ {
// Jump to first item in previous group // Jump to first item in previous group
@ -380,10 +384,7 @@ namespace MWGui
} }
} }
if (mControllerFocus < 0) mControllerFocus = wrap(mControllerFocus, mButtons.size());
mControllerFocus = mButtons.size() - 1;
else if (mControllerFocus >= mButtons.size())
mControllerFocus = 0;
if (prevFocus != mControllerFocus) if (prevFocus != mControllerFocus)
updateControllerFocus(prevFocus, mControllerFocus); updateControllerFocus(prevFocus, mControllerFocus);
@ -396,19 +397,29 @@ namespace MWGui
if (prevFocus >= 0 && prevFocus < mButtons.size()) if (prevFocus >= 0 && prevFocus < mButtons.size())
{ {
Gui::SharedStateButton* prev = mButtons.at(prevFocus); Gui::SharedStateButton* prev = mButtons[prevFocus].first;
if (prev) if (prev)
prev->onMouseLostFocus(nullptr); prev->onMouseLostFocus(nullptr);
} }
if (newFocus >= 0 && newFocus < mButtons.size()) if (newFocus >= 0 && newFocus < mButtons.size())
{ {
Gui::SharedStateButton* focused = mButtons.at(newFocus); Gui::SharedStateButton* focused = mButtons[newFocus].first;
if (focused) if (focused)
{ {
focused->onMouseSetFocus(nullptr); focused->onMouseSetFocus(nullptr);
if (mControllerTooltip) if (mControllerTooltip)
MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused); MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused);
// Scroll the list to keep the active item in view
int line = mButtons[newFocus].second;
if (line <= 5)
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
else
{
const int lineHeight = focused->getHeight();
mScrollView->setViewOffset(MyGUI::IntPoint(0, -lineHeight * (line - 5)));
}
} }
} }
} }

View File

@ -94,8 +94,8 @@ namespace MWGui
void addGroup(const std::string& label1, const std::string& label2); void addGroup(const std::string& label1, const std::string& label2);
void adjustSpellWidget(const Spell& spell, SpellModel::ModelIndex index, MyGUI::Widget* widget); void adjustSpellWidget(const Spell& spell, SpellModel::ModelIndex index, MyGUI::Widget* widget);
/// Keep a list of buttons for controller navigation /// Keep a list of buttons for controller navigation and their index in the full list.
std::vector<Gui::SharedStateButton *> mButtons; std::vector<std::pair<Gui::SharedStateButton *, int>> mButtons;
/// Keep a list of group offsets for controller navigation /// Keep a list of group offsets for controller navigation
std::vector<int> mGroupIndices; std::vector<int> mGroupIndices;

View File

@ -251,7 +251,8 @@ namespace MWGui
{ {
if (arg.button == SDL_CONTROLLER_BUTTON_A) if (arg.button == SDL_CONTROLLER_BUTTON_A)
{ {
onTrainingSelected(mTrainingButtons[mControllerFocus]); if (mControllerFocus >= 0 && mControllerFocus < mTrainingButtons.size())
onTrainingSelected(mTrainingButtons[mControllerFocus]);
} }
else if (arg.button == SDL_CONTROLLER_BUTTON_B) else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{ {

View File

@ -258,7 +258,8 @@ namespace MWGui
{ {
if (arg.button == SDL_CONTROLLER_BUTTON_A) if (arg.button == SDL_CONTROLLER_BUTTON_A)
{ {
onTravelButtonClick(mDestinationButtons[mControllerFocus]); if (mControllerFocus >= 0 && mControllerFocus < mDestinationButtons.size())
onTravelButtonClick(mDestinationButtons[mControllerFocus]);
} }
else if (arg.button == SDL_CONTROLLER_BUTTON_B) else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{ {