From db3b43538aae3ce9762de81295bc24f8eb53f016 Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Thu, 15 Dec 2016 20:27:11 +0300 Subject: Add support for rectangular skills tabs. --- src/CMakeLists.txt | 1 + src/Makefile.am | 1 + src/gui/widgets/skillrectanglelistbox.h | 410 ++++++++++++++++++++++++++++++++ src/gui/widgets/tabs/skilltab.h | 18 +- src/gui/windows/skilldialog.cpp | 27 ++- src/resources/skill/skillinfo.cpp | 2 + src/resources/skill/skillinfo.h | 2 + 7 files changed, 459 insertions(+), 2 deletions(-) create mode 100644 src/gui/widgets/skillrectanglelistbox.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f292fb900..fbbde02c1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -277,6 +277,7 @@ SET(SRCS resources/skill/skillinfo.cpp resources/skill/skillinfo.h gui/widgets/skilllistbox.h + gui/widgets/skillrectanglelistbox.h gui/models/skillmodel.cpp gui/models/skillmodel.h gui/models/sortlistmodelbuy.h diff --git a/src/Makefile.am b/src/Makefile.am index fdf0792d2..ebf972ca2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1062,6 +1062,7 @@ manaplus_SOURCES += main.cpp \ resources/skill/skillinfo.cpp \ resources/skill/skillinfo.h \ gui/widgets/skilllistbox.h \ + gui/widgets/skillrectanglelistbox.h \ gui/widgets/tabs/shortcuttab.h \ gui/widgets/tabs/skilltab.h \ gui/widgets/tabs/socialattacktab.h \ diff --git a/src/gui/widgets/skillrectanglelistbox.h b/src/gui/widgets/skillrectanglelistbox.h new file mode 100644 index 000000000..68f9f6f85 --- /dev/null +++ b/src/gui/widgets/skillrectanglelistbox.h @@ -0,0 +1,410 @@ +/* + * The ManaPlus Client + * Copyright (C) 2011-2016 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GUI_WIDGETS_SKILLRECTANGLELISTBOX_H +#define GUI_WIDGETS_SKILLRECTANGLELISTBOX_H + +#include "const/resources/skill.h" + +#include "dragdrop.h" +#include "settings.h" + +#include "gui/skin.h" +#include "gui/viewport.h" + +#include "gui/widgets/widget.h" + +#include "gui/fonts/font.h" + +#include "gui/models/skillmodel.h" + +#include "gui/popups/popupmenu.h" +#include "gui/popups/skillpopup.h" + +#include "utils/delete2.h" +#include "utils/stringutils.h" + +#include "render/graphics.h" + +#include "localconsts.h" + +class SkillModel; + +class SkillRectangleListBox final : public Widget, + public MouseListener +{ + public: + SkillRectangleListBox(const Widget2 *const widget, + SkillModel *const model) : + Widget(widget), + MouseListener(), + mHighlightColor(getThemeColor(ThemeColorId::HIGHLIGHT)), + mTextColor(getThemeColor(ThemeColorId::TEXT)), + mTextColor2(getThemeColor(ThemeColorId::TEXT_OUTLINE)), + mCooldownColor(getThemeColor(ThemeColorId::SKILL_COOLDOWN)), + mForegroundSelectedColor(getThemeColor( + ThemeColorId::LISTBOX_SELECTED)), + mForegroundSelectedColor2(getThemeColor( + ThemeColorId::LISTBOX_SELECTED_OUTLINE)), + mModel(model), + mSkin(nullptr), + mSelected(-1), + mPadding(2), + mBoxWidth(80), + mBoxHeight(70), + mIconXOffset(24), + mIconYOffset(10), + mTextXOffset(0), + mTextYOffset(44), + mSkillClicked(false) + { + if (theme) + mSkin = theme->load("listbox.xml", ""); + + if (mSkin) + { + mPadding = mSkin->getPadding(); + mBoxWidth = mSkin->getOption("boxWidth", 80); + mBoxHeight = mSkin->getOption("boxHeight", 70); + mIconXOffset = mSkin->getOption("iconXOffset", 24); + mIconYOffset = mSkin->getOption("iconYOffset", 10); + mTextXOffset = mSkin->getOption("textXOffset", 0); + mTextYOffset = mSkin->getOption("textYOffset", 44); + } + Font *const font = getFont(); + int minWidth = font->getWidth("Lvl: 10/10") + mTextXOffset + 2; + int minHeight = font->getHeight() + mTextYOffset + 2; + if (mBoxWidth < minWidth) + mBoxWidth = minWidth; + if (mBoxHeight < minHeight) + mBoxHeight = minHeight; + int maxX = 0; + int maxY = 0; + for (int i = 0; + i < model->getNumberOfElements(); + ++i) + { + SkillInfo *const e = model->getSkillAt(i); + if (e) + { + if (e->x > maxX) + maxX = e->x; + if (e->y > maxY) + maxY = e->y; + } + } + maxX ++; + maxY ++; + setWidth(maxX * mBoxWidth); + setHeight(maxY * mBoxHeight); + addMouseListener(this); + } + + A_DELETE_COPY(SkillRectangleListBox) + + ~SkillRectangleListBox() + { + delete2(mModel) + } + + SkillInfo *getSelectedInfo() const + { + if (!mModel) + return nullptr; + const int selected = mSelected; + if (selected < 0 || + selected > mModel->getNumberOfElements()) + { + return nullptr; + } + + return mModel->getSkillAt(selected); + } + + void draw(Graphics *const graphics) override final A_NONNULL(2) + { + if (!mModel) + return; + + SkillModel *const model = mModel; + updateAlpha(); + + int maxX = 0; + int maxY = 0; + mHighlightColor.a = CAST_S32(mAlpha * 255.0F); + graphics->setColor(mHighlightColor); + Font *const font = getFont(); + if (mSelected >= 0) + { + SkillInfo *const e = model->getSkillAt(mSelected); + if (e) + { + //const SkillData *const data = e->data; + const int x = e->x * mBoxWidth + mPadding; + const int y = e->y * mBoxHeight + mPadding; + + graphics->fillRectangle(Rect(x, y, + mBoxWidth, mBoxHeight)); + + const int xOffset = (mBoxWidth - + font->getWidth(e->skillLevel)) / 2; + font->drawString(graphics, + mForegroundSelectedColor, + mForegroundSelectedColor, + e->skillLevel, + x + mTextXOffset + xOffset, + y + mTextYOffset); + } + } + + // +++ need split drawing icons and text + for (int i = 0; + i < model->getNumberOfElements(); + ++i) + { + SkillInfo *const e = model->getSkillAt(i); + if (e) + { + if (e->x > maxX) + maxX = e->x; + if (e->y > maxY) + maxY = e->y; + const SkillData *const data = e->data; + const int x = e->x * mBoxWidth + mPadding; + const int y = e->y * mBoxHeight + mPadding; + + graphics->drawImage(data->icon, + x + mIconXOffset, + y + mIconYOffset); + + if (i != mSelected) + { + const int width1 = font->getWidth(e->skillLevel); + const int xOffset = (mBoxWidth - + width1) / 2; + font->drawString(graphics, + mTextColor, + mTextColor2, + e->skillLevel, + x + mTextXOffset + xOffset, + y + mTextYOffset); + if (e->skillLevelWidth < 0) + { + // Add one for padding + e->skillLevelWidth = width1 + 1; + } + } + else + { + if (e->skillLevelWidth < 0) + { + // Add one for padding + e->skillLevelWidth = font->getWidth(e->skillLevel) + 1; + } + } + } + } + maxX ++; + maxY ++; + setWidth(maxX * mBoxWidth); + setHeight(maxY * mBoxHeight); + } + + void safeDraw(Graphics *const graphics) override final A_NONNULL(2) + { + SkillRectangleListBox::draw(graphics); + } + + const SkillInfo *getSkillByEvent(const MouseEvent &event) const + { + if (!mModel) + return nullptr; + const int posX = (event.getX() - mPadding) / mBoxWidth; + const int posY = (event.getY() - mPadding) / mBoxHeight; + for (int i = 0; + i < mModel->getNumberOfElements(); + ++i) + { + SkillInfo *const e = mModel->getSkillAt(i); + if (e) + { + if (posX == e->x && posY == e->y) + return e; + } + } + return nullptr; + } + + int getSelectionByEvent(const MouseEvent &event) const + { + if (!mModel) + return -1; + const int posX = (event.getX() - mPadding) / mBoxWidth; + const int posY = (event.getY() - mPadding) / mBoxHeight; + for (int i = 0; + i < mModel->getNumberOfElements(); + ++i) + { + SkillInfo *const e = mModel->getSkillAt(i); + if (e) + { + if (posX == e->x && posY == e->y) + return i; + } + } + return -1; + } + + void mouseMoved(MouseEvent &event) override final + { + if (!viewport || !dragDrop.isEmpty()) + return; + + const SkillInfo *const skill = getSkillByEvent(event); + if (!skill) + return; + skillPopup->show(skill, + skill->customSelectedLevel, + skill->customCastType, + skill->customOffsetX, + skill->customOffsetY); + skillPopup->position(viewport->mMouseX, + viewport->mMouseY); + } + + void mouseDragged(MouseEvent &event) override final + { + if (event.getButton() != MouseButton::LEFT) + return; + setSelected(std::max(0, getSelectionByEvent(event))); + + if (dragDrop.isEmpty()) + { + if (mSkillClicked) + { + mSkillClicked = false; + const SkillInfo *const skill = getSkillByEvent(event); + if (!skill) + return; + dragDrop.dragSkill(skill, DragDropSource::Skills); + dragDrop.setItem(skill->id + SKILL_MIN_ID); + dragDrop.setItemData(strprintf("%d %d %d", + CAST_S32(skill->customCastType), + skill->customOffsetX, + skill->customOffsetY)); + } + } + } + + void mousePressed(MouseEvent &event) override final + { + const MouseButtonT button = event.getButton(); + if (button == MouseButton::LEFT || + button == MouseButton::RIGHT) + { + const SkillInfo *const skill = getSkillByEvent(event); + if (!skill) + return; + event.consume(); + mSkillClicked = true; + SkillModel *const model = mModel; + if (model && + mSelected >= 0 && + model->getSkillAt(mSelected) == skill) + { + skillPopup->hide(); + if (button == MouseButton::LEFT && + event.getX() > + getWidth() - mPadding - skill->skillLevelWidth) + { + popupMenu->showSkillLevelPopup(skill); + } + else if (button == MouseButton::RIGHT) + { + popupMenu->showSkillPopup(skill); + } + } + } + } + + void mouseReleased(MouseEvent &event) override final + { + if (event.getButton() == MouseButton::LEFT) + { + setSelected(std::max(0, getSelectionByEvent(event))); + distributeActionEvent(); + } + } + + void mouseExited(MouseEvent &event A_UNUSED) override final + { + skillPopup->hide(); + } + + void updateAlpha() + { + const float alpha = std::max(settings.guiAlpha, + theme->getMinimumOpacity()); + + if (mAlpha != alpha) + mAlpha = alpha; + } + + void setSelected(const int selected) + { + if (!mModel) + { + mSelected = -1; + } + else + { + if (selected < 0) + mSelected = -1; + else if (selected >= mModel->getNumberOfElements()) + mSelected = mModel->getNumberOfElements() - 1; + else + mSelected = selected; + } + } + + private: + Color mHighlightColor; + Color mTextColor; + Color mTextColor2; + Color mCooldownColor; + Color mForegroundSelectedColor; + Color mForegroundSelectedColor2; + SkillModel *mModel; + Skin *mSkin; + int mSelected; + int mPadding; + int mBoxWidth; + int mBoxHeight; + int mIconXOffset; + int mIconYOffset; + int mTextXOffset; + int mTextYOffset; + bool mSkillClicked; + static float mAlpha; +}; + +float SkillRectangleListBox::mAlpha = 1.0; + +#endif // GUI_WIDGETS_SKILLRECTANGLELISTBOX_H diff --git a/src/gui/widgets/tabs/skilltab.h b/src/gui/widgets/tabs/skilltab.h index 8aa8a2bf0..99f8491bb 100644 --- a/src/gui/widgets/tabs/skilltab.h +++ b/src/gui/widgets/tabs/skilltab.h @@ -26,6 +26,7 @@ #include "gui/windows/skilldialog.h" #include "gui/widgets/skilllistbox.h" +#include "gui/widgets/skillrectanglelistbox.h" #include "gui/widgets/tabs/tab.h" @@ -38,7 +39,18 @@ class SkillTab final : public Tab const std::string &name, SkillListBox *const listBox) : Tab(widget), - mListBox(listBox) + mListBox(listBox), + mRectangleListBox(nullptr) + { + setCaption(name); + } + + SkillTab(const Widget2 *const widget, + const std::string &name, + SkillRectangleListBox *const listBox) : + Tab(widget), + mListBox(nullptr), + mRectangleListBox(listBox) { setCaption(name); } @@ -48,12 +60,15 @@ class SkillTab final : public Tab ~SkillTab() { delete2(mListBox) + delete2(mRectangleListBox) } SkillInfo *getSelectedInfo() const { if (mListBox) return mListBox->getSelectedInfo(); + else if (mRectangleListBox) + return mRectangleListBox->getSelectedInfo(); else return nullptr; } @@ -67,6 +82,7 @@ class SkillTab final : public Tab private: SkillListBox *mListBox; + SkillRectangleListBox *mRectangleListBox; }; #endif // GUI_WIDGETS_TABS_SKILLTAB_H diff --git a/src/gui/windows/skilldialog.cpp b/src/gui/windows/skilldialog.cpp index cfcf60b1b..f1c81e0bb 100644 --- a/src/gui/windows/skilldialog.cpp +++ b/src/gui/windows/skilldialog.cpp @@ -322,11 +322,16 @@ void SkillDialog::loadXmlFile(const std::string &fileName, const std::string setTypeStr = XML::getProperty(set, "type", ""); SkillSetTypeT setType = SkillSetType::VerticalList; - if (setTypeStr == "list" || + if (setTypeStr == "" || + setTypeStr == "list" || setTypeStr == "vertical") { setType = SkillSetType::VerticalList; } + else if (setTypeStr == "rectangle") + { + setType = SkillSetType::Rectangle; + } SkillModel *const model = new SkillModel; if (!mDefaultModel) @@ -370,6 +375,22 @@ void SkillDialog::loadXmlFile(const std::string &fileName, break; } case SkillSetType::Rectangle: + { + SkillRectangleListBox *const listbox = + new SkillRectangleListBox(this, + model); + listbox->setActionEventId("sel"); + listbox->addActionListener(this); + ScrollArea *const scroll = new ScrollArea(this, + listbox, + Opaque_false); + scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER); + scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS); + SkillTab *const tab = new SkillTab(this, setName, listbox); + mDeleteTabs.push_back(tab); + mTabs->addTab(tab, scroll); + break; + } default: reportAlways("Unsupported skillset type: %s", setTypeStr.c_str()); @@ -422,6 +443,10 @@ SkillInfo *SkillDialog::loadSkill(XmlNodePtr node, "castingWaterAction", SpriteAction::CASTWATER); skill->useTextParameter = XML::getBoolProperty( node, "useTextParameter", false); + skill->x = XML::getProperty(node, + "x", 0); + skill->y = XML::getProperty(node, + "y", 0); skill->visible = skill->alwaysVisible; model->addSkill(skill); mSkills[id] = skill; diff --git a/src/resources/skill/skillinfo.cpp b/src/resources/skill/skillinfo.cpp index 963ef3417..b4dcc6600 100644 --- a/src/resources/skill/skillinfo.cpp +++ b/src/resources/skill/skillinfo.cpp @@ -56,6 +56,8 @@ SkillInfo::SkillInfo() : duration(0), durationTime(0), cooldown(0), + x(0), + y(0), type(SkillType::Unknown), owner(SkillOwner::Player), customCastType(CastType::Default), diff --git a/src/resources/skill/skillinfo.h b/src/resources/skill/skillinfo.h index fbf0b2715..f9712f9d8 100644 --- a/src/resources/skill/skillinfo.h +++ b/src/resources/skill/skillinfo.h @@ -68,6 +68,8 @@ struct SkillInfo final int duration; int durationTime; int cooldown; + int x; + int y; SkillType::SkillType type; SkillOwner::Type owner; CastTypeT customCastType; -- cgit v1.2.3-60-g2f50