forked from teamnwah/openmw-tes3coop
Refactor spell window to use model/view and remove duplicated code in QuickKeysMenu
This should also improve window resizing performance, the widgets are now just resized instead of recreated.moveref
parent
7abbca8be9
commit
79237d16a7
@ -0,0 +1,140 @@
|
||||
#include "spellmodel.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool sortSpells(const MWGui::Spell& left, const MWGui::Spell& right)
|
||||
{
|
||||
if (left.mType != right.mType)
|
||||
return left.mType < right.mType;
|
||||
|
||||
int cmp = left.mName.compare(right.mName);
|
||||
return cmp < 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
SpellModel::SpellModel(const MWWorld::Ptr &actor)
|
||||
: mActor(actor)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SpellModel::update()
|
||||
{
|
||||
mSpells.clear();
|
||||
|
||||
MWMechanics::CreatureStats& stats = mActor.getClass().getCreatureStats(mActor);
|
||||
const MWMechanics::Spells& spells = stats.getSpells();
|
||||
|
||||
const MWWorld::ESMStore &esmStore =
|
||||
MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
const ESM::Spell* spell = esmStore.get<ESM::Spell>().find(it->first);
|
||||
if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
continue;
|
||||
|
||||
Spell newSpell;
|
||||
newSpell.mName = spell->mName;
|
||||
if (spell->mData.mType == ESM::Spell::ST_Spell)
|
||||
{
|
||||
newSpell.mType = Spell::Type_Spell;
|
||||
std::string cost = boost::lexical_cast<std::string>(spell->mData.mCost);
|
||||
std::string chance = boost::lexical_cast<std::string>(int(MWMechanics::getSpellSuccessChance(spell, mActor)));
|
||||
newSpell.mCostColumn = cost + "/" + chance;
|
||||
}
|
||||
else
|
||||
newSpell.mType = Spell::Type_Power;
|
||||
newSpell.mId = it->first;
|
||||
|
||||
newSpell.mSelected = (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == it->first);
|
||||
newSpell.mActive = true;
|
||||
mSpells.push_back(newSpell);
|
||||
}
|
||||
|
||||
MWWorld::InventoryStore& invStore = mActor.getClass().getInventoryStore(mActor);
|
||||
for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it)
|
||||
{
|
||||
MWWorld::Ptr item = *it;
|
||||
const std::string enchantId = item.getClass().getEnchantment(item);
|
||||
if (enchantId.empty())
|
||||
continue;
|
||||
const ESM::Enchantment* enchant =
|
||||
esmStore.get<ESM::Enchantment>().find(item.getClass().getEnchantment(item));
|
||||
if (enchant->mData.mType != ESM::Enchantment::WhenUsed && enchant->mData.mType != ESM::Enchantment::CastOnce)
|
||||
continue;
|
||||
|
||||
Spell newSpell;
|
||||
newSpell.mItem = item;
|
||||
newSpell.mId = item.getClass().getId(item);
|
||||
newSpell.mName = item.getClass().getName(item);
|
||||
newSpell.mType = Spell::Type_EnchantedItem;
|
||||
newSpell.mSelected = invStore.getSelectedEnchantItem() == it;
|
||||
|
||||
// FIXME: move to mwmechanics
|
||||
if (enchant->mData.mType == ESM::Enchantment::CastOnce)
|
||||
{
|
||||
newSpell.mCostColumn = "100/100";
|
||||
newSpell.mActive = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
float enchantCost = enchant->mData.mCost;
|
||||
int eSkill = mActor.getClass().getSkill(mActor, ESM::Skill::Enchant);
|
||||
int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10));
|
||||
|
||||
std::string cost = boost::lexical_cast<std::string>(castCost);
|
||||
int currentCharge = int(item.getCellRef().getEnchantmentCharge());
|
||||
if (currentCharge == -1)
|
||||
currentCharge = enchant->mData.mCharge;
|
||||
std::string charge = boost::lexical_cast<std::string>(currentCharge);
|
||||
newSpell.mCostColumn = cost + "/" + charge;
|
||||
|
||||
bool equipped = false;
|
||||
for (int i=0; i < MWWorld::InventoryStore::Slots; ++i)
|
||||
{
|
||||
if (invStore.getSlot(i) != invStore.end() && *invStore.getSlot(i) == item)
|
||||
{
|
||||
equipped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
newSpell.mActive = equipped;
|
||||
}
|
||||
mSpells.push_back(newSpell);
|
||||
}
|
||||
|
||||
std::stable_sort(mSpells.begin(), mSpells.end(), sortSpells);
|
||||
}
|
||||
|
||||
size_t SpellModel::getItemCount() const
|
||||
{
|
||||
return mSpells.size();
|
||||
}
|
||||
|
||||
Spell SpellModel::getItem(ModelIndex index) const
|
||||
{
|
||||
if (index < 0 || index >= int(mSpells.size()))
|
||||
throw std::runtime_error("invalid spell index supplied");
|
||||
return mSpells[index];
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
#ifndef OPENMW_GUI_SPELLMODEL_H
|
||||
#define OPENMW_GUI_SPELLMODEL_H
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
struct Spell
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Type_Power,
|
||||
Type_Spell,
|
||||
Type_EnchantedItem
|
||||
};
|
||||
|
||||
Type mType;
|
||||
std::string mName;
|
||||
std::string mCostColumn; // Cost/chance or Cost/charge
|
||||
std::string mId; // Item ID or spell ID
|
||||
MWWorld::Ptr mItem; // Only for Type_EnchantedItem
|
||||
bool mSelected; // Is this the currently selected spell/item (only one can be selected at a time)
|
||||
bool mActive; // (Items only) is the item equipped?
|
||||
|
||||
Spell()
|
||||
: mSelected(false)
|
||||
, mActive(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
///@brief Model that lists all usable powers, spells and enchanted items for an actor.
|
||||
class SpellModel
|
||||
{
|
||||
public:
|
||||
SpellModel(const MWWorld::Ptr& actor);
|
||||
|
||||
typedef int ModelIndex;
|
||||
|
||||
void update();
|
||||
|
||||
Spell getItem (ModelIndex index) const;
|
||||
///< throws for invalid index
|
||||
|
||||
size_t getItemCount() const;
|
||||
|
||||
private:
|
||||
MWWorld::Ptr mActor;
|
||||
|
||||
std::vector<Spell> mSpells;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,255 @@
|
||||
#include "spellview.hpp"
|
||||
|
||||
#include <MyGUI_FactoryManager.h>
|
||||
#include <MyGUI_ScrollView.h>
|
||||
#include <MyGUI_ImageBox.h>
|
||||
#include <MyGUI_Gui.h>
|
||||
|
||||
#include <components/widgets/sharedstatebutton.hpp>
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
SpellView::SpellView()
|
||||
: mShowCostColumn(true)
|
||||
, mHighlightSelected(true)
|
||||
, mScrollView(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void SpellView::initialiseOverride()
|
||||
{
|
||||
Base::initialiseOverride();
|
||||
|
||||
assignWidget(mScrollView, "ScrollView");
|
||||
if (mScrollView == NULL)
|
||||
throw std::runtime_error("Item view needs a scroll view");
|
||||
|
||||
mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
}
|
||||
|
||||
void SpellView::registerComponents()
|
||||
{
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<SpellView>("Widget");
|
||||
}
|
||||
|
||||
void SpellView::setModel(SpellModel *model)
|
||||
{
|
||||
mModel.reset(model);
|
||||
update();
|
||||
}
|
||||
|
||||
SpellModel* SpellView::getModel()
|
||||
{
|
||||
return mModel.get();
|
||||
}
|
||||
|
||||
void SpellView::setShowCostColumn(bool show)
|
||||
{
|
||||
if (show != mShowCostColumn)
|
||||
{
|
||||
mShowCostColumn = show;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void SpellView::setHighlightSelected(bool highlight)
|
||||
{
|
||||
if (highlight != mHighlightSelected)
|
||||
{
|
||||
mHighlightSelected = highlight;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void SpellView::update()
|
||||
{
|
||||
if (!mModel.get())
|
||||
return;
|
||||
|
||||
mModel->update();
|
||||
|
||||
int curType = -1;
|
||||
|
||||
const int spellHeight = 18;
|
||||
|
||||
mLines.clear();
|
||||
|
||||
while (mScrollView->getChildCount())
|
||||
MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0));
|
||||
|
||||
for (SpellModel::ModelIndex i = 0; i<int(mModel->getItemCount()); ++i)
|
||||
{
|
||||
const Spell& spell = mModel->getItem(i);
|
||||
if (curType != spell.mType)
|
||||
{
|
||||
if (spell.mType == Spell::Type_Power)
|
||||
addGroup("#{sPowers}", "");
|
||||
else if (spell.mType == Spell::Type_Spell)
|
||||
addGroup("#{sSpells}", "#{sCostChance}");
|
||||
else
|
||||
addGroup("#{sMagicItem}", "#{sCostCharge}");
|
||||
curType = spell.mType;
|
||||
}
|
||||
|
||||
const std::string skin = spell.mActive ? "SandTextButton" : "SpellTextUnequipped";
|
||||
|
||||
Gui::SharedStateButton* t = mScrollView->createWidget<Gui::SharedStateButton>(skin,
|
||||
MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
t->setCaption(spell.mName);
|
||||
t->setTextAlign(MyGUI::Align::Left);
|
||||
adjustSpellWidget(spell, i, t);
|
||||
|
||||
if (!spell.mCostColumn.empty() && mShowCostColumn)
|
||||
{
|
||||
Gui::SharedStateButton* costChance = mScrollView->createWidget<Gui::SharedStateButton>(skin,
|
||||
MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
costChance->setCaption(spell.mCostColumn);
|
||||
costChance->setTextAlign(MyGUI::Align::Right);
|
||||
adjustSpellWidget(spell, i, costChance);
|
||||
|
||||
Gui::ButtonGroup group;
|
||||
group.push_back(t);
|
||||
group.push_back(costChance);
|
||||
Gui::SharedStateButton::createButtonGroup(group);
|
||||
|
||||
mLines.push_back(std::make_pair(t, costChance));
|
||||
}
|
||||
else
|
||||
mLines.push_back(std::make_pair(t, (MyGUI::Widget*)NULL));
|
||||
|
||||
t->setStateSelected(spell.mSelected);
|
||||
}
|
||||
|
||||
layoutWidgets();
|
||||
}
|
||||
|
||||
void SpellView::layoutWidgets()
|
||||
{
|
||||
int height = 0;
|
||||
for (std::vector< std::pair<MyGUI::Widget*, MyGUI::Widget*> >::iterator it = mLines.begin();
|
||||
it != mLines.end(); ++it)
|
||||
{
|
||||
height += (it->first)->getHeight();
|
||||
}
|
||||
|
||||
bool scrollVisible = height > mScrollView->getHeight();
|
||||
int width = mScrollView->getWidth() - (scrollVisible ? 18 : 0);
|
||||
|
||||
height = 0;
|
||||
for (std::vector< std::pair<MyGUI::Widget*, MyGUI::Widget*> >::iterator it = mLines.begin();
|
||||
it != mLines.end(); ++it)
|
||||
{
|
||||
int lineHeight = (it->first)->getHeight();
|
||||
(it->first)->setCoord(4, height, width-8, lineHeight);
|
||||
if (it->second)
|
||||
{
|
||||
(it->second)->setCoord(4, height, width-8, lineHeight);
|
||||
MyGUI::TextBox* second = (it->second)->castType<MyGUI::TextBox>(false);
|
||||
if (second)
|
||||
(it->first)->setSize(width-8-second->getTextSize().width, lineHeight);
|
||||
}
|
||||
|
||||
height += lineHeight;
|
||||
}
|
||||
|
||||
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
|
||||
mScrollView->setVisibleVScroll(false);
|
||||
mScrollView->setCanvasSize(mScrollView->getWidth(), std::max(mScrollView->getHeight(), height));
|
||||
mScrollView->setVisibleVScroll(true);
|
||||
}
|
||||
|
||||
void SpellView::addGroup(const std::string &label, const std::string& label2)
|
||||
{
|
||||
if (mScrollView->getChildCount() > 0)
|
||||
{
|
||||
MyGUI::ImageBox* separator = mScrollView->createWidget<MyGUI::ImageBox>("MW_HLine",
|
||||
MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
separator->setNeedMouseFocus(false);
|
||||
mLines.push_back(std::make_pair(separator, (MyGUI::Widget*)NULL));
|
||||
}
|
||||
|
||||
MyGUI::TextBox* groupWidget = mScrollView->createWidget<MyGUI::TextBox>("SandBrightText",
|
||||
MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
groupWidget->setCaptionWithReplacing(label);
|
||||
groupWidget->setTextAlign(MyGUI::Align::Left);
|
||||
groupWidget->setNeedMouseFocus(false);
|
||||
|
||||
if (label2 != "")
|
||||
{
|
||||
MyGUI::TextBox* groupWidget2 = mScrollView->createWidget<MyGUI::TextBox>("SandBrightText",
|
||||
MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
groupWidget2->setCaptionWithReplacing(label2);
|
||||
groupWidget2->setTextAlign(MyGUI::Align::Right);
|
||||
groupWidget2->setNeedMouseFocus(false);
|
||||
|
||||
mLines.push_back(std::make_pair(groupWidget, groupWidget2));
|
||||
}
|
||||
else
|
||||
mLines.push_back(std::make_pair(groupWidget, (MyGUI::Widget*)NULL));
|
||||
}
|
||||
|
||||
|
||||
void SpellView::setSize(const MyGUI::IntSize &_value)
|
||||
{
|
||||
bool changed = (_value.width != getWidth() || _value.height != getHeight());
|
||||
Base::setSize(_value);
|
||||
if (changed)
|
||||
layoutWidgets();
|
||||
}
|
||||
|
||||
void SpellView::setSize(int _width, int _height)
|
||||
{
|
||||
setSize(MyGUI::IntSize(_width, _height));
|
||||
}
|
||||
|
||||
void SpellView::setCoord(const MyGUI::IntCoord &_value)
|
||||
{
|
||||
bool changed = (_value.width != getWidth() || _value.height != getHeight());
|
||||
Base::setCoord(_value);
|
||||
if (changed)
|
||||
layoutWidgets();
|
||||
}
|
||||
|
||||
void SpellView::setCoord(int _left, int _top, int _width, int _height)
|
||||
{
|
||||
setCoord(MyGUI::IntCoord(_left, _top, _width, _height));
|
||||
}
|
||||
|
||||
void SpellView::adjustSpellWidget(const Spell &spell, SpellModel::ModelIndex index, MyGUI::Widget *widget)
|
||||
{
|
||||
if (spell.mType == Spell::Type_EnchantedItem)
|
||||
{
|
||||
widget->setUserData(spell.mItem);
|
||||
widget->setUserString("ToolTipType", "ItemPtr");
|
||||
}
|
||||
else
|
||||
{
|
||||
widget->setUserString("ToolTipType", "Spell");
|
||||
widget->setUserString("Spell", spell.mId);
|
||||
}
|
||||
|
||||
widget->setUserString("SpellModelIndex", MyGUI::utility::toString(index));
|
||||
|
||||
widget->eventMouseWheel += MyGUI::newDelegate(this, &SpellView::onMouseWheel);
|
||||
widget->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellView::onSpellSelected);
|
||||
}
|
||||
|
||||
void SpellView::onSpellSelected(MyGUI::Widget* _sender)
|
||||
{
|
||||
SpellModel::ModelIndex i = MyGUI::utility::parseInt(_sender->getUserString("SpellModelIndex"));
|
||||
eventSpellClicked(i);
|
||||
}
|
||||
|
||||
void SpellView::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
||||
{
|
||||
if (mScrollView->getViewOffset().top + _rel*0.3 > 0)
|
||||
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
|
||||
else
|
||||
mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
#ifndef OPENMW_GUI_SPELLVIEW_H
|
||||
#define OPENMW_GUI_SPELLVIEW_H
|
||||
|
||||
#include <MyGUI_Widget.h>
|
||||
|
||||
#include "spellmodel.hpp"
|
||||
|
||||
namespace MyGUI
|
||||
{
|
||||
class ScrollView;
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
class SpellModel;
|
||||
|
||||
///@brief Displays a SpellModel in a list widget
|
||||
class SpellView : public MyGUI::Widget
|
||||
{
|
||||
MYGUI_RTTI_DERIVED(SpellView)
|
||||
public:
|
||||
SpellView();
|
||||
|
||||
/// Register needed components with MyGUI's factory manager
|
||||
static void registerComponents ();
|
||||
|
||||
/// Should the cost/chance column be shown?
|
||||
void setShowCostColumn(bool show);
|
||||
|
||||
void setHighlightSelected(bool highlight);
|
||||
|
||||
/// Takes ownership of \a model
|
||||
void setModel (SpellModel* model);
|
||||
|
||||
SpellModel* getModel();
|
||||
|
||||
void update();
|
||||
|
||||
typedef MyGUI::delegates::CMultiDelegate1<SpellModel::ModelIndex> EventHandle_ModelIndex;
|
||||
/// Fired when a spell was clicked
|
||||
EventHandle_ModelIndex eventSpellClicked;
|
||||
|
||||
virtual void initialiseOverride();
|
||||
|
||||
virtual void setSize(const MyGUI::IntSize& _value);
|
||||
virtual void setCoord(const MyGUI::IntCoord& _value);
|
||||
void setSize(int _width, int _height);
|
||||
void setCoord(int _left, int _top, int _width, int _height);
|
||||
|
||||
private:
|
||||
MyGUI::ScrollView* mScrollView;
|
||||
|
||||
std::auto_ptr<SpellModel> mModel;
|
||||
|
||||
std::vector< std::pair<MyGUI::Widget*, MyGUI::Widget*> > mLines;
|
||||
|
||||
bool mShowCostColumn;
|
||||
bool mHighlightSelected;
|
||||
|
||||
void layoutWidgets();
|
||||
void addGroup(const std::string& label1, const std::string& label2);
|
||||
void adjustSpellWidget(const Spell& spell, SpellModel::ModelIndex index, MyGUI::Widget* widget);
|
||||
|
||||
void onSpellSelected(MyGUI::Widget* _sender);
|
||||
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue