diff options
Diffstat (limited to 'src/gui')
176 files changed, 3075 insertions, 4107 deletions
diff --git a/src/gui/specialswindow.cpp b/src/gui/abilitieswindow.cpp index 340a1e41..700fa7ff 100644 --- a/src/gui/specialswindow.cpp +++ b/src/gui/abilitieswindow.cpp @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "gui/specialswindow.h" +#include "gui/abilitieswindow.h" #include "log.h" @@ -32,9 +32,9 @@ #include "gui/widgets/windowcontainer.h" #include "net/net.h" -#include "net/specialhandler.h" +#include "net/abilityhandler.h" -#include "resources/specialdb.h" +#include "resources/abilitydb.h" #include "resources/theme.h" #include "utils/dtor.h" @@ -44,19 +44,19 @@ #include <string> -#define SPECIALS_WIDTH 200 -#define SPECIALS_HEIGHT 32 +#define ABILITIES_WIDTH 200 +#define ABILITIES_HEIGHT 32 -class SpecialEntry : public Container +class AbilityEntry : public Container { public: - SpecialEntry(SpecialInfo *info); + AbilityEntry(AbilityInfo *info); void update(int current, int needed); protected: - friend class SpecialsWindow; - SpecialInfo *mInfo; + friend class AbilitiesWindow; + AbilityInfo *mInfo; private: Icon *mIcon = nullptr; // icon to display @@ -65,46 +65,46 @@ class SpecialEntry : public Container ProgressBar *mRechargeBar = nullptr; // recharge bar (only shown when applicable) }; -SpecialsWindow::SpecialsWindow(): - Window(_("Specials")) +AbilitiesWindow::AbilitiesWindow(): + Window(_("Abilities")) { - setWindowName("Specials"); + setWindowName("Abilities"); setCloseButton(true); setResizable(true); setSaveVisible(true); - setDefaultSize(windowContainer->getWidth() - 280, 40, SPECIALS_WIDTH + 20, 225); + setDefaultSize(windowContainer->getWidth() - 280, 40, ABILITIES_WIDTH + 20, 225); setupWindow->registerWindowForReset(this); center(); loadWindowState(); } -SpecialsWindow::~SpecialsWindow() +AbilitiesWindow::~AbilitiesWindow() { // Clear gui } -void SpecialsWindow::action(const gcn::ActionEvent &event) +void AbilitiesWindow::action(const gcn::ActionEvent &event) { if (event.getId() == "use") { - auto *disp = dynamic_cast<SpecialEntry*>(event.getSource()->getParent()); + auto *disp = dynamic_cast<AbilityEntry*>(event.getSource()->getParent()); if (disp) { - if (disp->mInfo->targetMode == SpecialInfo::TARGET_BEING) + if (disp->mInfo->targetMode == AbilityInfo::TARGET_BEING) { Being *target = local_player->getTarget(); if (target) - Net::getSpecialHandler()->use(disp->mInfo->id, 1, target->getId()); + Net::getAbilityHandler()->useOn(disp->mInfo->id, target->getId()); else - Net::getSpecialHandler()->use(disp->mInfo->id); + Net::getAbilityHandler()->use(disp->mInfo->id); } else { // TODO: Allow the player to aim at a position on the map and - // Use special on the map position. + // Use abilities on the map position. } } } @@ -114,73 +114,73 @@ void SpecialsWindow::action(const gcn::ActionEvent &event) } } -void SpecialsWindow::draw(gcn::Graphics *graphics) +void AbilitiesWindow::draw(gcn::Graphics *graphics) { // update the progress bars - std::map<int, Special> specialData = PlayerInfo::getSpecialStatus(); + const std::map<int, Ability> &abilityData = PlayerInfo::getAbilityStatus(); bool foundNew = false; - unsigned int found = 0; // number of entries in specialData which match mEntries + unsigned int found = 0; // number of entries in abilityData which match mEntries - for (auto &special : specialData) + for (auto &[id, ability] : abilityData) { - auto e = mEntries.find(special.first); + auto e = mEntries.find(id); if (e == mEntries.end()) { - // found a new special - abort update and rebuild from scratch + // found a new ability - abort update and rebuild from scratch foundNew = true; break; } - // update progress bar of special - e->second->update(special.second.currentMana, special.second.neededMana); + // update progress bar of ability + e->second->update(ability.currentMana, ability.neededMana); found++; } - // a rebuild is needed when a) the number of specials changed or b) an existing entry isn't found anymore + // a rebuild is needed when a) the number of abilities changed or b) an existing entry isn't found anymore if (foundNew || found != mEntries.size()) - rebuild(specialData); + rebuild(abilityData); Window::draw(graphics); } -void SpecialsWindow::rebuild(const std::map<int, Special> &specialData) +void AbilitiesWindow::rebuild(const std::map<int, Ability> &abilityData) { delete_all(mEntries); mEntries.clear(); int vPos = 0; //vertical position of next placed element - for (auto special : specialData) + for (auto &[id, ability] : abilityData) { - logger->log("Updating special GUI for %d", special.first); + logger->log("Updating ability GUI for %d", id); - SpecialInfo *info = SpecialDB::get(special.first); + AbilityInfo *info = AbilityDB::get(id); if (info) { - info->rechargeCurrent = special.second.currentMana; - info->rechargeNeeded = special.second.neededMana; - auto* entry = new SpecialEntry(info); + info->rechargeCurrent = ability.currentMana; + info->rechargeNeeded = ability.neededMana; + auto *entry = new AbilityEntry(info); entry->setPosition(0, vPos); vPos += entry->getHeight() + 3; add(entry); - mEntries[special.first] = entry; + mEntries[id] = entry; } else { - logger->log("Warning: No info available of special %d", special.first); + logger->log("Warning: No info available of ability %d", id); } } } -SpecialEntry::SpecialEntry(SpecialInfo *info) : +AbilityEntry::AbilityEntry(AbilityInfo *info) : mInfo(info) { - setSize(SPECIALS_WIDTH, SPECIALS_HEIGHT); + setSize(ABILITIES_WIDTH, ABILITIES_HEIGHT); if (!info->icon.empty()) mIcon = new Icon(info->icon); else - mIcon = new Icon(Theme::resolveThemePath("unknown-item.png")); + mIcon = new Icon(Theme::getImageFromTheme("unknown-item.png")); mIcon->setPosition(1, 0); add(mIcon); @@ -189,7 +189,7 @@ SpecialEntry::SpecialEntry(SpecialInfo *info) : mNameLabel->setPosition(35, 0); add(mNameLabel); - mUse = new Button("Use", "use", specialsWindow); + mUse = new Button("Use", "use", abilitiesWindow); mUse->setPosition(getWidth() - mUse->getWidth(), 5); add(mUse); @@ -203,7 +203,7 @@ SpecialEntry::SpecialEntry(SpecialInfo *info) : } } -void SpecialEntry::update(int current, int needed) +void AbilityEntry::update(int current, int needed) { if (mRechargeBar) { diff --git a/src/gui/specialswindow.h b/src/gui/abilitieswindow.h index 4ed4db3e..ddc5a9e4 100644 --- a/src/gui/specialswindow.h +++ b/src/gui/abilitieswindow.h @@ -18,8 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SPECIALSWINDOW_H -#define SPECIALSWINDOW_H +#pragma once #include "playerinfo.h" @@ -29,14 +28,14 @@ #include <map> -class SpecialEntry; +class AbilityEntry; -class SpecialsWindow : public Window, public gcn::ActionListener +class AbilitiesWindow : public Window, public gcn::ActionListener { public: - SpecialsWindow(); + AbilitiesWindow(); - ~SpecialsWindow() override; + ~AbilitiesWindow() override; /** * Called when receiving actions from widget. @@ -45,16 +44,14 @@ class SpecialsWindow : public Window, public gcn::ActionListener void draw(gcn::Graphics *graphics) override; - bool hasSpecials() const + bool hasAbilities() const { return !mEntries.empty(); } private: - // (re)constructs the list of specials - void rebuild(const std::map<int, Special> &specialData); + // (re)constructs the list of abilities + void rebuild(const std::map<int, Ability> &abilityData); - std::map<int, SpecialEntry *> mEntries; + std::map<int, AbilityEntry *> mEntries; }; -extern SpecialsWindow *specialsWindow; - -#endif // SPECIALSWINDOW_H +extern AbilitiesWindow *abilitiesWindow; diff --git a/src/gui/beingpopup.h b/src/gui/beingpopup.h index 45169464..377b1b4e 100644 --- a/src/gui/beingpopup.h +++ b/src/gui/beingpopup.h @@ -18,8 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef BEINGPOPUP_H -#define BEINGPOPUP_H +#pragma once #include "gui/widgets/popup.h" @@ -47,5 +46,3 @@ class BeingPopup : public Popup Label *mBeingName; Label *mBeingParty; }; - -#endif // BEINGPOPUP_H diff --git a/src/gui/buydialog.h b/src/gui/buydialog.h index b1126225..4c67ef0d 100644 --- a/src/gui/buydialog.h +++ b/src/gui/buydialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef BUY_H -#define BUY_H +#pragma once #include "guichanfwd.h" @@ -124,5 +123,3 @@ class BuyDialog : public Window, public gcn::ActionListener, int mAmountItems; int mMaxItems; }; - -#endif diff --git a/src/gui/buyselldialog.h b/src/gui/buyselldialog.h index 2e324d42..be382ffa 100644 --- a/src/gui/buyselldialog.h +++ b/src/gui/buyselldialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef BUYSELL_H -#define BUYSELL_H +#pragma once #include "gui/widgets/window.h" @@ -63,5 +62,3 @@ class BuySellDialog : public Window, public gcn::ActionListener int mNpcId; gcn::Button *mBuyButton; }; - -#endif diff --git a/src/gui/changeemaildialog.h b/src/gui/changeemaildialog.h index 37d3ce01..cdf5664c 100644 --- a/src/gui/changeemaildialog.h +++ b/src/gui/changeemaildialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_CHANGEEMAIL_H -#define GUI_CHANGEEMAIL_H +#pragma once #include "gui/widgets/window.h" @@ -64,5 +63,3 @@ class ChangeEmailDialog : public Window, public gcn::ActionListener LoginData *mLoginData; }; - -#endif // GUI_CHANGEEMAIL_H diff --git a/src/gui/changepassworddialog.h b/src/gui/changepassworddialog.h index 22d449d1..541e0444 100644 --- a/src/gui/changepassworddialog.h +++ b/src/gui/changepassworddialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CHANGEPASSWORDDIALOG_H -#define CHANGEPASSWORDDIALOG_H +#pragma once #include "gui/widgets/window.h" @@ -59,5 +58,3 @@ class ChangePasswordDialog : public Window, public gcn::ActionListener LoginData *mLoginData; }; - -#endif diff --git a/src/gui/charcreatedialog.cpp b/src/gui/charcreatedialog.cpp index fa1144b6..cff7d822 100644 --- a/src/gui/charcreatedialog.cpp +++ b/src/gui/charcreatedialog.cpp @@ -50,7 +50,7 @@ CharCreateDialog::CharCreateDialog(CharSelectDialog *parent, int slot): mSlot(slot) { mPlayer = new Being(0, ActorSprite::PLAYER, 0, nullptr); - mPlayer->setGender(Gender::MALE); + mPlayer->setGender(Gender::Male); const std::vector<int> &items = CharDB::getDefaultItems(); for (size_t i = 0; i < items.size(); ++i) @@ -70,18 +70,6 @@ CharCreateDialog::CharCreateDialog(CharSelectDialog *parent, int slot): mNameField = new TextField(std::string()); mNameLabel = new Label(_("Name:")); - mNextHairColorButton = new Button("", "nextcolor", this); - mPrevHairColorButton = new Button("", "prevcolor", this); - mPrevHairColorButton->setButtonIcon("tab_arrows_left.png"); - mNextHairColorButton->setButtonIcon("tab_arrows_right.png"); - - mHairColorLabel = new Label(_("Hair color:")); - mNextHairStyleButton = new Button("", "nextstyle", this); - mPrevHairStyleButton = new Button("", "prevstyle", this); - mPrevHairStyleButton->setButtonIcon("tab_arrows_left.png"); - mNextHairStyleButton->setButtonIcon("tab_arrows_right.png"); - - mHairStyleLabel = new Label(_("Hair style:")); mCreateButton = new Button(_("Create"), "create", this); mCancelButton = new Button(_("Cancel"), "cancel", this); mMale = new RadioButton(_("Male"), "gender"); @@ -112,12 +100,6 @@ CharCreateDialog::CharCreateDialog(CharSelectDialog *parent, int slot): mNameLabel->setPosition(5, 5); mNameField->setDimension( gcn::Rectangle(45, 5, w - 45 - 7, mNameField->getHeight())); - mPrevHairColorButton->setPosition(90, 35); - mNextHairColorButton->setPosition(165, 35); - mHairColorLabel->setPosition(5, 40); - mPrevHairStyleButton->setPosition(90, 64); - mNextHairStyleButton->setPosition(165, 64); - mHairStyleLabel->setPosition(5, 70); mAttributesLeft->setPosition(15, 280); updateSliders(); mCancelButton->setPosition( @@ -136,16 +118,36 @@ CharCreateDialog::CharCreateDialog(CharSelectDialog *parent, int slot): if (mHairColorsIds.size() > 1) { - add(mNextHairColorButton); - add(mPrevHairColorButton); - add(mHairColorLabel); + auto hairColorLabel = new Label(_("Hair color:")); + auto nextHairColorButton = new Button("", "nextcolor", this); + auto prevHairColorButton = new Button("", "prevcolor", this); + prevHairColorButton->setButtonIcon("tab_arrows_left.png"); + nextHairColorButton->setButtonIcon("tab_arrows_right.png"); + + hairColorLabel->setPosition(5, 40); + prevHairColorButton->setPosition(90, 35); + nextHairColorButton->setPosition(165, 35); + + add(nextHairColorButton); + add(prevHairColorButton); + add(hairColorLabel); } if (mHairStylesIds.size() > 1) { - add(mNextHairStyleButton); - add(mPrevHairStyleButton); - add(mHairStyleLabel); + auto hairStyleLabel = new Label(_("Hair style:")); + auto nextHairStyleButton = new Button("", "nextstyle", this); + auto prevHairStyleButton = new Button("", "prevstyle", this); + prevHairStyleButton->setButtonIcon("tab_arrows_left.png"); + nextHairStyleButton->setButtonIcon("tab_arrows_right.png"); + + hairStyleLabel->setPosition(5, 70); + prevHairStyleButton->setPosition(90, 64); + nextHairStyleButton->setPosition(165, 64); + + add(nextHairStyleButton); + add(prevHairStyleButton); + add(hairStyleLabel); } add(mAttributesLeft); @@ -165,14 +167,15 @@ CharCreateDialog::~CharCreateDialog() delete mPlayer; // Make sure the char server handler knows that we're gone - Net::getCharHandler()->setCharCreateDialog(nullptr); + if (auto charHandler = Net::getCharHandler()) + charHandler->setCharCreateDialog(nullptr); } void CharCreateDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "create") { - if (Net::getNetworkType() == ServerType::MANASERV + if (Net::getNetworkType() == ServerType::ManaServ || getName().length() >= 4) { // Attempt to create the character @@ -180,7 +183,7 @@ void CharCreateDialog::action(const gcn::ActionEvent &event) int characterSlot = mSlot; // On Manaserv, the slots start at 1, so we offset them. - if (Net::getNetworkType() == ServerType::MANASERV) + if (Net::getNetworkType() == ServerType::ManaServ) ++characterSlot; // Should avoid the most common crash case @@ -231,9 +234,9 @@ void CharCreateDialog::action(const gcn::ActionEvent &event) else if (event.getId() == "gender") { if (mMale->isSelected()) - mPlayer->setGender(Gender::MALE); + mPlayer->setGender(Gender::Male); else - mPlayer->setGender(Gender::FEMALE); + mPlayer->setGender(Gender::Female); } } @@ -341,7 +344,7 @@ void CharCreateDialog::setAttributes(const std::vector<std::string> &labels, add(attributeLabel); auto *attributeSlider = new Slider(min, max); - attributeSlider->setDimension(gcn::Rectangle(75, y, 100, 10)); + attributeSlider->setDimension(gcn::Rectangle(75, y, 100, attributeSlider->getHeight())); attributeSlider->setActionEventId("statslider"); attributeSlider->addActionListener(this); add(attributeSlider); @@ -373,7 +376,7 @@ void CharCreateDialog::setAttributes(const std::vector<std::string> &labels, void CharCreateDialog::setDefaultGender(Gender gender) { - if (gender == Gender::FEMALE) + if (gender == Gender::Female) { mFemale->setSelected(true); mMale->setSelected(false); diff --git a/src/gui/charcreatedialog.h b/src/gui/charcreatedialog.h index 46db6229..f1759650 100644 --- a/src/gui/charcreatedialog.h +++ b/src/gui/charcreatedialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CHAR_CREATE_DIALOG_H -#define CHAR_CREATE_DIALOG_H +#pragma once #include "being.h" #include "guichanfwd.h" @@ -82,12 +81,6 @@ class CharCreateDialog : public Window, public gcn::ActionListener gcn::TextField *mNameField; gcn::Label *mNameLabel; - Button *mNextHairColorButton; - Button *mPrevHairColorButton; - gcn::Label *mHairColorLabel; - Button *mNextHairStyleButton; - Button *mPrevHairStyleButton; - gcn::Label *mHairStyleLabel; gcn::RadioButton *mMale; gcn::RadioButton *mFemale; @@ -114,5 +107,3 @@ class CharCreateDialog : public Window, public gcn::ActionListener int mSlot; }; - -#endif // CHAR_CREATE_DIALOG_H diff --git a/src/gui/charselectdialog.cpp b/src/gui/charselectdialog.cpp index 7792813c..2485be69 100644 --- a/src/gui/charselectdialog.cpp +++ b/src/gui/charselectdialog.cpp @@ -150,7 +150,7 @@ CharSelectDialog::CharSelectDialog(LoginData *loginData): for (int i = 0; i < (int)mLoginData->characterSlots; i++) { mCharacterEntries.push_back(new CharacterDisplay(this)); - place(i % SLOTS_PER_ROW, (int)i / SLOTS_PER_ROW, mCharacterEntries[i]); + place(i % SLOTS_PER_ROW, i / SLOTS_PER_ROW, mCharacterEntries[i]); } reflowLayout(); @@ -266,7 +266,7 @@ void CharSelectDialog::setCharacters(const Net::Characters &characters) { // Slots Number start at 1 for Manaserv, so we offset them by one. int characterSlot = character->slot; - if (Net::getNetworkType() == ServerType::MANASERV && characterSlot > 0) + if (Net::getNetworkType() == ServerType::ManaServ && characterSlot > 0) --characterSlot; if (characterSlot >= (int)mCharacterEntries.size()) diff --git a/src/gui/charselectdialog.h b/src/gui/charselectdialog.h index 2deb5a7d..e5e558c9 100644 --- a/src/gui/charselectdialog.h +++ b/src/gui/charselectdialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CHAR_SELECT_H -#define CHAR_SELECT_H +#pragma once #include "gui/widgets/window.h" @@ -100,5 +99,3 @@ class CharSelectDialog : public Window, public gcn::ActionListener, Net::CharHandler *mCharHandler; }; - -#endif diff --git a/src/gui/chatwindow.cpp b/src/gui/chatwindow.cpp index 453baea2..d006097c 100644 --- a/src/gui/chatwindow.cpp +++ b/src/gui/chatwindow.cpp @@ -30,6 +30,7 @@ #include "gui/recorder.h" #include "gui/setup.h" +#include "gui/widgets/browserbox.h" #include "gui/widgets/chattab.h" #include "gui/widgets/itemlinkhandler.h" #include "gui/widgets/layout.h" @@ -76,18 +77,17 @@ class ChatAutoComplete : public AutoCompleteLister { void getAutoCompleteList(std::vector<std::string> &list) const override { - auto *tab = static_cast<ChatTab*>(chatWindow->mChatTabs - ->getSelectedTab()); - - return tab->getAutoCompleteList(list); + auto tab = static_cast<ChatTab *>(chatWindow->mChatTabs->getSelectedTab()); + tab->getAutoCompleteList(list); } }; ChatWindow::ChatWindow(): - Window(_("Chat")), - mHistory(new TextHistory()), + Window(SkinType::Popup, _("Chat")), + mItemLinkHandler(new ItemLinkHandler(this)), + mChatInput(new ChatInput), mAutoComplete(new ChatAutoComplete), - mTmpVisible(false) + mChatTabs(new TabbedArea) { listen(Event::ChatChannel); listen(Event::NoticesChannel); @@ -106,21 +106,20 @@ ChatWindow::ChatWindow(): setMinWidth(150); setMinHeight(90); - mItemLinkHandler = new ItemLinkHandler(this); - - mChatInput = new ChatInput; mChatInput->setActionEventId("chatinput"); mChatInput->addActionListener(this); - mChatTabs = new TabbedArea; + // Override the padding from the theme since we want the content very close + // to the border on this window. + setPadding(std::min<unsigned>(getPadding(), 6)); + getLayout().setPadding(0); - getLayout().setPadding(3); place(0, 0, mChatTabs, 3, 3); - place(0, 3, mChatInput, 3); + place(0, 3, mChatInput, 3).setPadding(mChatInput->getFrameSize()); loadWindowState(); - mChatInput->setHistory(mHistory); + mChatInput->setHistory(&mHistory); mChatInput->setAutoComplete(mAutoComplete); mRecorder = new Recorder(this); @@ -131,7 +130,6 @@ ChatWindow::~ChatWindow() delete mRecorder; removeAllWhispers(); delete mItemLinkHandler; - delete mHistory; delete mAutoComplete; } @@ -146,15 +144,10 @@ ChatTab *ChatWindow::getFocused() const return static_cast<ChatTab*>(mChatTabs->getSelectedTab()); } -void ChatWindow::clearTab(ChatTab *tab) -{ - if (tab) - tab->clearText(); -} - void ChatWindow::clearTab() { - clearTab(getFocused()); + if (auto tab = getFocused()) + tab->clearText(); } void ChatWindow::prevTab() @@ -247,6 +240,7 @@ void ChatWindow::addTab(ChatTab *tab) // Make sure we don't end up with duplicates in the gui // TODO + tab->mTextOutput->setPalette(getSkin().palette); mChatTabs->addTab(tab, tab->mScrollArea); // Update UI diff --git a/src/gui/chatwindow.h b/src/gui/chatwindow.h index 59ab6d99..6cac6f8e 100644 --- a/src/gui/chatwindow.h +++ b/src/gui/chatwindow.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CHAT_H -#define CHAT_H +#pragma once #include "eventlistener.h" @@ -32,21 +31,14 @@ #include <guichan/widget.hpp> #include <guichan/widgetlistener.hpp> -#include <list> #include <string> #include <map> -#include <vector> -class BrowserBox; class ChatTab; -class Channel; class ChatInput; class Recorder; -class ScrollArea; class TabbedArea; class ItemLinkHandler; -class Tab; -class WhisperTab; #define DEFAULT_CHAT_WINDOW_SCROLL 7 // 1 means `1/8th of the window size'. @@ -99,11 +91,6 @@ class ChatWindow : public Window, ChatTab *getFocused() const; /** - * Clear the given tab. - */ - void clearTab(ChatTab *tab); - - /** * Clear the current tab. */ void clearTab(); @@ -202,11 +189,11 @@ class ChatWindow : public Window, /** Input box for typing chat messages. */ ChatInput *mChatInput; - TextHistory *mHistory; + TextHistory mHistory; AutoCompleteLister *mAutoComplete; private: - bool mTmpVisible; + bool mTmpVisible = false; /** Tabbed area for holding each channel. */ TabbedArea *mChatTabs; @@ -216,5 +203,3 @@ class ChatWindow : public Window, }; extern ChatWindow *chatWindow; - -#endif diff --git a/src/gui/confirmdialog.h b/src/gui/confirmdialog.h index 8eb3f46b..ba51558e 100644 --- a/src/gui/confirmdialog.h +++ b/src/gui/confirmdialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef OPTION_DIALOG_H -#define OPTION_DIALOG_H +#pragma once #include "gui/widgets/window.h" @@ -47,5 +46,3 @@ class ConfirmDialog : public Window, public gcn::ActionListener private: TextBox *mTextBox; }; - -#endif diff --git a/src/gui/connectiondialog.h b/src/gui/connectiondialog.h index e76a0a3e..6e477435 100644 --- a/src/gui/connectiondialog.h +++ b/src/gui/connectiondialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CONNECTION_H -#define CONNECTION_H +#pragma once #include "client.h" @@ -58,5 +57,3 @@ class ConnectionDialog : public Window, gcn::ActionListener gcn::Label *mLabel; State mCancelState; }; - -#endif diff --git a/src/gui/customserverdialog.cpp b/src/gui/customserverdialog.cpp index c7c03b1a..9a589671 100644 --- a/src/gui/customserverdialog.cpp +++ b/src/gui/customserverdialog.cpp @@ -37,11 +37,10 @@ std::string TypeListModel::getElementAt(int elementIndex) if (elementIndex == 0) return "TmwAthena"; #ifdef MANASERV_SUPPORT - else if (elementIndex == 1) + if (elementIndex == 1) return "ManaServ"; #endif - else - return "Unknown"; + return "Unknown"; } CustomServerDialog::CustomServerDialog(ServerDialog *parent, int index): @@ -62,8 +61,8 @@ CustomServerDialog::CustomServerDialog(ServerDialog *parent, int index): mPortField = new TextField(std::string()); #ifdef MANASERV_SUPPORT - mTypeListModel = new TypeListModel(); - mTypeField = new DropDown(mTypeListModel); + mTypeListModel = std::make_unique<TypeListModel>(); + mTypeField = new DropDown(mTypeListModel.get()); mTypeField->setSelected(0); // TmwAthena by default for now. #endif @@ -77,46 +76,25 @@ CustomServerDialog::CustomServerDialog(ServerDialog *parent, int index): mPortField->addActionListener(this); place(0, 0, nameLabel); - place(1, 0, mNameField, 4).setPadding(3); + place(1, 0, mNameField, 4).setPadding(2); place(0, 1, serverAdressLabel); - place(1, 1, mServerAddressField, 4).setPadding(3); + place(1, 1, mServerAddressField, 4).setPadding(2); place(0, 2, portLabel); - place(1, 2, mPortField, 4).setPadding(3); + place(1, 2, mPortField, 4).setPadding(2); #ifdef MANASERV_SUPPORT place(0, 3, typeLabel); - place(1, 3, mTypeField).setPadding(3); + place(1, 3, mTypeField).setPadding(2); #endif place(0, 4, descriptionLabel); - place(1, 4, mDescriptionField, 4).setPadding(3); + place(1, 4, mDescriptionField, 4).setPadding(2); place(4, 5, mOkButton); place(3, 5, mCancelButton); - // Do this manually instead of calling reflowLayout so we can enforce a - // minimum width. - int width = 0, height = 0; - getLayout().reflow(width, height); - if (width < 300) - { - width = 300; - getLayout().reflow(width, height); - } - if (height < 120) - { - height = 120; - getLayout().reflow(width, height); - } - - setContentSize(width, height); - - setMinWidth(getWidth()); - setMinHeight(getHeight()); - setDefaultSize(getWidth(), getHeight(), ImageRect::CENTER); + reflowLayout(); + setLocationRelativeTo(getParentWindow()); - setResizable(false); addKeyListener(this); - loadWindowState(); - // Add the entry's info when in modify mode. if (index > -1) { @@ -126,28 +104,17 @@ CustomServerDialog::CustomServerDialog(ServerDialog *parent, int index): mServerAddressField->setText(serverInfo.hostname); mPortField->setText(toString(serverInfo.port)); #ifdef MANASERV_SUPPORT - mTypeField->setSelected(serverInfo.type == ServerType::TMWATHENA ? + mTypeField->setSelected(serverInfo.type == ServerType::TmwAthena ? 0 : 1); #endif } - setLocationRelativeTo(getParentWindow()); setVisible(true); mNameField->requestFocus(); } -CustomServerDialog::~CustomServerDialog() -{ -#ifdef MANASERV_SUPPORT - delete mTypeListModel; -#endif -} - -void CustomServerDialog::logic() -{ - Window::logic(); -} +CustomServerDialog::~CustomServerDialog() = default; void CustomServerDialog::action(const gcn::ActionEvent &event) { @@ -178,16 +145,16 @@ void CustomServerDialog::action(const gcn::ActionEvent &event) switch (mTypeField->getSelected()) { case 0: - serverInfo.type = ServerType::TMWATHENA; + serverInfo.type = ServerType::TmwAthena; break; case 1: - serverInfo.type = ServerType::MANASERV; + serverInfo.type = ServerType::ManaServ; break; default: - serverInfo.type = ServerType::UNKNOWN; + serverInfo.type = ServerType::Unknown; } #else - serverInfo.type = ServerType::TMWATHENA; + serverInfo.type = ServerType::TmwAthena; #endif if (mPortField->getText().empty()) serverInfo.port = ServerInfo::defaultPortForServerType(serverInfo.type); diff --git a/src/gui/customserverdialog.h b/src/gui/customserverdialog.h index 8b0af4c8..e523260c 100644 --- a/src/gui/customserverdialog.h +++ b/src/gui/customserverdialog.h @@ -18,15 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CUSTOMSERVERDIALOG_H -#define CUSTOMSERVERDIALOG_H - -class Button; -class Label; -class TextField; -class DropDown; -class ServerDialog; -class TypeListModel; +#pragma once #include "gui/widgets/window.h" @@ -34,6 +26,12 @@ class TypeListModel; #include <guichan/keylistener.hpp> #include <guichan/listmodel.hpp> +#include <memory> + +class Button; +class DropDown; +class ServerDialog; +class TextField; /** * Server Type List Model @@ -65,7 +63,6 @@ class CustomServerDialog : public Window, { public: CustomServerDialog(ServerDialog *parent, int index = -1); - ~CustomServerDialog() override; /** @@ -75,22 +72,18 @@ class CustomServerDialog : public Window, void keyPressed(gcn::KeyEvent &keyEvent) override; - void logic() override; - private: TextField *mServerAddressField; TextField *mPortField; - TextField *mNameField; + TextField *mNameField; TextField *mDescriptionField; Button *mOkButton; Button *mCancelButton; #ifdef MANASERV_SUPPORT DropDown *mTypeField; - TypeListModel *mTypeListModel; + std::unique_ptr<TypeListModel> mTypeListModel; #endif ServerDialog *mServerDialog; // The index of the entry to modify, -1 when only adding a new entry. int mIndex; }; - -#endif // CUSTOMSERVERDIALOG_H diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp index 3c514191..17020062 100644 --- a/src/gui/debugwindow.cpp +++ b/src/gui/debugwindow.cpp @@ -244,11 +244,15 @@ DebugWindow::DebugWindow() place(0, 0, tabs, 2, 2); loadWindowState(); - Tab *tabInfo = new Tab; - tabInfo->setCaption(_("Info")); - tabs->addTab(tabInfo, new DebugInfo); - - Tab *tabSwitches = new Tab; - tabSwitches->setCaption(_("Switches")); - tabs->addTab(tabSwitches, new DebugSwitches); + mInfoTab = std::make_unique<Tab>(); + mInfoTab->setCaption(_("Info")); + mInfoWidget = std::make_unique<DebugInfo>(); + tabs->addTab(mInfoTab.get(), mInfoWidget.get()); + + mSwitchesTab = std::make_unique<Tab>(); + mSwitchesTab->setCaption(_("Switches")); + mSwitchesWidget = std::make_unique<DebugSwitches>(); + tabs->addTab(mSwitchesTab.get(), mSwitchesWidget.get()); } + +DebugWindow::~DebugWindow() = default; diff --git a/src/gui/debugwindow.h b/src/gui/debugwindow.h index 3376ae18..807f0d0f 100644 --- a/src/gui/debugwindow.h +++ b/src/gui/debugwindow.h @@ -19,11 +19,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef DEBUGWINDOW_H -#define DEBUGWINDOW_H +#pragma once #include "gui/widgets/window.h" +#include <memory> + +class Tab; + /** * The debug window. * @@ -33,8 +36,13 @@ class DebugWindow : public Window { public: DebugWindow(); + ~DebugWindow() override; + + private: + std::unique_ptr<Tab> mInfoTab; + std::unique_ptr<Tab> mSwitchesTab; + std::unique_ptr<gcn::Widget> mInfoWidget; + std::unique_ptr<gcn::Widget> mSwitchesWidget; }; extern DebugWindow *debugWindow; - -#endif diff --git a/src/gui/emotepopup.cpp b/src/gui/emotepopup.cpp index 589d5087..4df6b995 100644 --- a/src/gui/emotepopup.cpp +++ b/src/gui/emotepopup.cpp @@ -22,12 +22,10 @@ #include "gui/emotepopup.h" -#include "configuration.h" #include "emoteshortcut.h" #include "graphics.h" -#include "imagesprite.h" -#include "log.h" +#include "gui/gui.h" #include "resources/emotedb.h" #include "resources/image.h" #include "resources/theme.h" @@ -35,59 +33,61 @@ #include <guichan/mouseinput.hpp> #include <guichan/selectionlistener.hpp> -const int EmotePopup::gridWidth = 34; // emote icon width + 4 -const int EmotePopup::gridHeight = 36; // emote icon height + 4 - static const int MAX_COLUMNS = 6; EmotePopup::EmotePopup() { - mSelectionImage = Theme::getImageFromTheme("selection.png"); - if (!mSelectionImage) - logger->error("Unable to load selection.png"); - - mSelectionImage->setAlpha(config.guiAlpha); - addMouseListener(this); recalculateSize(); setVisible(true); } -EmotePopup::~EmotePopup() -{ - mSelectionImage->decRef(); -} +EmotePopup::~EmotePopup() = default; void EmotePopup::draw(gcn::Graphics *graphics) { + auto *g = static_cast<Graphics*>(graphics); + Popup::draw(graphics); const int emoteCount = EmoteDB::getEmoteCount(); + auto &slotSkin = gui->getTheme()->getSkin(SkinType::EmoteSlot); + WidgetState slotState; + slotState.width = slotSkin.width; + slotState.height = slotSkin.height; + for (int i = 0; i < emoteCount ; i++) { int row = i / mColumnCount; int column = i % mColumnCount; - int emoteX = getPadding() + column * gridWidth; - int emoteY = getPadding() + row * gridHeight; + slotState.x = getPadding() + column * slotSkin.width; + slotState.y = getPadding() + row * slotSkin.height; // Center the last row when there are less emotes than columns if (row == mRowCount - 1) { const int emotesLeft = emoteCount % mColumnCount; - emoteX += (mColumnCount - emotesLeft) * gridWidth / 2; + slotState.x += (mColumnCount - emotesLeft) * slotSkin.width / 2; } + slotState.flags = 0; + // Draw selection image below hovered item if (i == mHoveredEmoteIndex) - { - static_cast<Graphics*>(graphics)->drawImage( - mSelectionImage, emoteX, emoteY + 4); - } + slotState.flags |= STATE_HOVERED; + + slotSkin.draw(g, slotState); // Draw emote icon - EmoteDB::getByIndex(i).sprite->draw(static_cast<Graphics*>(graphics), emoteX, emoteY); + if (auto image = EmoteDB::getByIndex(i).image) + { + image->setAlpha(1.0f); + g->drawImage(image, + slotState.x + (slotSkin.width - image->getWidth()) / 2, + slotState.y + (slotSkin.height - image->getHeight()) / 2); + } } } @@ -140,22 +140,24 @@ int EmotePopup::getIndexAt(int x, int y) const return -1; // Take into account the border - x -= 2; - y -= 4; + x -= getPadding(); + y -= getPadding(); + + auto &slotSkin = gui->getTheme()->getSkin(SkinType::EmoteSlot); - const int row = y / gridHeight; + const int row = y / slotSkin.height; // Take into account that the last row is centered if (row == mRowCount - 1) { const int emotesLeft = EmoteDB::getEmoteCount() % mColumnCount; const int emotesMissing = mColumnCount - emotesLeft; - x -= emotesMissing * gridWidth / 2; + x -= emotesMissing * slotSkin.width / 2; if (x < 0) return -1; } - const int column = std::min(x / gridWidth, mColumnCount - 1); + const int column = std::min(x / slotSkin.width, mColumnCount - 1); const int index = column + (row * mColumnCount); if (index >= 0 && index < EmoteDB::getEmoteCount()) @@ -179,7 +181,8 @@ void EmotePopup::recalculateSize() mColumnCount = 0; } - setContentSize(mColumnCount * gridWidth, mRowCount * gridHeight); + auto &slotSkin = gui->getTheme()->getSkin(SkinType::EmoteSlot); + setContentSize(mColumnCount * slotSkin.width, mRowCount * slotSkin.height); } void EmotePopup::distributeValueChangedEvent() @@ -187,7 +190,5 @@ void EmotePopup::distributeValueChangedEvent() const gcn::SelectionEvent event(this); for (auto &listener : mListeners) - { listener->valueChanged(event); - } } diff --git a/src/gui/emotepopup.h b/src/gui/emotepopup.h index ef3fffed..664eef27 100644 --- a/src/gui/emotepopup.h +++ b/src/gui/emotepopup.h @@ -20,8 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef EMOTEPOPUP_H -#define EMOTEPOPUP_H +#pragma once #include "gui/widgets/popup.h" @@ -104,7 +103,6 @@ class EmotePopup : public Popup */ void distributeValueChangedEvent(); - Image *mSelectionImage; int mSelectedEmoteId = -1; int mHoveredEmoteIndex = -1; @@ -112,9 +110,4 @@ class EmotePopup : public Popup int mColumnCount = 1; std::list<gcn::SelectionListener *> mListeners; - - static const int gridWidth; - static const int gridHeight; }; - -#endif diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index e7eeb048..b9d3b0fd 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -47,9 +47,6 @@ #include <guichan/font.hpp> -static const int BOX_WIDTH = 36; -static const int BOX_HEIGHT = 36; - EquipmentWindow::EquipmentWindow(Equipment *equipment): Window(_("Equipment")), mEquipment(equipment) @@ -65,42 +62,18 @@ EquipmentWindow::EquipmentWindow(Equipment *equipment): setWindowName("Equipment"); setCloseButton(true); setSaveVisible(true); - setDefaultSize(180, 300, ImageRect::CENTER); + setContentSize(175, 290); + setDefaultSize(getWidth(), getHeight(), ImageRect::CENTER); loadWindowState(); mUnequip = new Button(_("Unequip"), "unequip", this); const gcn::Rectangle &area = getChildrenArea(); - mUnequip->setPosition(area.width - mUnequip->getWidth() - 5, - area.height - mUnequip->getHeight() - 5); + mUnequip->setPosition(area.width - mUnequip->getWidth() - getPadding(), + area.height - mUnequip->getHeight() - getPadding()); mUnequip->setEnabled(false); add(playerBox); add(mUnequip); - - loadEquipBoxes(); -} - -void EquipmentWindow::loadEquipBoxes() -{ - mBoxes.resize(mEquipment->getSlotNumber()); - - for (size_t i = 0; i < mBoxes.size(); ++i) - { - auto &box = mBoxes[i]; - - Position boxPosition = Net::getInventoryHandler()->getBoxPosition(i); - box.posX = boxPosition.x + getPadding(); - box.posY = boxPosition.y + getTitleBarHeight(); - - const std::string &backgroundFile = - Net::getInventoryHandler()->getBoxBackground(i); - - if (!backgroundFile.empty()) - { - box.backgroundImage = - Theme::instance()->getImageFromTheme(backgroundFile); - } - } } EquipmentWindow::~EquipmentWindow() @@ -111,60 +84,52 @@ EquipmentWindow::~EquipmentWindow() void EquipmentWindow::draw(gcn::Graphics *graphics) { Window::draw(graphics); - Window::drawChildren(graphics); - // Draw equipment boxes auto *g = static_cast<Graphics*>(graphics); - for (size_t i = 0; i < mBoxes.size(); i++) - { - const auto &box = mBoxes[i]; + auto theme = gui->getTheme(); + auto &boxSkin = theme->getSkin(SkinType::EquipmentBox); - // When there is a background image, draw it centered in the box: - if (box.backgroundImage) - { - int posX = box.posX - + (BOX_WIDTH - box.backgroundImage->getWidth()) / 2; - int posY = box.posY - + (BOX_HEIGHT - box.backgroundImage->getHeight()) / 2; - g->drawImage(box.backgroundImage, posX, posY); - } - - const gcn::Rectangle tRect(box.posX, box.posY, - BOX_WIDTH, BOX_HEIGHT); - - if (static_cast<int>(i) == mSelected) - { - const gcn::Color color = Theme::getThemeColor(Theme::HIGHLIGHT); + // Draw equipment boxes + const int boxCount = mEquipment->getSlotNumber(); + for (int i = 0; i < boxCount; ++i) + { + Position boxPos = Net::getInventoryHandler()->getBoxPosition(i); + boxPos.x += getPadding(); + boxPos.y += getTitleBarHeight(); - // Set color to the highlight color - g->setColor(gcn::Color(color.r, color.g, color.b, getGuiAlpha())); - g->fillRectangle(tRect); - } + WidgetState boxState(gcn::Rectangle(boxPos.x, boxPos.y, boxSkin.width, boxSkin.height)); + if (i == mSelected) + boxState.flags |= STATE_SELECTED; - // Draw black box border - g->setColor(gcn::Color(0, 0, 0)); - g->drawRectangle(tRect); + boxSkin.draw(g, boxState); if (Item *item = mEquipment->getEquipment(i)) { - // Draw Item. - Image *image = item->getImage(); - // Ensure the image is drawn with maximum opacity - image->setAlpha(1.0f); - g->drawImage(image, - box.posX + 2, - box.posY + 2); + if (Image *image = item->getImage()) + { + image->setAlpha(1.0f); + g->drawImage(image, + boxPos.x + boxSkin.padding, + boxPos.y + boxSkin.padding); + } if (i == TmwAthena::EQUIP_PROJECTILE_SLOT) { g->setColor(Theme::getThemeColor(Theme::TEXT)); graphics->drawText(toString(item->getQuantity()), - box.posX + (BOX_WIDTH / 2), - box.posY - getFont()->getHeight(), + boxPos.x + boxSkin.width / 2, + boxPos.y - getFont()->getHeight(), gcn::Graphics::CENTER); } } + else + { + auto &icon = Net::getInventoryHandler()->getBoxIcon(i); + if (!icon.empty()) + if (auto image = theme->getIcon(icon)) + g->drawImage(image, boxPos.x + boxSkin.padding, boxPos.y + boxSkin.padding); + } } } @@ -183,12 +148,18 @@ void EquipmentWindow::action(const gcn::ActionEvent &event) */ int EquipmentWindow::getBoxIndex(int x, int y) const { - for (size_t i = 0; i < mBoxes.size(); ++i) - { - const auto &box = mBoxes[i]; - const gcn::Rectangle tRect(box.posX, box.posY, - BOX_WIDTH, BOX_HEIGHT); + auto &boxSkin = gui->getTheme()->getSkin(SkinType::EquipmentBox); + // Translate coordinates to content area + const auto childrenArea = const_cast<EquipmentWindow*>(this)->getChildrenArea(); + x -= childrenArea.x; + y -= childrenArea.y; + + const int boxCount = mEquipment->getSlotNumber(); + for (int i = 0; i < boxCount; ++i) + { + const Position boxPos = Net::getInventoryHandler()->getBoxPosition(i); + const gcn::Rectangle tRect(boxPos.x, boxPos.y, boxSkin.width, boxSkin.height); if (tRect.isPointInRect(x, y)) return i; } @@ -240,6 +211,8 @@ void EquipmentWindow::mousePressed(gcn::MouseEvent& mouseEvent) void EquipmentWindow::mouseMoved(gcn::MouseEvent &event) { + Window::mouseMoved(event); + const int x = event.getX(); const int y = event.getY(); @@ -264,6 +237,8 @@ void EquipmentWindow::mouseMoved(gcn::MouseEvent &event) void EquipmentWindow::mouseExited(gcn::MouseEvent &event) { + Window::mouseExited(event); + mItemPopup->setVisible(false); } diff --git a/src/gui/equipmentwindow.h b/src/gui/equipmentwindow.h index 1b63c866..944ae99e 100644 --- a/src/gui/equipmentwindow.h +++ b/src/gui/equipmentwindow.h @@ -19,11 +19,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef EQUIPMENTWINDOW_H -#define EQUIPMENTWINDOW_H +#pragma once #include "equipment.h" -#include "resources/image.h" #include "gui/widgets/window.h" @@ -53,11 +51,8 @@ class EquipmentWindow : public Window, public gcn::ActionListener void action(const gcn::ActionEvent &event) override; void mousePressed(gcn::MouseEvent& mouseEvent) override; - - /** - * Loads the correct amount of displayed equip boxes. - */ - void loadEquipBoxes(); + void mouseMoved(gcn::MouseEvent &event) override; + void mouseExited(gcn::MouseEvent &event) override; /** * Returns the current selected slot or -1 if none. @@ -66,25 +61,10 @@ class EquipmentWindow : public Window, public gcn::ActionListener { return mSelected; } protected: - /** - * Equipment box. - */ - struct EquipBox - { - int posX = 0; - int posY = 0; - Image *backgroundImage = nullptr; - }; - - std::vector<EquipBox> mBoxes; /**< Equipment boxes. */ - int mSelected = -1; /**< Index of selected item. */ Equipment *mEquipment; private: - void mouseExited(gcn::MouseEvent &event) override; - void mouseMoved(gcn::MouseEvent &event) override; - int getBoxIndex(int x, int y) const; Item *getItem(int x, int y) const; std::string getSlotName(int x, int y) const; @@ -96,5 +76,3 @@ class EquipmentWindow : public Window, public gcn::ActionListener }; extern EquipmentWindow *equipmentWindow; - -#endif // EQUIPMENTWINDOW_H diff --git a/src/gui/focushandler.h b/src/gui/focushandler.h index eb59bcf3..d90898bd 100644 --- a/src/gui/focushandler.h +++ b/src/gui/focushandler.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef FOCUSHANDLER_H -#define FOCUSHANDLER_H +#pragma once #include <guichan/focushandler.hpp> @@ -73,5 +72,3 @@ class FocusHandler : public gcn::FocusHandler */ std::list<gcn::Widget*> mModalStack; }; - -#endif diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a59242dc..0c3d78eb 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -37,10 +37,13 @@ #include "resources/resourcemanager.h" #include "resources/theme.h" +#include "utils/filesystem.h" #include <guichan/exception.hpp> #include <guichan/image.hpp> +#include <algorithm> + #include <SDL_image.h> // Guichan stuff @@ -55,9 +58,19 @@ gcn::Font *monoFont = nullptr; bool Gui::debugDraw; -Gui::Gui(Graphics *graphics) - : mCustomCursorScale(Client::getVideo().settings().scale()) +Gui::Gui(Graphics *graphics, const std::string &themePath) + : mAvailableThemes(Theme::getAvailableThemes()) + , mCustomCursorScale(Client::getVideo().settings().scale()) { + // Try to find the requested theme, using the first one as fallback + auto themeIt = std::find_if(mAvailableThemes.begin(), + mAvailableThemes.end(), + [&themePath](const ThemeInfo &theme) { + return theme.getPath() == themePath; + }); + + setTheme(themeIt != mAvailableThemes.end() ? *themeIt : mAvailableThemes.front()); + logger->log("Initializing GUI..."); // Set graphics setGraphics(graphics); @@ -66,7 +79,7 @@ Gui::Gui(Graphics *graphics) guiInput = new SDLInput; setInput(guiInput); - // Set focus handler + // Replace focus handler delete mFocusHandler; mFocusHandler = new FocusHandler; @@ -78,12 +91,10 @@ Gui::Gui(Graphics *graphics) Window::setWindowContainer(guiTop); setTop(guiTop); - ResourceManager *resman = ResourceManager::getInstance(); - // Set global font const int fontSize = config.fontSize; std::string fontFile = branding.getValue("font", "fonts/dejavusans.ttf"); - std::string path = resman->getPath(fontFile); + std::string path = ResourceManager::getPath(fontFile); // Initialize the font scale before creating the fonts TrueTypeFont::updateFontScale(graphics->getScale()); @@ -101,7 +112,7 @@ Gui::Gui(Graphics *graphics) // Set bold font fontFile = branding.getValue("boldFont", "fonts/dejavusans-bold.ttf"); - path = resman->getPath(fontFile); + path = ResourceManager::getPath(fontFile); try { boldFont = new TrueTypeFont(path, fontSize); @@ -114,7 +125,7 @@ Gui::Gui(Graphics *graphics) // Set mono font fontFile = branding.getValue("monoFont", "fonts/dejavusans-mono.ttf"); - path = resman->getPath(fontFile); + path = ResourceManager::getPath(fontFile); try { monoFont = new TrueTypeFont(path, fontSize); @@ -151,8 +162,6 @@ Gui::~Gui() delete getTop(); delete guiInput; - - Theme::deleteInstance(); } void Gui::logic() @@ -228,6 +237,11 @@ void Gui::setCursorType(Cursor cursor) updateCursor(); } +void Gui::setTheme(const ThemeInfo &theme) +{ + mTheme = std::make_unique<Theme>(theme); +} + void Gui::updateCursor() { if (mCustomCursor && !mCustomMouseCursors.empty()) @@ -258,7 +272,7 @@ void Gui::handleTextInput(const TextInput &textInput) static SDL_Surface *loadSurface(const std::string &path) { - if (SDL_RWops *file = ResourceManager::getInstance()->open(path)) + if (SDL_RWops *file = FS::openRWops(path)) return IMG_Load_RW(file, 1); return nullptr; } @@ -270,7 +284,7 @@ void Gui::loadCustomCursors() mCustomMouseCursors.clear(); - const std::string cursorPath = Theme::resolveThemePath("mouse.png"); + const std::string cursorPath = mTheme->resolvePath("mouse.png"); SDL_Surface *mouseSurface = loadSurface(cursorPath); if (!mouseSurface) { @@ -301,7 +315,7 @@ void Gui::loadCustomCursors() 0, targetCursorSize, targetCursorSize, 32, rmask, gmask, bmask, amask); - for (int i = 0; i <= static_cast<int>(Cursor::LAST); ++i) + for (int i = 0; i < static_cast<int>(Cursor::Count); ++i) { int x = i % columns * cursorSize; int y = i / columns * cursorSize; @@ -330,22 +344,22 @@ void Gui::loadSystemCursors() constexpr struct { Cursor cursor; SDL_SystemCursor systemCursor; - } cursors[static_cast<int>(Cursor::LAST) + 1] = { - { Cursor::POINTER, SDL_SYSTEM_CURSOR_ARROW }, - { Cursor::RESIZE_ACROSS, SDL_SYSTEM_CURSOR_SIZEWE }, - { Cursor::RESIZE_DOWN, SDL_SYSTEM_CURSOR_SIZENS }, - { Cursor::RESIZE_DOWN_LEFT, SDL_SYSTEM_CURSOR_SIZENESW }, - { Cursor::RESIZE_DOWN_RIGHT, SDL_SYSTEM_CURSOR_SIZENWSE }, - { Cursor::FIGHT, SDL_SYSTEM_CURSOR_HAND }, - { Cursor::PICKUP, SDL_SYSTEM_CURSOR_HAND }, - { Cursor::TALK, SDL_SYSTEM_CURSOR_HAND }, - { Cursor::ACTION, SDL_SYSTEM_CURSOR_HAND }, - { Cursor::LEFT, SDL_SYSTEM_CURSOR_ARROW }, - { Cursor::UP, SDL_SYSTEM_CURSOR_ARROW }, - { Cursor::RIGHT, SDL_SYSTEM_CURSOR_ARROW }, - { Cursor::DOWN, SDL_SYSTEM_CURSOR_ARROW }, - { Cursor::DRAG, SDL_SYSTEM_CURSOR_SIZEALL }, - { Cursor::HAND, SDL_SYSTEM_CURSOR_HAND }, + } cursors[static_cast<int>(Cursor::Count)] = { + { Cursor::Pointer, SDL_SYSTEM_CURSOR_ARROW }, + { Cursor::ResizeAcross, SDL_SYSTEM_CURSOR_SIZEWE }, + { Cursor::ResizeDown, SDL_SYSTEM_CURSOR_SIZENS }, + { Cursor::ResizeDownLeft, SDL_SYSTEM_CURSOR_SIZENESW }, + { Cursor::ResizeDownRight, SDL_SYSTEM_CURSOR_SIZENWSE }, + { Cursor::Fight, SDL_SYSTEM_CURSOR_HAND }, + { Cursor::PickUp, SDL_SYSTEM_CURSOR_HAND }, + { Cursor::Talk, SDL_SYSTEM_CURSOR_HAND }, + { Cursor::Action, SDL_SYSTEM_CURSOR_HAND }, + { Cursor::Left, SDL_SYSTEM_CURSOR_ARROW }, + { Cursor::Up, SDL_SYSTEM_CURSOR_ARROW }, + { Cursor::Right, SDL_SYSTEM_CURSOR_ARROW }, + { Cursor::Down, SDL_SYSTEM_CURSOR_ARROW }, + { Cursor::Drag, SDL_SYSTEM_CURSOR_SIZEALL }, + { Cursor::Hand, SDL_SYSTEM_CURSOR_HAND }, }; for (auto cursor : cursors) diff --git a/src/gui/gui.h b/src/gui/gui.h index fd1dcf94..2584b780 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -19,18 +19,20 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_H -#define GUI_H +#pragma once #include "eventlistener.h" #include "guichanfwd.h" +#include "resources/theme.h" + #include "utils/time.h" #include <guichan/gui.hpp> #include <SDL.h> +#include <memory> #include <vector> class TextInput; @@ -49,23 +51,24 @@ class SDLInput; * Cursors are in graphic order from left to right. * CURSOR_POINTER should be left untouched. */ -enum class Cursor { - POINTER = 0, - RESIZE_ACROSS, - RESIZE_DOWN, - RESIZE_DOWN_LEFT, - RESIZE_DOWN_RIGHT, - FIGHT, - PICKUP, - TALK, - ACTION, - LEFT, - UP, - RIGHT, - DOWN, - DRAG, - HAND, - LAST = HAND, +enum class Cursor +{ + Pointer = 0, + ResizeAcross, + ResizeDown, + ResizeDownLeft, + ResizeDownRight, + Fight, + PickUp, + Talk, + Action, + Left, + Up, + Right, + Down, + Drag, + Hand, + Count, }; /** @@ -76,7 +79,7 @@ enum class Cursor { class Gui final : public gcn::Gui, public EventListener { public: - Gui(Graphics *screen); + Gui(Graphics *screen, const std::string &themePath); ~Gui() override; @@ -121,6 +124,20 @@ class Gui final : public gcn::Gui, public EventListener */ void setCursorType(Cursor cursor); + const std::vector<ThemeInfo> &getAvailableThemes() const + { return mAvailableThemes; } + + /** + * Sets the global GUI theme. + */ + void setTheme(const ThemeInfo &theme); + + /** + * The global GUI theme. + */ + Theme *getTheme() const + { return mTheme.get(); } + static bool debugDraw; protected: @@ -133,6 +150,8 @@ class Gui final : public gcn::Gui, public EventListener void loadCustomCursors(); void loadSystemCursors(); + std::vector<ThemeInfo> mAvailableThemes; + std::unique_ptr<Theme> mTheme; /**< The global GUI theme */ gcn::Font *mGuiFont; /**< The global GUI font */ gcn::Font *mInfoParticleFont; /**< Font for Info Particles*/ bool mCustomCursor = false; /**< Show custom cursor */ @@ -140,7 +159,7 @@ class Gui final : public gcn::Gui, public EventListener std::vector<SDL_Cursor *> mSystemMouseCursors; std::vector<SDL_Cursor *> mCustomMouseCursors; Timer mMouseActivityTimer; - Cursor mCursorType = Cursor::POINTER; + Cursor mCursorType = Cursor::Pointer; }; extern Gui *gui; /**< The GUI system */ @@ -155,5 +174,3 @@ extern gcn::Font *boldFont; * Monospaced text font */ extern gcn::Font *monoFont; - -#endif // GUI_H diff --git a/src/gui/helpwindow.cpp b/src/gui/helpwindow.cpp index 7bb31188..e0e21610 100644 --- a/src/gui/helpwindow.cpp +++ b/src/gui/helpwindow.cpp @@ -29,9 +29,10 @@ #include "gui/widgets/layout.h" #include "gui/widgets/scrollarea.h" -#include "resources/resourcemanager.h" #include "configuration.h" +#include "log.h" +#include "utils/filesystem.h" #include "utils/gettext.h" HelpWindow::HelpWindow(): @@ -47,7 +48,6 @@ HelpWindow::HelpWindow(): setDefaultSize(500, 400, ImageRect::CENTER); mBrowserBox = new BrowserBox; - mBrowserBox->setFrameSize(4); mScrollArea = new ScrollArea(mBrowserBox); auto *okButton = new Button(_("Close"), "close", this); @@ -88,12 +88,20 @@ void HelpWindow::loadHelp(const std::string &helpFile) void HelpWindow::loadFile(const std::string &file) { - ResourceManager *resman = ResourceManager::getInstance(); std::string helpPath = branding.getStringValue("helpPath"); if (helpPath.empty()) helpPath = paths.getStringValue("help"); - const auto lines = resman->loadTextFile(helpPath + file + ".txt"); - for (auto &line : lines) - mBrowserBox->addRow(line); + const std::string fileName = helpPath + file + ".txt"; + + size_t contentsLength; + char *fileContents = (char *) FS::loadFile(fileName, contentsLength); + if (!fileContents) + { + logger->log("Couldn't load text file: %s", fileName.c_str()); + return; + } + + mBrowserBox->addRows(std::string_view(fileContents, contentsLength)); + SDL_free(fileContents); } diff --git a/src/gui/helpwindow.h b/src/gui/helpwindow.h index 30fa450e..2daf2480 100644 --- a/src/gui/helpwindow.h +++ b/src/gui/helpwindow.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef HELP_H -#define HELP_H +#pragma once #include "gui/widgets/linkhandler.h" #include "gui/widgets/window.h" @@ -62,5 +61,3 @@ class HelpWindow : public Window, public LinkHandler, }; extern HelpWindow *helpWindow; - -#endif diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index 048f83f5..ab2e9c86 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -30,7 +30,6 @@ #include "gui/itemamountwindow.h" #include "gui/setup.h" -#include "gui/sdlinput.h" #include "gui/viewport.h" #include "gui/widgets/button.h" @@ -40,9 +39,6 @@ #include "gui/widgets/progressbar.h" #include "gui/widgets/scrollarea.h" -#include "net/inventoryhandler.h" -#include "net/net.h" - #include "resources/iteminfo.h" #include "resources/theme.h" @@ -80,7 +76,7 @@ InventoryWindow::InventoryWindow(Inventory *inventory): mItems = new ItemContainer(mInventory); mItems->addSelectionListener(this); - gcn::ScrollArea *invenScroll = new ScrollArea(mItems); + auto invenScroll = new ScrollArea(mItems); invenScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); mSlotsLabel = new Label(_("Slots:")); @@ -105,7 +101,6 @@ InventoryWindow::InventoryWindow(Inventory *inventory): mEquipButton = new Button(_("Equip"), "equip", this); mUseButton = new Button(_("Activate"), "activate", this); mDropButton = new Button(_("Drop..."), "drop", this); - mSplitButton = new Button(_("Split"), "split", this); mOutfitButton = new Button(_("Outfits"), "outfit", this); mWeightLabel = new Label(_("Weight:")); @@ -121,7 +116,6 @@ InventoryWindow::InventoryWindow(Inventory *inventory): place(0, 3, mUseButton); place(1, 3, mEquipButton); place(3, 3, mDropButton); - place(4, 3, mSplitButton); place(7, 3, mOutfitButton); updateWeight(); @@ -182,7 +176,6 @@ void InventoryWindow::action(const gcn::ActionEvent &event) if (!inventoryWindow->isVisible()) return; Item *item = inventoryWindow->getSelectedItem(); - if (!item) return; @@ -190,12 +183,13 @@ void InventoryWindow::action(const gcn::ActionEvent &event) } Item *item = mItems->getSelectedItem(); - if (!item) return; if (event.getId() == "activate") + { item->doEvent(Event::DoUse); + } else if (event.getId() == "equip") { if (item->isEquippable()) @@ -214,15 +208,9 @@ void InventoryWindow::action(const gcn::ActionEvent &event) { ItemAmountWindow::showWindow(ItemAmountWindow::ItemDrop, this, item); } - else if (event.getId() == "split") - { - ItemAmountWindow::showWindow(ItemAmountWindow::ItemSplit, this, item, - (item->getQuantity() - 1)); - } else if (event.getId() == "retrieve") { Item *item = mItems->getSelectedItem(); - if (!item) return; @@ -247,26 +235,27 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event) Window::mouseClicked(event); Item *item = mItems->getSelectedItem(); + if (!item) + return; - if (event.getSource() == mItems && item && isDoubleClick(item->getInvIndex())) + if (event.getSource() == mItems && isDoubleClick(item->getInvIndex()) + && isMainInventory()) { - if (isMainInventory() && item->getInfo().activatable) + if (item->getInfo().activatable) { action(gcn::ActionEvent(mUseButton, mUseButton->getActionEventId())); } - else if (isMainInventory() && item->isEquippable()) + else if (item->isEquippable()) { action(gcn::ActionEvent(mEquipButton, mEquipButton->getActionEventId())); } + return; } if (event.getButton() == gcn::MouseEvent::RIGHT) { - if (!item) - return; - /* Convert relative to the window coordinates to absolute screen * coordinates. */ @@ -279,10 +268,6 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event) { if (instances.size() > 1 && keyboard.isKeyActive(KeyboardConfig::KEY_EMOTE)) { - Item *item = mItems->getSelectedItem(); - - if(!item) - return; if (mInventory->isMainInventory()) { Event event(Event::DoMove); @@ -305,32 +290,10 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event) } } -void InventoryWindow::keyPressed(gcn::KeyEvent &event) -{ - switch (event.getKey().getValue()) - { - case Key::LEFT_SHIFT: - case Key::RIGHT_SHIFT: - mSplit = true; - break; - } -} - void InventoryWindow::keyReleased(gcn::KeyEvent &event) { if (isInputFocused()) - { mItems->setFilter(mFilterText->getText()); - return; - } - - switch (event.getKey().getValue()) - { - case Key::LEFT_SHIFT: - case Key::RIGHT_SHIFT: - mSplit = false; - break; - } } void InventoryWindow::valueChanged(const gcn::SelectionEvent &event) @@ -338,15 +301,6 @@ void InventoryWindow::valueChanged(const gcn::SelectionEvent &event) if (!mInventory->isMainInventory()) return; - Item *item = mItems->getSelectedItem(); - - if (mSplit && Net::getInventoryHandler()-> - canSplit(mItems->getSelectedItem()) && item) - { - ItemAmountWindow::showWindow(ItemAmountWindow::ItemSplit, this, item, - (item->getQuantity() - 1)); - } - updateButtons(); } @@ -359,44 +313,19 @@ void InventoryWindow::updateButtons() mUseButton->setEnabled(false); mEquipButton->setEnabled(false); mDropButton->setEnabled(false); - mSplitButton->setEnabled(false); - return; } mDropButton->setEnabled(true); - if (item->isEquippable()) - { - if (item->isEquipped()) - mEquipButton->setCaption(_("Unequip")); - else - mEquipButton->setCaption(_("Equip")); - mEquipButton->setEnabled(true); - } - else - mEquipButton->setEnabled(false); - + mEquipButton->setCaption(item->isEquipped() ? _("Unequip") : _("Equip")); + mEquipButton->setEnabled(item->isEquippable()); mEquipButton->adjustSize(); mUseButton->setEnabled(item->getInfo().activatable); - if (item->getQuantity() > 1) - mDropButton->setCaption(_("Drop...")); - else - mDropButton->setCaption(_("Drop")); - - if (Net::getInventoryHandler()->canSplit(item)) - mSplitButton->setEnabled(true); - else - mSplitButton->setEnabled(false); - - mSplitButton->adjustSize(); -} - -void InventoryWindow::setSplitAllowed(bool allowed) -{ - mSplitButton->setVisible(allowed); + mDropButton->setCaption(item->getQuantity() > 1 ? _("Drop...") : _("Drop")); + mDropButton->adjustSize(); } void InventoryWindow::close() @@ -418,12 +347,9 @@ void InventoryWindow::event(Event::Channel channel, const Event &event) { if (event.getType() == Event::UpdateAttribute) { - int id = event.getInt("id"); - if (id == TOTAL_WEIGHT || - id == MAX_WEIGHT) - { + const int id = event.getInt("id"); + if (id == TOTAL_WEIGHT || id == MAX_WEIGHT) updateWeight(); - } } } @@ -432,8 +358,8 @@ void InventoryWindow::updateWeight() if (!isMainInventory()) return; - int total = PlayerInfo::getAttribute(TOTAL_WEIGHT); - int max = PlayerInfo::getAttribute(MAX_WEIGHT); + const int total = PlayerInfo::getAttribute(TOTAL_WEIGHT); + const int max = PlayerInfo::getAttribute(MAX_WEIGHT); if (max <= 0) return; @@ -451,21 +377,14 @@ bool InventoryWindow::isInputFocused() const bool InventoryWindow::isAnyInputFocused() { - auto it = instances.begin(); - auto it_end = instances.end(); - - for (; it != it_end; it++) - { - if ((*it)->isInputFocused()) - { + for (auto instance : instances) + if (instance->isInputFocused()) return true; - } - } return false; } -void InventoryWindow::slotsChanged(Inventory* inventory) +void InventoryWindow::slotsChanged(Inventory *inventory) { if (inventory == mInventory) { @@ -473,7 +392,6 @@ void InventoryWindow::slotsChanged(Inventory* inventory) const int maxSlots = mInventory->getSize(); mSlotsBar->setProgress((float) usedSlots / maxSlots); - mSlotsBar->setText(strprintf("%d/%d", usedSlots, maxSlots)); } } diff --git a/src/gui/inventorywindow.h b/src/gui/inventorywindow.h index 048b229c..267d8dc1 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef INVENTORYWINDOW_H -#define INVENTORYWINDOW_H +#pragma once #include "inventory.h" #include "eventlistener.h" @@ -28,8 +27,6 @@ #include "gui/widgets/window.h" #include "gui/widgets/textfield.h" -#include "net/inventoryhandler.h" - #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> #include <guichan/selectionlistener.hpp> @@ -44,12 +41,12 @@ class TextBox; * * \ingroup Interface */ -class InventoryWindow : public Window, - public gcn::ActionListener, - public gcn::KeyListener, - public gcn::SelectionListener, - public InventoryListener, - public EventListener +class InventoryWindow final : public Window, + public gcn::ActionListener, + public gcn::KeyListener, + public gcn::SelectionListener, + public InventoryListener, + public EventListener { public: InventoryWindow(Inventory *inventory); @@ -64,7 +61,7 @@ class InventoryWindow : public Window, /** * Returns the selected item. */ - Item* getSelectedItem() const; + Item *getSelectedItem() const; /** * Handles closing of the window @@ -77,11 +74,6 @@ class InventoryWindow : public Window, void mouseClicked(gcn::MouseEvent &event) override; /** - * Handles the key presses. - */ - void keyPressed(gcn::KeyEvent &event) override; - - /** * Handles the key releases. */ void keyReleased(gcn::KeyEvent &event) override; @@ -92,11 +84,6 @@ class InventoryWindow : public Window, void valueChanged(const gcn::SelectionEvent &event) override; /** - * Sets whether the split button should be shown. - */ - void setSplitAllowed(bool allowed); - - /** * Closes the Storage Window, as well as telling the server that the * window has been closed. */ @@ -111,9 +98,9 @@ class InventoryWindow : public Window, static bool isAnyInputFocused(); - void slotsChanged(Inventory* inventory) override; + void slotsChanged(Inventory *inventory) override; - bool isMainInventory() { return mInventory->isMainInventory(); } + bool isMainInventory() const { return mInventory->isMainInventory(); } void event(Event::Channel channel, const Event &event) override; @@ -134,16 +121,12 @@ class InventoryWindow : public Window, std::string mWeight, mSlots; - gcn::Button *mUseButton, *mEquipButton, *mDropButton, *mSplitButton, + gcn::Button *mUseButton, *mEquipButton, *mDropButton, *mOutfitButton, *mStoreButton, *mRetrieveButton; gcn::Label *mWeightLabel, *mSlotsLabel, *mFilterLabel; ProgressBar *mWeightBar, *mSlotsBar; - - bool mSplit = false; }; extern InventoryWindow *inventoryWindow; - -#endif diff --git a/src/gui/itemamountwindow.cpp b/src/gui/itemamountwindow.cpp index 947b5bdc..de204da3 100644 --- a/src/gui/itemamountwindow.cpp +++ b/src/gui/itemamountwindow.cpp @@ -21,6 +21,7 @@ #include "gui/itemamountwindow.h" +#include "inventory.h" #include "item.h" #include "keyboardconfig.h" @@ -34,8 +35,8 @@ #include "gui/widgets/slider.h" #include "gui/widgets/icon.h" -#include "net/inventoryhandler.h" #include "net/net.h" +#include "net/tradehandler.h" #include "utils/gettext.h" @@ -44,14 +45,11 @@ void ItemAmountWindow::finish(Item *item, int amount, Usage usage) switch (usage) { case TradeAdd: - tradeWindow->tradeItem(item, amount); + Net::getTradeHandler()->addItem(item, amount); break; case ItemDrop: item->doEvent(Event::DoDrop, amount); break; - case ItemSplit: - item->doEvent(Event::DoSplit, amount); - break; case StoreAdd: { Event event(Event::DoMove); @@ -130,7 +128,7 @@ ItemAmountWindow::ItemAmountWindow(Usage usage, Window *parent, Item *item, place(4, 2, cancelButton); place(5, 2, okButton); - reflowLayout(225, 0); + reflowLayout(); resetAmount(); @@ -148,9 +146,6 @@ ItemAmountWindow::ItemAmountWindow(Usage usage, Window *parent, Item *item, case StoreRemove: setCaption(_("Select amount of items to retrieve.")); break; - case ItemSplit: - setCaption(_("Select amount of items to split.")); - break; } setLocationRelativeTo(getParentWindow()); diff --git a/src/gui/itemamountwindow.h b/src/gui/itemamountwindow.h index d0ac52d5..489fdbef 100644 --- a/src/gui/itemamountwindow.h +++ b/src/gui/itemamountwindow.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef ITEM_AMOUNT_WINDOW_H -#define ITEM_AMOUNT_WINDOW_H +#pragma once #include "gui/widgets/window.h" @@ -33,7 +32,7 @@ class ItemPopup; class Icon; /** - * Window used for selecting the amount of items to drop, trade or split. + * Window used for selecting the amount of items to drop, trade or store. * * \ingroup Interface */ @@ -47,7 +46,6 @@ class ItemAmountWindow : public Window, ItemDrop, StoreAdd, StoreRemove, - ItemSplit }; /** @@ -100,5 +98,3 @@ class ItemAmountWindow : public Window, bool mEnabledKeyboard; }; - -#endif // ITEM_AMOUNT_WINDOW_H diff --git a/src/gui/itempopup.cpp b/src/gui/itempopup.cpp index 7f1dec3b..3e7ba15a 100644 --- a/src/gui/itempopup.cpp +++ b/src/gui/itempopup.cpp @@ -23,7 +23,6 @@ #include "gui/itempopup.h" #include "configuration.h" -#include "graphics.h" #include "units.h" #include "gui/gui.h" @@ -35,7 +34,6 @@ #include "utils/gettext.h" #include "utils/stringutils.h" -#include "resources/image.h" #include "resources/resourcemanager.h" #include "resources/theme.h" @@ -45,38 +43,38 @@ #define ITEMPOPUP_WRAP_WIDTH 196 -static const gcn::Color &getColorFromItemType(ItemType type) +static int getColorIdFromItemType(ItemType type) { switch (type) { case ITEM_UNUSABLE: - return Theme::getThemeColor(Theme::GENERIC); + return Theme::GENERIC; case ITEM_USABLE: - return Theme::getThemeColor(Theme::USABLE); + return Theme::USABLE; case ITEM_EQUIPMENT_ONE_HAND_WEAPON: - return Theme::getThemeColor(Theme::ONEHAND); + return Theme::ONEHAND; case ITEM_EQUIPMENT_TWO_HANDS_WEAPON: - return Theme::getThemeColor(Theme::TWOHAND); + return Theme::TWOHAND; case ITEM_EQUIPMENT_TORSO: - return Theme::getThemeColor(Theme::TORSO); + return Theme::TORSO; case ITEM_EQUIPMENT_ARMS: - return Theme::getThemeColor(Theme::ARMS); + return Theme::ARMS; case ITEM_EQUIPMENT_HEAD: - return Theme::getThemeColor(Theme::HEAD); + return Theme::HEAD; case ITEM_EQUIPMENT_LEGS: - return Theme::getThemeColor(Theme::LEGS); + return Theme::LEGS; case ITEM_EQUIPMENT_SHIELD: - return Theme::getThemeColor(Theme::SHIELD); + return Theme::SHIELD; case ITEM_EQUIPMENT_RING: - return Theme::getThemeColor(Theme::RING); + return Theme::RING; case ITEM_EQUIPMENT_NECKLACE: - return Theme::getThemeColor(Theme::NECKLACE); + return Theme::NECKLACE; case ITEM_EQUIPMENT_FEET: - return Theme::getThemeColor(Theme::FEET); + return Theme::FEET; case ITEM_EQUIPMENT_AMMO: - return Theme::getThemeColor(Theme::AMMO); + return Theme::AMMO; default: - return Theme::getThemeColor(Theme::UNKNOWN_ITEM); + return Theme::UNKNOWN_ITEM; } } @@ -134,11 +132,16 @@ void ItemPopup::setNoItem() mItemName->setCaption(caption); mItemName->adjustSize(); - mItemName->setForegroundColor(Theme::getThemeColor(Theme::GENERIC)); + auto theme = gui->getTheme(); + auto &palette = theme->getPalette(getSkin().palette); + + mItemName->setForegroundColor(palette.getColor(Theme::GENERIC)); + mItemName->setOutlineColor(palette.getOutlineColor(Theme::GENERIC)); mItemName->setPosition(0, 0); mItemDesc->setText(std::string()); mItemEffect->setText(std::string()); + mItemWeight->setText(std::string()); setContentSize(mItemName->getWidth(), mItemName->getHeight()); } @@ -153,8 +156,8 @@ void ItemPopup::setItem(const ItemInfo &item, bool showImage) if (showImage) { ResourceManager *resman = ResourceManager::getInstance(); - auto image = resman->getImageRef(paths.getStringValue("itemIcons") + - item.display.image); + auto image = resman->getImage(paths.getStringValue("itemIcons") + + item.display.image); mIcon->setImage(image); if (image) @@ -174,9 +177,15 @@ void ItemPopup::setItem(const ItemInfo &item, bool showImage) if (!mItemEquipSlot.empty()) caption += " (" + mItemEquipSlot + ")"; + auto theme = gui->getTheme(); + auto &palette = theme->getPalette(getSkin().palette); + + const auto typeColorId = getColorIdFromItemType(mItemType); + mItemName->setCaption(caption); mItemName->adjustSize(); - mItemName->setForegroundColor(getColorFromItemType(mItemType)); + mItemName->setForegroundColor(palette.getColor(typeColorId)); + mItemName->setOutlineColor(palette.getOutlineColor(typeColorId)); mItemName->setPosition(space, 0); mItemDesc->setTextWrapped(item.description, ITEMPOPUP_WRAP_WIDTH); diff --git a/src/gui/itempopup.h b/src/gui/itempopup.h index 3b213633..c741ed64 100644 --- a/src/gui/itempopup.h +++ b/src/gui/itempopup.h @@ -20,8 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef ITEMPOPUP_H -#define ITEMPOPUP_H +#pragma once #include "gui/widgets/popup.h" @@ -30,6 +29,7 @@ #include <guichan/mouselistener.hpp> class Icon; +class Label; class TextBox; /** @@ -66,7 +66,7 @@ class ItemPopup : public Popup void mouseMoved(gcn::MouseEvent &mouseEvent) override; private: - gcn::Label *mItemName; + Label *mItemName; TextBox *mItemDesc; TextBox *mItemEffect; TextBox *mItemWeight; @@ -74,5 +74,3 @@ class ItemPopup : public Popup ItemType mItemType; Icon *mIcon; }; - -#endif // ITEMPOPUP_H diff --git a/src/gui/logindialog.cpp b/src/gui/logindialog.cpp index 1f96e02d..42ec0842 100644 --- a/src/gui/logindialog.cpp +++ b/src/gui/logindialog.cpp @@ -64,8 +64,8 @@ LoginDialog::LoginDialog(LoginData *loginData): place(0, 0, userLabel); place(0, 1, passLabel); - place(1, 0, mUserField, 3).setPadding(1); - place(1, 1, mPassField, 3).setPadding(1); + place(1, 0, mUserField, 3).setPadding(2); + place(1, 1, mPassField, 3).setPadding(2); place(0, 5, mKeepCheck, 4); place(0, 6, mRegisterButton).setHAlign(LayoutCell::LEFT); place(2, 6, mServerButton); diff --git a/src/gui/logindialog.h b/src/gui/logindialog.h index 67814cd4..fc3fa249 100644 --- a/src/gui/logindialog.h +++ b/src/gui/logindialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef LOGIN_H -#define LOGIN_H +#pragma once #include "gui/widgets/window.h" @@ -69,5 +68,3 @@ class LoginDialog : public Window, public gcn::ActionListener, LoginData *mLoginData; }; - -#endif diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index f5206eda..60bfadfa 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -34,12 +34,14 @@ #include "resources/resourcemanager.h" #include "resources/userpalette.h" +#include "utils/filesystem.h" #include "utils/gettext.h" +#include <algorithm> #include <guichan/font.hpp> Minimap::Minimap(): - Window(_("Map")) + Window(SkinType::Popup, _("Map")) { setWindowName("Minimap"); setDefaultSize(5, 25, 100, 100); @@ -58,8 +60,7 @@ Minimap::Minimap(): setVisible(config.showMinimap, isSticky()); } -Minimap::~Minimap() -{} +Minimap::~Minimap() = default; void Minimap::setMap(Map *map) { @@ -86,11 +87,11 @@ void Minimap::setMap(Map *map) std::string minimapName = map->getProperty("minimap"); - if (minimapName.empty() && resman->exists(tempname)) + if (minimapName.empty() && FS::exists(tempname)) minimapName = tempname; if (!minimapName.empty()) - mMapImage = resman->getImageRef(minimapName); + mMapImage = resman->getImage(minimapName); } if (mMapImage) @@ -136,40 +137,38 @@ void Minimap::draw(gcn::Graphics *graphics) { Window::draw(graphics); + if (!mMap) + return; + + auto g = static_cast<Graphics*>(graphics); const gcn::Rectangle a = getChildrenArea(); - graphics->pushClipArea(a); + g->pushClipRect(a); // does actual clipping + g->pushClipArea(a); // only applies an offset + + const int tileWidth = mMap->getTileWidth(); + const int tileHeight = mMap->getTileHeight(); int mapOriginX = 0; int mapOriginY = 0; - if (mMapImage && mMap) + if (mMapImage) { if (mMapImage->getWidth() > a.width || mMapImage->getHeight() > a.height) { const Vector &p = local_player->getPosition(); - mapOriginX = (int) (((a.width) / 2) - (int) (p.x * mWidthProportion) - / mMap->getTileWidth()); - mapOriginY = (int) (((a.height) / 2) - - (int) (p.y * mHeightProportion) - / mMap->getTileHeight()); + mapOriginX = (a.width / 2) - (int) (p.x * mWidthProportion) / tileWidth; + mapOriginY = (a.height / 2) - (int) (p.y * mHeightProportion) / tileHeight; const int minOriginX = a.width - mMapImage->getWidth(); const int minOriginY = a.height - mMapImage->getHeight(); - if (mapOriginX < minOriginX) - mapOriginX = minOriginX; - if (mapOriginY < minOriginY) - mapOriginY = minOriginY; - if (mapOriginX > 0) - mapOriginX = 0; - if (mapOriginY > 0) - mapOriginY = 0; + mapOriginX = std::clamp(mapOriginX, minOriginX, 0); + mapOriginY = std::clamp(mapOriginY, minOriginY, 0); } - static_cast<Graphics*>(graphics)-> - drawImage(mMapImage, mapOriginX, mapOriginY); + g->drawImage(mMapImage, mapOriginX, mapOriginY); } for (auto actor : actorSpriteManager->getAll()) @@ -218,16 +217,14 @@ void Minimap::draw(gcn::Graphics *graphics) const int offsetWidth = (int) ((dotSize - 1) * mWidthProportion); const Vector &pos = being->getPosition(); - if (mMap) - { - graphics->fillRectangle(gcn::Rectangle( - (int) (pos.x * mWidthProportion) / mMap->getTileWidth() - + mapOriginX - offsetWidth, - (int) (pos.y * mHeightProportion) / mMap->getTileHeight() - + mapOriginY - offsetHeight, - dotSize, dotSize)); - } + g->fillRectangle( + gcn::Rectangle((int) (pos.x * mWidthProportion) / tileWidth + mapOriginX - offsetWidth, + (int) (pos.y * mHeightProportion) / tileHeight + mapOriginY + - offsetHeight, + dotSize, + dotSize)); } - graphics->popClipArea(); + g->popClipArea(); + g->popClipRect(); } diff --git a/src/gui/minimap.h b/src/gui/minimap.h index 8212f5b7..bf6cd89d 100644 --- a/src/gui/minimap.h +++ b/src/gui/minimap.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef MINIMAP_H -#define MINIMAP_H +#pragma once #include "gui/widgets/window.h" @@ -67,5 +66,3 @@ class Minimap : public Window }; extern Minimap *minimap; - -#endif diff --git a/src/gui/ministatuswindow.cpp b/src/gui/ministatuswindow.cpp index 598cb722..9d695954 100644 --- a/src/gui/ministatuswindow.cpp +++ b/src/gui/ministatuswindow.cpp @@ -21,10 +21,11 @@ #include "gui/ministatuswindow.h" -#include "animatedsprite.h" #include "configuration.h" +#include "game.h" #include "graphics.h" #include "playerinfo.h" +#include "sprite.h" #include "statuseffect.h" #include "gui/gui.h" @@ -39,16 +40,22 @@ #include "net/tmwa/protocol.h" +#include "resources/statuseffectdb.h" #include "resources/theme.h" #include "utils/gettext.h" #include "utils/stringutils.h" #include "utils/time.h" +#include <algorithm> + +static constexpr int ICON_SPACING = 3; + MiniStatusWindow::MiniStatusWindow(): Popup("MiniStatus") { setPadding(3); + setMinHeight(0); listen(Event::AttributesChannel); listen(Event::ActorSpriteChannel); @@ -83,8 +90,7 @@ MiniStatusWindow::MiniStatusWindow(): add(mMpBar); add(mXpBar); - setContentSize(mXpBar->getX() + mXpBar->getWidth(), - mXpBar->getY() + mXpBar->getHeight()); + updateSize(); auto stateIt = config.windows.find(getPopupName()); setVisible(stateIt != config.windows.end() ? stateIt->second.visible.value_or(true) @@ -95,36 +101,27 @@ MiniStatusWindow::MiniStatusWindow(): addMouseListener(this); } -void MiniStatusWindow::setIcon(int index, AnimatedSprite *sprite) -{ - if (index >= (int) mIcons.size()) - mIcons.resize(index + 1); - - delete mIcons[index]; - mIcons[index] = sprite; -} - -void MiniStatusWindow::eraseIcon(int index) -{ - mIcons.erase(mIcons.begin() + index); -} +MiniStatusWindow::~MiniStatusWindow() = default; void MiniStatusWindow::drawIcons(Graphics *graphics) { - // Draw icons - int icon_x = mXpBar->getX() + mXpBar->getWidth() + 14; - for (auto &icon : mIcons) + const auto game = Game::instance(); + const int tileWidth = game->getCurrentTileWidth(); + const int tileHeight = game->getCurrentTileHeight(); + + int iconX = mXpBar->getX() + mXpBar->getWidth() + ICON_SPACING + tileWidth / 2; + int iconY = ICON_SPACING + tileHeight; + + for (auto &icon : mStatusIcons) { - if (icon) - { - icon->draw(graphics, icon_x, 3); - icon_x += 2 + icon->getWidth(); - } + icon.sprite->draw(graphics, + iconX - icon.sprite->getWidth() / 2, + iconY - icon.sprite->getHeight()); + iconX += ICON_SPACING + icon.sprite->getWidth(); } } -void MiniStatusWindow::event(Event::Channel channel, - const Event &event) +void MiniStatusWindow::event(Event::Channel channel, const Event &event) { if (channel == Event::AttributesChannel) { @@ -146,7 +143,7 @@ void MiniStatusWindow::event(Event::Channel channel, } if (event.getType() == Event::UpdateStat) { - if (Net::getNetworkType() == ServerType::TMWATHENA && + if (Net::getNetworkType() == ServerType::TmwAthena && event.getInt("id") == TmwAthena::MATK) { StatusWindow::updateMPBar(mMpBar); @@ -157,54 +154,28 @@ void MiniStatusWindow::event(Event::Channel channel, { if (event.getType() == Event::UpdateStatusEffect) { - int index = event.getInt("index"); - bool newStatus = event.getBool("newStatus"); + const int id = event.getInt("index"); + const bool newStatus = event.getBool("newStatus"); - StatusEffect *effect = StatusEffect::getStatusEffect(index, - newStatus); + auto effect = StatusEffectDB::getStatusEffect(id); + if (!effect) + return; - if (effect) - { - effect->deliverMessage(); - effect->playSFX(); - - AnimatedSprite *sprite = effect->getIcon(); - - if (!sprite) - { - // delete sprite, if necessary - for (unsigned int i = 0; i < mStatusEffectIcons.size();) - if (mStatusEffectIcons[i] == index) - { - mStatusEffectIcons.erase(mStatusEffectIcons.begin() - + i); - miniStatusWindow->eraseIcon(i); - } - else - i++; - } - else - { - // replace sprite or append - bool found = false; - - for (unsigned int i = 0; i < mStatusEffectIcons.size(); - i++) - if (mStatusEffectIcons[i] == index) - { - miniStatusWindow->setIcon(i, sprite); - found = true; - break; - } - - if (!found) - { // add new - int offset = mStatusEffectIcons.size(); - miniStatusWindow->setIcon(offset, sprite); - mStatusEffectIcons.push_back(index); - } - } - } + effect->deliverMessage(newStatus); + effect->playSfx(newStatus); + + Sprite *sprite = newStatus ? effect->getIconSprite() : nullptr; + auto it = std::find_if(mStatusIcons.begin(), mStatusIcons.end(), + [id](const StatusIcon &icon) { + return icon.effectId == id; + }); + + if (!sprite && it != mStatusIcons.end()) + mStatusIcons.erase(it); + else if (sprite && it == mStatusIcons.end()) + mStatusIcons.push_back(StatusIcon{id, std::unique_ptr<Sprite>(sprite)}); + + updateSize(); } } } @@ -226,43 +197,84 @@ void MiniStatusWindow::logic() } */ - for (auto &icon : mIcons) - if (icon) - icon->update(Time::absoluteTimeMs()); + for (auto &icon : mStatusIcons) + icon.sprite->update(Time::deltaTimeMs()); +} + +void MiniStatusWindow::draw(gcn::Graphics *graphics) +{ + drawChildren(graphics); + + drawIcons(static_cast<Graphics*>(graphics)); } void MiniStatusWindow::mouseMoved(gcn::MouseEvent &event) { Popup::mouseMoved(event); - const int x = event.getX(); - const int y = event.getY(); + std::string tooltip1; + std::string tooltip2; if (event.getSource() == mXpBar) { - mTextPopup->show(x + getX(), y + getY(), - strprintf("%u/%u", PlayerInfo::getAttribute(EXP), - PlayerInfo::getAttribute(EXP_NEEDED)), - strprintf("%s: %u", _("Need"), - PlayerInfo::getAttribute(EXP_NEEDED) - - PlayerInfo::getAttribute(EXP))); + const int xp = PlayerInfo::getAttribute(EXP); + const int xpNeeded = PlayerInfo::getAttribute(EXP_NEEDED); + tooltip1 = strprintf("%u/%u", xp, xpNeeded); + tooltip2 = strprintf("%s: %u", _("Need"), xpNeeded - xp); } else if (event.getSource() == mHpBar) { - mTextPopup->show(x + getX(), y + getY(), - strprintf("%u/%u", PlayerInfo::getAttribute(HP), - PlayerInfo::getAttribute(MAX_HP))); + const int hp = PlayerInfo::getAttribute(HP); + const int maxHp = PlayerInfo::getAttribute(MAX_HP); + tooltip1 = strprintf("%u/%u", hp, maxHp); } else if (event.getSource() == mMpBar) { - mTextPopup->show(x + getX(), y + getY(), - strprintf("%u/%u", PlayerInfo::getAttribute(MP), - PlayerInfo::getAttribute(MAX_MP))); + const int mp = PlayerInfo::getAttribute(MP); + const int maxMp = PlayerInfo::getAttribute(MAX_MP); + tooltip1 = strprintf("%u/%u", mp, maxMp); } else { + // Check if the mouse is over one of the status icons + const auto game = Game::instance(); + const int tileWidth = game->getCurrentTileWidth(); + const int tileHeight = game->getCurrentTileHeight(); + + int iconX = mXpBar->getX() + mXpBar->getWidth() + ICON_SPACING + tileWidth / 2; + int iconY = ICON_SPACING + tileHeight; + + for (const auto &icon : mStatusIcons) + { + int spriteX = iconX + icon.sprite->getOffsetX() - icon.sprite->getWidth() / 2; + int spriteY = iconY + icon.sprite->getOffsetY() - icon.sprite->getHeight(); + + if (event.getX() >= spriteX && + event.getX() < spriteX + icon.sprite->getWidth() && + event.getY() >= spriteY && + event.getY() < spriteY + icon.sprite->getHeight()) + { + auto effect = StatusEffectDB::getStatusEffect(icon.effectId); + if (effect) + tooltip1 = effect->name; + break; + } + + iconX += ICON_SPACING + icon.sprite->getWidth(); + } + } + + if (tooltip1.empty()) + { mTextPopup->setVisible(false); } + else + { + mTextPopup->show(event.getX() + getX(), + event.getY() + getY(), + tooltip1, + tooltip2); + } } void MiniStatusWindow::mouseExited(gcn::MouseEvent &event) @@ -271,3 +283,19 @@ void MiniStatusWindow::mouseExited(gcn::MouseEvent &event) mTextPopup->setVisible(false); } + +void MiniStatusWindow::updateSize() +{ + int width = mXpBar->getX() + mXpBar->getWidth(); + int height = mXpBar->getY() + mXpBar->getHeight(); + + // Increase width based on the size of the status icons + if (!mStatusIcons.empty()) + { + width += ICON_SPACING; + for (const auto &icon : mStatusIcons) + width += ICON_SPACING + icon.sprite->getWidth(); + } + + setContentSize(width, height); +} diff --git a/src/gui/ministatuswindow.h b/src/gui/ministatuswindow.h index db9e4c69..21c4b76e 100644 --- a/src/gui/ministatuswindow.h +++ b/src/gui/ministatuswindow.h @@ -19,16 +19,16 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef MINISTATUS_H -#define MINISTATUS_H +#pragma once #include "eventlistener.h" #include "gui/widgets/popup.h" +#include <memory> #include <vector> -class AnimatedSprite; +class Sprite; class Graphics; class ProgressBar; class TextPopup; @@ -42,28 +42,20 @@ class MiniStatusWindow : public Popup, public EventListener { public: MiniStatusWindow(); - - void drawIcons(Graphics *graphics); + ~MiniStatusWindow() override; void event(Event::Channel channel, const Event &event) override; void logic() override; // Updates icons - void draw(gcn::Graphics *graphics) override - { drawChildren(graphics); } + void draw(gcn::Graphics *graphics) override; void mouseMoved(gcn::MouseEvent &mouseEvent) override; void mouseExited(gcn::MouseEvent &event) override; private: - bool isInBar(ProgressBar *bar, int x, int y) const; - - /** - * Sets one of the icons. - */ - void setIcon(int index, AnimatedSprite *sprite); - - void eraseIcon(int index); + void drawIcons(Graphics *graphics); + void updateSize(); /* * Mini Status Bars @@ -73,10 +65,13 @@ class MiniStatusWindow : public Popup, public EventListener ProgressBar *mXpBar; TextPopup *mTextPopup; - std::vector<int> mStatusEffectIcons; - std::vector<AnimatedSprite *> mIcons; + struct StatusIcon + { + int effectId; + std::unique_ptr<Sprite> sprite; + }; + + std::vector<StatusIcon> mStatusIcons; }; extern MiniStatusWindow *miniStatusWindow; - -#endif diff --git a/src/gui/npcdialog.cpp b/src/gui/npcdialog.cpp index 18b3ff1b..16e7db94 100644 --- a/src/gui/npcdialog.cpp +++ b/src/gui/npcdialog.cpp @@ -87,8 +87,8 @@ NpcDialog::NpcDialog(int npcId) // Setup output text box mTextBox = new BrowserBox(BrowserBox::AUTO_WRAP); mTextBox->setWrapIndent(15); - mTextBox->setFrameSize(2); mTextBox->setLinkHandler(mItemLinkHandler.get()); + mTextBox->setEnableKeys(true); mScrollArea = new ScrollArea(mTextBox); mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); @@ -537,16 +537,11 @@ void NpcEventListener::event(Event::Channel channel, else if (event.getType() == Event::Next) { int id = event.getInt("id"); - NpcDialog *dialog = getDialog(id, false); - if (!dialog) - { - int mNpcId = id; - Net::getNpcHandler()->nextDialog(mNpcId); - return; - } - - dialog->showNextButton(); + if (NpcDialog *dialog = getDialog(id, false)) + dialog->showNextButton(); + else + Net::getNpcHandler()->nextDialog(id); } else if (event.getType() == Event::ClearDialog) { @@ -556,32 +551,19 @@ void NpcEventListener::event(Event::Channel channel, else if (event.getType() == Event::Close) { int id = event.getInt("id"); - NpcDialog *dialog = getDialog(id, false); - if (!dialog) - { - int mNpcId = id; - Net::getNpcHandler()->closeDialog(mNpcId); - return; - } - - dialog->showCloseButton(); - } - else if (event.getType() == Event::CloseDialog) - { - if (NpcDialog *dialog = getDialog(event.getInt("id"), false)) - dialog->setVisible(false); + if (NpcDialog *dialog = getDialog(id, false)) + dialog->showCloseButton(); + else + Net::getNpcHandler()->closeDialog(id); } else if (event.getType() == Event::CloseAll) { NpcDialog::closeAll(); } - else if (event.getType() == Event::End) + else if (event.getType() == Event::CloseDialog) { - int id = event.getInt("id"); - NpcDialog *dialog = getDialog(id, false); - - if (dialog) + if (NpcDialog *dialog = getDialog(event.getInt("id"), false)) dialog->close(); } else if (event.getType() == Event::Post) diff --git a/src/gui/npcdialog.h b/src/gui/npcdialog.h index f2b50370..8a18a455 100644 --- a/src/gui/npcdialog.h +++ b/src/gui/npcdialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef NPCDIALOG_H -#define NPCDIALOG_H +#pragma once #include "gui/widgets/window.h" @@ -214,5 +213,3 @@ class NpcDialog final : public Window, NpcInputState mInputState = NPC_INPUT_NONE; NpcActionState mActionState = NPC_ACTION_WAIT; }; - -#endif // NPCDIALOG_H diff --git a/src/gui/npcpostdialog.h b/src/gui/npcpostdialog.h index 61c17ca6..7021b5b4 100644 --- a/src/gui/npcpostdialog.h +++ b/src/gui/npcpostdialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_NPCPOSTDIALOG_H -#define GUI_NPCPOSTDIALOG_H +#pragma once #include "gui/widgets/window.h" @@ -57,5 +56,3 @@ private: TextBox *mText; TextField *mSender; }; - -#endif diff --git a/src/gui/okdialog.h b/src/gui/okdialog.h index fcb327a9..f56f24e2 100644 --- a/src/gui/okdialog.h +++ b/src/gui/okdialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef OK_DIALOG_H -#define OK_DIALOG_H +#pragma once #include "gui/widgets/window.h" @@ -47,5 +46,3 @@ class OkDialog : public Window, public gcn::ActionListener private: TextBox *mTextBox; }; - -#endif // OK_DIALOG_H diff --git a/src/gui/outfitwindow.cpp b/src/gui/outfitwindow.cpp index b5033d70..f914c0cc 100644 --- a/src/gui/outfitwindow.cpp +++ b/src/gui/outfitwindow.cpp @@ -78,8 +78,8 @@ OutfitWindow::~OutfitWindow() void OutfitWindow::load() { - for (int o = 0; o < OUTFITS_COUNT; o++) - memset(mOutfits[o].items, -1, sizeof(mOutfits[o].items)); + for (auto &mOutfit : mOutfits) + memset(mOutfit.items, -1, sizeof(mOutfit.items)); for (auto &outfit : config.outfits) { @@ -89,10 +89,8 @@ void OutfitWindow::load() std::string buf; std::stringstream ss(outfit.items); - for (size_t i = 0; (ss >> buf) && i < OUTFIT_ITEM_COUNT; i++) - { + for (int i = 0; (ss >> buf) && i < OUTFIT_ITEM_COUNT; i++) mOutfits[outfit.index].items[i] = atoi(buf.c_str()); - } mOutfits[outfit.index].unequip = outfit.unequip; } @@ -105,16 +103,15 @@ void OutfitWindow::save() std::string outfitStr; for (int o = 0; o < OUTFITS_COUNT; o++) { - auto &items = mOutfits[o].items; bool emptyOutfit = true; - for (int i = 0; i < OUTFIT_ITEM_COUNT; i++) + for (int item : mOutfits[o].items) { if (!outfitStr.empty()) outfitStr += " "; - outfitStr += items[i] ? toString(items[i]) : toString(-1); - emptyOutfit &= items[i] <= 0; + outfitStr += item ? toString(item) : toString(-1); + emptyOutfit &= item <= 0; } if (!emptyOutfit) @@ -159,10 +156,9 @@ void OutfitWindow::wearOutfit(int outfit) if (mOutfits[outfit].unequip) unequipNotInOutfit(outfit); - Item *item; - for (int i = 0; i < OUTFIT_ITEM_COUNT; i++) + for (int i : mOutfits[outfit].items) { - item = PlayerInfo::getInventory()->findItem(mOutfits[outfit].items[i]); + Item *item = PlayerInfo::getInventory()->findItem(i); if (item && !item->isEquipped() && item->getQuantity()) { if (item->isEquippable()) @@ -174,9 +170,7 @@ void OutfitWindow::wearOutfit(int outfit) void OutfitWindow::copyOutfit(int outfit) { for (int i = 0; i < OUTFIT_ITEM_COUNT; i++) - { mOutfits[mCurrentOutfit].items[i] = mOutfits[outfit].items[i]; - } } void OutfitWindow::draw(gcn::Graphics *graphics) @@ -328,9 +322,9 @@ void OutfitWindow::unequipNotInOutfit(int outfit) if (inventory->getItem(i) && inventory->getItem(i)->isEquipped()) { bool found = false; - for (int f = 0; f < OUTFIT_ITEM_COUNT; f++) + for (int item : mOutfits[outfit].items) { - if (inventory->getItem(i)->getId() == mOutfits[outfit].items[f]) + if (inventory->getItem(i)->getId() == item) { found = true; break; diff --git a/src/gui/outfitwindow.h b/src/gui/outfitwindow.h index 56e96795..10de5321 100644 --- a/src/gui/outfitwindow.h +++ b/src/gui/outfitwindow.h @@ -19,15 +19,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef OUTFITWINDOW_H -#define OUTFITWINDOW_H +#pragma once #include "gui/widgets/window.h" #include <guichan/actionlistener.hpp> -#define OUTFITS_COUNT 15 -#define OUTFIT_ITEM_COUNT 9 +constexpr int OUTFITS_COUNT = 15; +constexpr int OUTFIT_ITEM_COUNT = 9; class Button; class CheckBox; @@ -91,5 +90,3 @@ class OutfitWindow : public Window, gcn::ActionListener }; extern OutfitWindow *outfitWindow; - -#endif diff --git a/src/gui/palette.cpp b/src/gui/palette.cpp index 6c6e6f06..948660d2 100644 --- a/src/gui/palette.cpp +++ b/src/gui/palette.cpp @@ -25,7 +25,6 @@ #include <cmath> static constexpr double PI = 3.14159265; -const gcn::Color Palette::BLACK = gcn::Color(0, 0, 0); Timer Palette::mRainbowTimer; Palette::Palettes Palette::mInstances; @@ -47,23 +46,37 @@ Palette::Palette(int size) : mInstances.insert(this); } +Palette::Palette(Palette &&pal) + : mColors(std::move(pal.mColors)) + , mGradVector(std::move(pal.mGradVector)) +{ + mInstances.insert(this); +} + Palette::~Palette() { mInstances.erase(this); } -const gcn::Color &Palette::getColor(char c, bool &valid) - { - for (const auto &color : mColors) +Palette &Palette::operator=(Palette &&pal) +{ + if (this != &pal) { - if (color.ch == c) - { - valid = true; - return color.color; - } + mColors = std::move(pal.mColors); + mGradVector = std::move(pal.mGradVector); } - valid = false; - return BLACK; + return *this; +} + +void Palette::setColor(int type, + const gcn::Color &color, + const std::optional<gcn::Color> &outlineColor, + GradientType grad, + int delay) +{ + auto &elem = mColors[type]; + elem.set(type, color, grad, delay); + elem.outlineColor = outlineColor; } void Palette::advanceGradients() @@ -97,17 +110,19 @@ void Palette::advanceGradient(int advance) const int pos = elem->gradientIndex % delay; const int colIndex = elem->gradientIndex / delay; - if (elem->grad == PULSE) - { + switch (elem->grad) { + case STATIC: + break; + case PULSE: { const int colVal = (int) (255.0 * sin(PI * colIndex / numOfColors)); const gcn::Color &col = elem->testColor; elem->color.r = ((colVal * col.r) / 255) % (col.r + 1); elem->color.g = ((colVal * col.g) / 255) % (col.g + 1); elem->color.b = ((colVal * col.b) / 255) % (col.b + 1); + break; } - if (elem->grad == SPECTRUM) - { + case SPECTRUM: { int colVal; if (colIndex % 2) @@ -129,9 +144,9 @@ void Palette::advanceGradient(int advance) elem->color.b = (colIndex == 3 || colIndex == 4) ? 255 : (colIndex == 2 || colIndex == 5) ? colVal : 0; + break; } - else if (elem->grad == RAINBOW) - { + case RAINBOW: { const gcn::Color &startCol = RAINBOW_COLORS[colIndex]; const gcn::Color &destCol = RAINBOW_COLORS[(colIndex + 1) % numOfColors]; @@ -147,6 +162,8 @@ void Palette::advanceGradient(int advance) elem->color.b =(int)(startColVal * startCol.b + destColVal * destCol.b); + break; + } } } } diff --git a/src/gui/palette.h b/src/gui/palette.h index 9de911d5..268b9fc6 100644 --- a/src/gui/palette.h +++ b/src/gui/palette.h @@ -20,16 +20,16 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PALETTE_H -#define PALETTE_H +#pragma once #include "utils/time.h" #include <guichan/color.hpp> #include <cstdlib> -#include <string> +#include <optional> #include <set> +#include <string> #include <vector> // Default Gradient Delay @@ -41,8 +41,13 @@ constexpr int GRADIENT_DELAY = 40; class Palette { public: - /** Black Color Constant */ - static const gcn::Color BLACK; + Palette(int size); + Palette(const Palette &) = delete; + Palette(Palette &&); + ~Palette(); + + Palette &operator=(const Palette &) = delete; + Palette &operator=(Palette &&); /** Colors can be static or can alter over time. */ enum GradientType { @@ -52,62 +57,49 @@ class Palette RAINBOW }; - /** - * Returns the color associated with a character, if it exists. Returns - * Palette::BLACK if the character is not found. - * - * @param c character requested - * @param valid indicate whether character is known - * - * @return the requested color or Palette::BLACK - */ - const gcn::Color &getColor(char c, bool &valid); + void setColor(int type, + const gcn::Color &color, + const std::optional<gcn::Color> &outlineColor, + GradientType grad, + int delay); /** - * Gets the color associated with the type. Sets the alpha channel - * before returning. + * Gets the color associated with the type. * * @param type the color type requested - * @param alpha alpha channel to use - * * @return the requested color */ - const gcn::Color &getColor(int type, int alpha = 255) + const gcn::Color &getColor(int type) const { - gcn::Color &col = mColors[type].color; - col.a = alpha; - return col; + return mColors[type].color; } /** - * Gets the GradientType associated with the specified type. + * Gets the optional outline color associated with the type. * - * @param type the color type of the color - * - * @return the gradient type of the color with the given index + * @param type the color type requested + * @return the requested outline color, if any */ - GradientType getGradientType(int type) const + const std::optional<gcn::Color> &getOutlineColor(int type) const { - return mColors[type].grad; + return mColors[type].outlineColor; } /** - * Get the character used by the specified color. + * Gets the GradientType associated with the specified type. * * @param type the color type of the color - * - * @return the color char of the color with the given index + * @return the gradient type of the color with the given index */ - char getColorChar(int type) const + GradientType getGradientType(int type) const { - return mColors[type].ch; + return mColors[type].grad; } /** * Gets the gradient delay for the specified type. * * @param type the color type of the color - * * @return the gradient delay of the color with the given index */ int getGradientDelay(int type) const @@ -129,10 +121,6 @@ class Palette using Palettes = std::set<Palette *>; static Palettes mInstances; - Palette(int size); - - ~Palette(); - void advanceGradient(int advance); struct ColorElem @@ -141,8 +129,8 @@ class Palette gcn::Color color; gcn::Color testColor; gcn::Color committedColor; + std::optional<gcn::Color> outlineColor; std::string text; - char ch; GradientType grad; GradientType committedGrad; int gradientIndex; @@ -169,5 +157,3 @@ class Palette std::vector<ColorElem> mColors; std::vector<ColorElem*> mGradVector; }; - -#endif diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index b1165f27..4bafc074 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -91,12 +91,12 @@ void PopupMenu::showPopup(int x, int y, Being *being) switch (player_relations.getRelation(name)) { - case PlayerRelation::NEUTRAL: + case PlayerRelation::Neutral: mBrowserBox->addRow(strprintf("@@friend|%s@@", strprintf(_("Befriend %s"), name.c_str()).c_str())); - case PlayerRelation::FRIEND: + case PlayerRelation::Friend: mBrowserBox->addRow(strprintf("@@disregard|%s@@", strprintf(_("Disregard %s"), name.c_str()).c_str())); @@ -105,7 +105,7 @@ void PopupMenu::showPopup(int x, int y, Being *being) name.c_str()).c_str())); break; - case PlayerRelation::DISREGARDED: + case PlayerRelation::Disregarded: mBrowserBox->addRow(strprintf("@@unignore|%s@@", strprintf(_("Unignore %s"), name.c_str()).c_str())); @@ -114,7 +114,7 @@ void PopupMenu::showPopup(int x, int y, Being *being) name.c_str()).c_str())); break; - case PlayerRelation::IGNORED: + case PlayerRelation::Ignored: mBrowserBox->addRow(strprintf("@@unignore|%s@@", strprintf(_("Unignore %s"), name.c_str()).c_str())); @@ -126,7 +126,7 @@ void PopupMenu::showPopup(int x, int y, Being *being) strprintf(_("Invite %s to join your guild"), name.c_str()).c_str())); if (local_player->isInParty() || - Net::getNetworkType() == ServerType::MANASERV) + Net::getNetworkType() == ServerType::ManaServ) { mBrowserBox->addRow(strprintf("@@party|%s@@", strprintf(_("Invite %s to join your party"), @@ -223,25 +223,25 @@ void PopupMenu::handleLink(const std::string &link) else if (link == "unignore" && being && being->getType() == ActorSprite::PLAYER) { - player_relations.setRelation(being->getName(), PlayerRelation::NEUTRAL); + player_relations.setRelation(being->getName(), PlayerRelation::Neutral); } else if (link == "ignore" && being && being->getType() == ActorSprite::PLAYER) { - player_relations.setRelation(being->getName(), PlayerRelation::IGNORED); + player_relations.setRelation(being->getName(), PlayerRelation::Ignored); } else if (link == "disregard" && being && being->getType() == ActorSprite::PLAYER) { - player_relations.setRelation(being->getName(), PlayerRelation::DISREGARDED); + player_relations.setRelation(being->getName(), PlayerRelation::Disregarded); } else if (link == "friend" && being && being->getType() == ActorSprite::PLAYER) { - player_relations.setRelation(being->getName(), PlayerRelation::FRIEND); + player_relations.setRelation(being->getName(), PlayerRelation::Friend); } // Guild action else if (link == "guild" && being && @@ -249,18 +249,15 @@ void PopupMenu::handleLink(const std::string &link) { local_player->inviteToGuild(being); } - // Pick Up Floor Item action else if ((link == "pickup") && mFloorItem) { local_player->pickUp(mFloorItem); } - // Look To action else if (link == "look") { } - else if (link == "activate" || link == "equip" || link == "unequip") { assert(mItem); @@ -288,49 +285,36 @@ void PopupMenu::handleLink(const std::string &link) else if (mFloorItem) chatWindow->addItemText(mFloorItem->getInfo().name); } - - else if (link == "split") - { - ItemAmountWindow::showWindow(ItemAmountWindow::ItemSplit, - inventoryWindow, mItem); - } - else if (link == "drop") { ItemAmountWindow::showWindow(ItemAmountWindow::ItemDrop, inventoryWindow, mItem); } - else if (link == "store") { ItemAmountWindow::showWindow(ItemAmountWindow::StoreAdd, inventoryWindow, mItem); } - else if (link == "retrieve") { ItemAmountWindow::showWindow(ItemAmountWindow::StoreRemove, mWindow, mItem); } - else if (link == "party" && being && being->getType() == ActorSprite::PLAYER) { Net::getPartyHandler()->invite(being); } - else if (link == "name" && being) { const std::string &name = being->getName(); chatWindow->addInputText(name); } - else if (link == "admin-kick" && being && being->getType() == ActorSprite::PLAYER) { Net::getAdminHandler()->kick(being->getName()); } - // Unknown actions else if (link != "cancel") { @@ -376,11 +360,6 @@ void PopupMenu::showPopup(Window *parent, int x, int y, Item *item, else mBrowserBox->addRow(strprintf("@@drop|%s@@", _("Drop"))); } - - if (Net::getInventoryHandler()->canSplit(item)) - { - mBrowserBox->addRow(strprintf("@@split|%s@@", _("Split"))); - } } // Assume in storage for now // TODO: make this whole system more flexible, if needed diff --git a/src/gui/popupmenu.h b/src/gui/popupmenu.h index c7199b78..5a5a88ee 100644 --- a/src/gui/popupmenu.h +++ b/src/gui/popupmenu.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef POPUP_MENU_H -#define POPUP_MENU_H +#pragma once #include "gui/widgets/linkhandler.h" #include "gui/widgets/popup.h" @@ -76,5 +75,3 @@ class PopupMenu : public Popup, public LinkHandler */ void showPopup(int x, int y); }; - -#endif diff --git a/src/gui/quitdialog.cpp b/src/gui/quitdialog.cpp index 4afbd419..51831c4c 100644 --- a/src/gui/quitdialog.cpp +++ b/src/gui/quitdialog.cpp @@ -71,7 +71,8 @@ QuitDialog::QuitDialog(QuitDialog** pointerToMe): placeOption(place, mSwitchAccountServer); // Only added if we are connected to a gameserver - if (state == STATE_GAME) placeOption(place, mSwitchCharacter); + if (state == STATE_GAME) + placeOption(place, mSwitchCharacter); } mOptions[0]->setSelected(true); @@ -90,7 +91,9 @@ QuitDialog::QuitDialog(QuitDialog** pointerToMe): QuitDialog::~QuitDialog() { - if (mMyPointer) *mMyPointer = nullptr; + if (mMyPointer) + *mMyPointer = nullptr; + // Optional widgets, so delete them by hand. delete mForceQuit; delete mLogoutQuit; @@ -166,7 +169,8 @@ void QuitDialog::keyPressed(gcn::KeyEvent &keyEvent) mOptions[0]->setSelected(true); return; } - else if (it == mOptions.begin() && dir < 0) + + if (it == mOptions.begin() && dir < 0) it = mOptions.end(); it += dir; diff --git a/src/gui/quitdialog.h b/src/gui/quitdialog.h index d62d5c51..4672e45f 100644 --- a/src/gui/quitdialog.h +++ b/src/gui/quitdialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef QUITDIALOG_H -#define QUITDIALOG_H +#pragma once #include "gui/widgets/window.h" @@ -69,5 +68,3 @@ class QuitDialog : public Window, public gcn::ActionListener, QuitDialog **mMyPointer; }; - -#endif diff --git a/src/gui/recorder.cpp b/src/gui/recorder.cpp index 8dd0f8ed..894e3631 100644 --- a/src/gui/recorder.cpp +++ b/src/gui/recorder.cpp @@ -32,15 +32,16 @@ #include "utils/gettext.h" #include "utils/stringutils.h" -Recorder::Recorder(ChatWindow *chat, const std::string &title, - const std::string &buttonTxt) : - Window(title) +Recorder::Recorder(ChatWindow *chat, + const std::string &title, + const std::string &buttonTxt) + : Window(title) + , mChat(chat) { setWindowName("Recorder"); const int offsetX = 2 * getPadding() + 10; const int offsetY = getTitleBarHeight() + getPadding() + 10; - mChat = chat; auto *button = new Button(buttonTxt, "activate", this); // 123 is the default chat window height. If you change this in Chat, please @@ -56,16 +57,12 @@ Recorder::Recorder(ChatWindow *chat, const std::string &title, loadWindowState(); } -Recorder::~Recorder() -{ -} +Recorder::~Recorder() = default; void Recorder::record(const std::string &msg) { if (mStream.is_open()) - { mStream << msg << std::endl; - } } void Recorder::setRecordingFile(const std::string &msg) diff --git a/src/gui/recorder.h b/src/gui/recorder.h index efd73021..8a84f423 100644 --- a/src/gui/recorder.h +++ b/src/gui/recorder.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef RECORD_H -#define RECORD_H +#pragma once #include "gui/widgets/window.h" @@ -72,5 +71,3 @@ class Recorder : public Window, public gcn::ActionListener std::ofstream mStream; }; - -#endif diff --git a/src/gui/register.cpp b/src/gui/register.cpp index d4ebb59c..62114c10 100644 --- a/src/gui/register.cpp +++ b/src/gui/register.cpp @@ -22,14 +22,12 @@ #include "gui/register.h" #include "client.h" -#include "configuration.h" #include "log.h" #include "gui/logindialog.h" #include "gui/okdialog.h" #include "gui/widgets/button.h" -#include "gui/widgets/checkbox.h" #include "gui/widgets/label.h" #include "gui/widgets/layout.h" #include "gui/widgets/passwordfield.h" @@ -227,7 +225,7 @@ void RegisterDialog::action(const gcn::ActionEvent &event) mLoginData->password = mPasswordField->getText(); if (mFemaleButton) mLoginData->gender = mFemaleButton->isSelected() ? - Gender::FEMALE : Gender::MALE; + Gender::Female : Gender::Male; if (mEmailField) mLoginData->email = mEmailField->getText(); mLoginData->registerLogin = true; diff --git a/src/gui/register.h b/src/gui/register.h index 1996cd47..ae9ee582 100644 --- a/src/gui/register.h +++ b/src/gui/register.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef REGISTER_H -#define REGISTER_H +#pragma once #include "gui/widgets/window.h" @@ -94,5 +93,3 @@ class RegisterDialog : public Window, public gcn::ActionListener, LoginData *mLoginData; }; - -#endif diff --git a/src/gui/sdlinput.h b/src/gui/sdlinput.h index a29f17ca..599aafe7 100644 --- a/src/gui/sdlinput.h +++ b/src/gui/sdlinput.h @@ -56,8 +56,7 @@ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SDLINPUT_H -#define SDLINPUT_H +#pragma once #include <queue> @@ -204,5 +203,3 @@ protected: bool mMouseDown = false; }; - -#endif diff --git a/src/gui/selldialog.cpp b/src/gui/selldialog.cpp index 5f499982..4aeacd6f 100644 --- a/src/gui/selldialog.cpp +++ b/src/gui/selldialog.cpp @@ -205,7 +205,7 @@ void SellDialog::action(const gcn::ActionEvent &event) sellCount = item->sellCurrentDuplicate(mAmountItems); // For Manaserv, the Item id is to be given as index. - if ((Net::getNetworkType() == ServerType::MANASERV)) + if ((Net::getNetworkType() == ServerType::ManaServ)) itemIndex = item->getId(); Net::getNpcHandler()->sellItem(mNpcId, itemIndex, sellCount); diff --git a/src/gui/selldialog.h b/src/gui/selldialog.h index d59343cb..165d9cc8 100644 --- a/src/gui/selldialog.h +++ b/src/gui/selldialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SELL_H -#define SELL_H +#pragma once #include "gui/widgets/window.h" @@ -113,5 +112,3 @@ class SellDialog : public Window, gcn::ActionListener, gcn::SelectionListener int mMaxItems = 0; int mAmountItems = 0; }; - -#endif diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp index 01b477da..f5418625 100644 --- a/src/gui/serverdialog.cpp +++ b/src/gui/serverdialog.cpp @@ -32,26 +32,21 @@ #include "gui/sdlinput.h" #include "gui/widgets/button.h" -#include "gui/widgets/dropdown.h" #include "gui/widgets/label.h" #include "gui/widgets/layout.h" #include "gui/widgets/listbox.h" #include "gui/widgets/scrollarea.h" -#include "gui/widgets/textfield.h" #include "resources/theme.h" #include "utils/gettext.h" #include "utils/stringutils.h" -#include "utils/xml.h" #include <guichan/font.hpp> #include <cstdlib> #include <string> -static const int MAX_SERVERLIST = 6; - ServersListModel::ServersListModel(ServerInfos *servers, ServerDialog *parent): mServers(servers), mVersionStrings(servers->size(), VersionString(0, std::string())), @@ -61,13 +56,11 @@ ServersListModel::ServersListModel(ServerInfos *servers, ServerDialog *parent): int ServersListModel::getNumberOfElements() { - MutexLocker lock(mParent->getMutex()); return mServers->size(); } std::string ServersListModel::getElementAt(int elementIndex) { - MutexLocker lock(mParent->getMutex()); const ServerInfo &server = mServers->at(elementIndex); std::string myServer; myServer += server.hostname; @@ -102,27 +95,25 @@ public: auto *model = static_cast<ServersListModel*>(mListModel); - updateAlpha(); - - graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, - (int) (mAlpha * 255.0f))); graphics->setFont(getFont()); const int height = getRowHeight(); - const gcn::Color unsupported = - Theme::getThemeColor(Theme::SERVER_VERSION_NOT_SUPPORTED, - (int) (mAlpha * 255.0f)); // Draw filled rectangle around the selected list element if (mSelected >= 0) + { + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + highlightColor.a = gui->getTheme()->getGuiAlpha(); + graphics->setColor(highlightColor); graphics->fillRectangle(gcn::Rectangle(0, height * mSelected, getWidth(), height)); + } // Draw the list elements for (int i = 0, y = 0; i < model->getNumberOfElements(); ++i, y += height) { - ServerInfo info = model->getServer(i); + const ServerInfo &info = model->getServer(i); graphics->setColor(Theme::getThemeColor(Theme::TEXT)); @@ -140,8 +131,9 @@ public: if (info.version.first > 0) { - graphics->setColor(unsupported); - + auto unsupportedColor = Theme::getThemeColor(Theme::SERVER_VERSION_NOT_SUPPORTED); + unsupportedColor.a = gui->getTheme()->getGuiAlpha(); + graphics->setColor(unsupportedColor); graphics->drawText(info.version.second, getWidth() - info.version.first - 2, top); } @@ -164,9 +156,8 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir): loadCustomServers(); - mServersListModel = new ServersListModel(&mServers, this); - - mServersList = new ServersListBox(mServersListModel); + mServersListModel = std::make_unique<ServersListModel>(&mServers, this); + mServersList = new ServersListBox(mServersListModel.get()); auto *usedScroll = new ScrollArea(mServersList); usedScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); @@ -185,8 +176,8 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir): usedScroll->setVerticalScrollAmount(0); place(0, 0, usedScroll, 6, 5).setPadding(3); - place(0, 5, mDescription, 5); - place(0, 6, mDownloadText, 5); + place(0, 5, mDescription, 6); + place(0, 6, mDownloadText, 6); place(0, 7, mManualEntryButton); place(1, 7, mModifyButton); place(2, 7, mDeleteButton); @@ -226,16 +217,7 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir): downloadServerList(); } -ServerDialog::~ServerDialog() -{ - if (mDownload) - { - mDownload->cancel(); - delete mDownload; - mDownload = nullptr; - } - delete mServersListModel; -} +ServerDialog::~ServerDialog() = default; void ServerDialog::action(const gcn::ActionEvent &event) { @@ -251,7 +233,7 @@ void ServerDialog::action(const gcn::ActionEvent &event) // Check login if (index < 0 #ifndef MANASERV_SUPPORT - || mServersListModel->getServer(index).type == ServerType::MANASERV + || mServersListModel->getServer(index).type == ServerType::ManaServ #endif ) { @@ -262,6 +244,7 @@ void ServerDialog::action(const gcn::ActionEvent &event) else { mDownload->cancel(); + mQuitButton->setEnabled(false); mConnectButton->setEnabled(false); mDeleteButton->setEnabled(false); @@ -345,7 +328,6 @@ void ServerDialog::valueChanged(const gcn::SelectionEvent &) // Update the server and post fields according to the new selection const ServerInfo &myServer = mServersListModel->getServer(index); mDescription->setCaption(myServer.description); - mDeleteButton->setEnabled(myServer.save); mModifyButton->setEnabled(myServer.save); } @@ -362,36 +344,42 @@ void ServerDialog::mouseClicked(gcn::MouseEvent &mouseEvent) void ServerDialog::logic() { - { - MutexLocker lock(&mMutex); - if (mDownloadStatus == DOWNLOADING_COMPLETE) - { - mDownloadStatus = DOWNLOADING_OVER; + Window::logic(); - mDescription->setCaption(mServers[0].description); - mDownloadText->setCaption(std::string()); - } - else if (mDownloadStatus == DOWNLOADING_IN_PROGRESS) - { - mDownloadText->setCaption(strprintf(_("Downloading server list..." - "%2.2f%%"), - mDownloadProgress * 100)); - } - else if (mDownloadStatus == DOWNLOADING_IDLE) - { - mDownloadText->setCaption(_("Waiting for server...")); - } - else if (mDownloadStatus == DOWNLOADING_PREPARING) + if (mDownloadDone) + return; + + auto state = mDownload->getState(); + + switch (state.status) { + case DownloadStatus::InProgress: + mDownloadText->setCaption(strprintf(_("Downloading server list..." + "%2.0f%%"), + state.progress * 100)); + break; + + case DownloadStatus::Canceled: + case DownloadStatus::Error: + mDownloadDone = true; + logger->log("Error retrieving server list: %s", mDownload->getError()); + mDownloadText->setCaption(_("Error retrieving server list!")); + break; + + case DownloadStatus::Complete: + mDownloadDone = true; + loadServers(); + + if (mServers.empty()) { - mDownloadText->setCaption(_("Preparing download")); + mDownloadText->setCaption(_("No servers found!")); } - else if (mDownloadStatus == DOWNLOADING_ERROR) + else { - mDownloadText->setCaption(_("Error retreiving server list!")); + mDownloadText->setCaption(std::string()); + mDescription->setCaption(mServers[0].description); } + break; } - - Window::logic(); } void ServerDialog::downloadServerList() @@ -406,7 +394,7 @@ void ServerDialog::downloadServerList() if (listFile.empty()) listFile = "https://www.manasource.org/serverlist.xml"; - mDownload = new Net::Download(this, listFile, &downloadUpdate); + mDownload = std::make_unique<Net::Download>(listFile); mDownload->setFile(mDir + "/serverlist.xml"); mDownload->start(); } @@ -432,102 +420,99 @@ void ServerDialog::loadServers() for (auto serverNode : rootNode.children()) { - if (serverNode.name() != "server") - continue; + if (serverNode.name() == "server") + loadServer(serverNode); + } +} - ServerInfo server; +void ServerDialog::loadServer(XML::Node serverNode) +{ + ServerInfo server; - std::string type = serverNode.getProperty("type", "unknown"); + std::string type = serverNode.getProperty("type", "unknown"); - server.type = ServerInfo::parseType(type); + server.type = ServerInfo::parseType(type); - // Ignore unknown server types - if (server.type == ServerType::UNKNOWN + // Ignore unknown server types + if (server.type == ServerType::Unknown #ifndef MANASERV_SUPPORT - || server.type == ServerType::MANASERV + || server.type == ServerType::ManaServ #endif ) - { - logger->log("Ignoring server entry with unknown type: %s", - type.c_str()); - continue; - } + { + logger->log("Ignoring server entry with unknown type: %s", + type.c_str()); + return; + } - server.name = serverNode.getProperty("name", std::string()); + server.name = serverNode.getProperty("name", std::string()); - std::string version = serverNode.getProperty("minimumVersion", - std::string()); + std::string version = serverNode.getProperty("minimumVersion", + std::string()); - bool meetsMinimumVersion = compareStrI(version, PACKAGE_VERSION) <= 0; + bool meetsMinimumVersion = strcmp(version.c_str(), PACKAGE_VERSION) <= 0; - // For display in the list - if (meetsMinimumVersion) - version.clear(); - else if (version.empty()) - version = _("requires a newer version"); - else - version = strprintf(_("requires v%s"), version.c_str()); + // For display in the list + if (meetsMinimumVersion) + version.clear(); + else if (version.empty()) + version = _("requires a newer version"); + else + version = strprintf(_("requires v%s"), version.c_str()); - for (auto subNode : serverNode.children()) + for (auto subNode : serverNode.children()) + { + if (subNode.name() == "connection") { - if (subNode.name() == "connection") + server.hostname = subNode.getProperty("hostname", std::string()); + server.port = subNode.getProperty("port", 0); + if (server.port == 0) { - server.hostname = subNode.getProperty("hostname", std::string()); - server.port = subNode.getProperty("port", 0); - if (server.port == 0) - { - // If no port is given, use the default for the given type - server.port = ServerInfo::defaultPortForServerType(server.type); - } - } - else if (subNode.name() == "description") - { - server.description = subNode.textContent(); - } - else if (subNode.name() == "persistentIp") - { - const auto text = subNode.textContent(); - server.persistentIp = text == "1" || text == "true"; + // If no port is given, use the default for the given type + server.port = ServerInfo::defaultPortForServerType(server.type); } } + else if (subNode.name() == "description") + { + server.description = subNode.textContent(); + } + else if (subNode.name() == "persistentIp") + { + const auto text = subNode.textContent(); + server.persistentIp = text == "1" || text == "true"; + } + } - server.version.first = gui->getFont()->getWidth(version); - server.version.second = version; + server.version.first = gui->getFont()->getWidth(version); + server.version.second = version; - MutexLocker lock(&mMutex); - // Add the server to the local list if it's not already present - bool found = false; - int i = 0; - for (auto &s : mServers) + // Add the server to the local list if it's not already present + bool found = false; + int i = 0; + for (auto &s : mServers) + { + if (s == server) { - if (s == server) - { - // Use the name listed in the server list - s.name = server.name; - s.version = server.version; - s.description = server.description; - mServersListModel->setVersionString(i, version); - found = true; - break; - } - ++i; + // Use the name listed in the server list + s.name = server.name; + s.version = server.version; + s.description = server.description; + mServersListModel->setVersionString(i, version); + found = true; + break; } - - if (!found) - mServers.push_back(server); + ++i; } + + if (!found) + mServers.push_back(server); } void ServerDialog::loadCustomServers() { for (auto &server : config.servers) - { if (server.isValid()) - { - server.save = true; mServers.push_back(server); - } - } } void ServerDialog::saveCustomServers(const ServerInfo ¤tServer, int index) @@ -558,51 +543,6 @@ void ServerDialog::saveCustomServers(const ServerInfo ¤tServer, int index) // Restore the correct description if (index < 0) index = 0; - mDescription->setCaption(mServers[index].description); -} - -int ServerDialog::downloadUpdate(void *ptr, DownloadStatus status, - size_t total, size_t remaining) -{ - if (status == DOWNLOAD_STATUS_CANCELLED) - return -1; - - auto *sd = reinterpret_cast<ServerDialog*>(ptr); - bool finished = false; - - if (status == DOWNLOAD_STATUS_COMPLETE) - { - finished = true; - } - else if (status < 0) - { - logger->log("Error retreiving server list: %s", - sd->mDownload->getError()); - sd->mDownloadStatus = DOWNLOADING_ERROR; - } - else - { - float progress = (float) remaining / total; - - if (progress != progress) - progress = 0.0f; // check for NaN - else if (progress < 0.0f) - progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway. - else if (progress > 1.0f) - progress = 1.0f; - - MutexLocker lock(&sd->mMutex); - sd->mDownloadStatus = DOWNLOADING_IN_PROGRESS; - sd->mDownloadProgress = progress; - } - - if (finished) - { - sd->loadServers(); - - MutexLocker lock(&sd->mMutex); - sd->mDownloadStatus = DOWNLOADING_COMPLETE; - } - - return 0; + if (static_cast<size_t>(index) < mServers.size()) + mDescription->setCaption(mServers[index].description); } diff --git a/src/gui/serverdialog.h b/src/gui/serverdialog.h index 4db36462..cc2725be 100644 --- a/src/gui/serverdialog.h +++ b/src/gui/serverdialog.h @@ -19,21 +19,20 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SERVERDIALOG_H -#define SERVERDIALOG_H +#pragma once #include "gui/widgets/window.h" #include "net/download.h" #include "net/serverinfo.h" - -#include "utils/mutex.h" +#include "utils/xml.h" #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> #include <guichan/listmodel.hpp> #include <guichan/selectionlistener.hpp> +#include <memory> #include <string> #include <vector> @@ -90,7 +89,6 @@ class ServerDialog : public Window, { public: ServerDialog(ServerInfo *serverInfo, const std::string &dir); - ~ServerDialog() override; /** @@ -110,10 +108,8 @@ class ServerDialog : public Window, void logic() override; protected: - friend class ServersListModel; - Mutex *getMutex() { return &mMutex; } - friend class CustomServerDialog; + /** * Saves the new server entry in the custom server list. * Removes the given entry when the serverInfo is empty. @@ -128,12 +124,10 @@ class ServerDialog : public Window, */ void downloadServerList(); void loadServers(); + void loadServer(XML::Node serverNode); void loadCustomServers(); - static int downloadUpdate(void *ptr, DownloadStatus status, - size_t total, size_t remaining); - Label *mDescription; Button *mQuitButton; Button *mConnectButton; @@ -142,31 +136,13 @@ class ServerDialog : public Window, Button *mDeleteButton; ListBox *mServersList; - ServersListModel *mServersListModel; + std::unique_ptr<ServersListModel> mServersListModel; const std::string &mDir; - enum ServerDialogDownloadStatus - { - DOWNLOADING_ERROR, - DOWNLOADING_PREPARING, - DOWNLOADING_IDLE, - DOWNLOADING_IN_PROGRESS, - DOWNLOADING_COMPLETE, - DOWNLOADING_OVER - }; - - /** Status of the current download. */ - ServerDialogDownloadStatus mDownloadStatus = DOWNLOADING_PREPARING; - - Net::Download *mDownload = nullptr; + std::unique_ptr<Net::Download> mDownload; + bool mDownloadDone = false; Label *mDownloadText; - - Mutex mMutex; - float mDownloadProgress = -1.0f; - ServerInfos mServers; ServerInfo *mServerInfo; }; - -#endif diff --git a/src/gui/setup.h b/src/gui/setup.h index d510267d..ec45f93b 100644 --- a/src/gui/setup.h +++ b/src/gui/setup.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SETUP_H -#define SETUP_H +#pragma once #include "gui/widgets/window.h" @@ -72,5 +71,3 @@ class Setup : public Window, public gcn::ActionListener }; extern Setup* setupWindow; - -#endif diff --git a/src/gui/setup_audio.cpp b/src/gui/setup_audio.cpp index f51bfcb6..43b132d8 100644 --- a/src/gui/setup_audio.cpp +++ b/src/gui/setup_audio.cpp @@ -29,6 +29,7 @@ #include "gui/widgets/checkbox.h" #include "gui/widgets/label.h" +#include "gui/widgets/layout.h" #include "gui/widgets/slider.h" #include "utils/gettext.h" @@ -71,11 +72,11 @@ Setup_Audio::Setup_Audio(): // Do the layout place(0, 0, mSoundCheckBox); - place(0, 1, mSfxSlider); + place(0, 1, mSfxSlider).setVAlign(LayoutCell::CENTER); place(1, 1, sfxLabel); - place(0, 2, mNotificationsSlider); + place(0, 2, mNotificationsSlider).setVAlign(LayoutCell::CENTER); place(1, 2, notificationsLabel); - place(0, 3, mMusicSlider); + place(0, 3, mMusicSlider).setVAlign(LayoutCell::CENTER); place(1, 3, musicLabel); place(0, 4, mDownloadMusicCheckBox); } diff --git a/src/gui/setup_audio.h b/src/gui/setup_audio.h index 4ee277ef..c19b38b6 100644 --- a/src/gui/setup_audio.h +++ b/src/gui/setup_audio.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_SETUP_AUDIO_H -#define GUI_SETUP_AUDIO_H +#pragma once #include "guichanfwd.h" @@ -51,5 +50,3 @@ class Setup_Audio : public SetupTab, public gcn::ActionListener gcn::Slider *mNotificationsSlider; gcn::Slider *mMusicSlider; }; - -#endif // GUI_SETUP_AUDIO_H diff --git a/src/gui/setup_colors.cpp b/src/gui/setup_colors.cpp index 880f6f8a..89eb668e 100644 --- a/src/gui/setup_colors.cpp +++ b/src/gui/setup_colors.cpp @@ -73,14 +73,21 @@ Setup_Colors::Setup_Colors() : mGradTypeText = new Label; - std::string longText = _("Static"); - - if (getFont()->getWidth(_("Pulse")) > getFont()->getWidth(longText)) - longText = _("Pulse"); - if (getFont()->getWidth(_("Rainbow")) > getFont()->getWidth(longText)) - longText = _("Rainbow"); - if (getFont()->getWidth(_("Spectrum")) > getFont()->getWidth(longText)) - longText = _("Spectrum"); + // Initialize with widest label for layout purposes + const char *longText = _("Static"); + int longWidth = getFont()->getWidth(longText); + + auto maybeLonger = [&] (const char *text) { + const int width = getFont()->getWidth(text); + if (width > longWidth) + { + longText = text; + longWidth = width; + } + }; + maybeLonger(_("Pulse")); + maybeLonger(_("Rainbow")); + maybeLonger(_("Spectrum")); mGradTypeText->setCaption(longText); @@ -150,19 +157,19 @@ Setup_Colors::Setup_Colors() : place(0, 0, mScroll, 6, 6).setPadding(2); place(0, 6, mPreviewBox, 6).setPadding(2); place(0, 7, mGradTypeLabel, 3); - place(3, 7, mGradTypeSlider); + place(3, 7, mGradTypeSlider).setVAlign(Layout::CENTER); place(4, 7, mGradTypeText, 2).setPadding(1); place(0, 8, mRedLabel, 3); - place(3, 8, mRedSlider); + place(3, 8, mRedSlider).setVAlign(Layout::CENTER); place(5, 8, mRedText).setPadding(1); place(0, 9, mGreenLabel, 3); - place(3, 9, mGreenSlider); + place(3, 9, mGreenSlider).setVAlign(Layout::CENTER); place(5, 9, mGreenText).setPadding(1); place(0, 10, mBlueLabel, 3); - place(3, 10, mBlueSlider); + place(3, 10, mBlueSlider).setVAlign(Layout::CENTER); place(5, 10, mBlueText).setPadding(1); place(0, 11, mGradDelayLabel, 3); - place(3, 11, mGradDelaySlider); + place(3, 11, mGradDelaySlider).setVAlign(Layout::CENTER); place(5, 11, mGradDelayText).setPadding(1); mGradTypeText->setCaption(std::string()); @@ -214,10 +221,10 @@ void Setup_Colors::action(const gcn::ActionEvent &event) } } -void Setup_Colors::valueChanged(const gcn::SelectionEvent &event) +void Setup_Colors::valueChanged(const gcn::SelectionEvent &) { mSelected = mColorBox->getSelected(); - int type = userPalette->getColorTypeAt(mSelected); + const int type = userPalette->getColorTypeAt(mSelected); const gcn::Color *col = &userPalette->getColor(type); Palette::GradientType grad = userPalette->getGradientType(type); const int delay = userPalette->getGradientDelay(type); @@ -226,11 +233,7 @@ void Setup_Colors::valueChanged(const gcn::SelectionEvent &event) mPreviewBox->setContent(mTextPreview); mTextPreview->setFont(boldFont); mTextPreview->setTextColor(col); - mTextPreview->setTextBGColor(nullptr); - mTextPreview->setOpaque(false); - mTextPreview->setShadow(true); mTextPreview->setOutline(true); - mTextPreview->useTextAlpha(false); switch (type) { @@ -242,6 +245,10 @@ void Setup_Colors::valueChanged(const gcn::SelectionEvent &event) case UserPalette::HIT_CRITICAL: case UserPalette::MISS: mTextPreview->setShadow(false); + break; + default: + mTextPreview->setShadow(true); + break; } if (grad != Palette::STATIC && grad != Palette::PULSE) diff --git a/src/gui/setup_colors.h b/src/gui/setup_colors.h index b534ca8a..9aa5be74 100644 --- a/src/gui/setup_colors.h +++ b/src/gui/setup_colors.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SETUP_COLORS_H -#define SETUP_COLORS_H +#pragma once #include "guichanfwd.h" @@ -87,5 +86,3 @@ class Setup_Colors : public SetupTab, void updateColor(); void updateGradType(); }; - -#endif // SETUP_COLORS_H diff --git a/src/gui/setup_interface.cpp b/src/gui/setup_interface.cpp index 7eef974c..ce77d3e6 100644 --- a/src/gui/setup_interface.cpp +++ b/src/gui/setup_interface.cpp @@ -23,12 +23,15 @@ #include "configuration.h" +#include "gui/okdialog.h" #include "gui/widgets/checkbox.h" +#include "gui/widgets/dropdown.h" #include "gui/widgets/label.h" #include "gui/widgets/layout.h" #include "gui/widgets/slider.h" #include "gui/widgets/spacer.h" -#include "gui/widgets/dropdown.h" + +#include "resources/theme.h" #include "utils/gettext.h" @@ -37,8 +40,35 @@ #include <SDL.h> +#include <algorithm> #include <string> +class ThemesListModel : public gcn::ListModel +{ +public: + int getNumberOfElements() override + { + return gui->getAvailableThemes().size(); + } + + std::string getElementAt(int i) override + { + return gui->getAvailableThemes().at(i).getName(); + } + + static int getThemeIndex(const std::string &path) + { + auto &themes = gui->getAvailableThemes(); + auto themeIt = std::find_if(themes.begin(), + themes.end(), + [&](const ThemeInfo &theme) { + return theme.getPath() == path; + }); + return themeIt != themes.end() ? std::distance(themes.begin(), themeIt) : 0; + } +}; + + const char *SIZE_NAME[4] = { N_("Tiny"), @@ -97,8 +127,7 @@ Setup_Interface::Setup_Interface(): mPickupParticleEnabled)), mSpeechSlider(new Slider(0, 3)), mSpeechLabel(new Label(std::string())), - mAlphaSlider(new Slider(0.2, 1.0)), - mFontSize(config.fontSize) + mAlphaSlider(new Slider(0.2, 1.0)) { setName(_("Interface")); @@ -108,18 +137,21 @@ Setup_Interface::Setup_Interface(): mShowMonsterDamageCheckBox = new CheckBox(_("Show damage"), mShowMonsterDamageEnabled); - speechLabel = new Label(_("Overhead text:")); - alphaLabel = new Label(_("GUI opacity")); - fontSizeLabel = new Label(_("Font size:")); + gcn::Label *speechLabel = new Label(_("Overhead text:")); + gcn::Label *alphaLabel = new Label(_("GUI opacity")); + gcn::Label *themeLabel = new Label(_("Theme:")); + gcn::Label *fontSizeLabel = new Label(_("Font size:")); + + mThemesListModel = std::make_unique<ThemesListModel>(); + mThemeDropDown = new DropDown(mThemesListModel.get()); - mFontSizeListModel = new FontSizeChoiceListModel; - mFontSizeDropDown = new DropDown(mFontSizeListModel); + mFontSizeListModel = std::make_unique<FontSizeChoiceListModel>(); + mFontSizeDropDown = new DropDown(mFontSizeListModel.get()); mAlphaSlider->setValue(mOpacity); mAlphaSlider->setWidth(90); mAlphaSlider->setEnabled(!config.disableTransparency); - // Set actions mShowMonsterDamageCheckBox->setActionEventId("monsterdamage"); mVisibleNamesCheckBox->setActionEventId("visiblenames"); @@ -127,6 +159,7 @@ Setup_Interface::Setup_Interface(): mPickupParticleCheckBox->setActionEventId("pickupparticle"); mNameCheckBox->setActionEventId("showownname"); mNPCLogCheckBox->setActionEventId("lognpc"); + mThemeDropDown->setActionEventId("theme"); mAlphaSlider->setActionEventId("guialpha"); mSpeechSlider->setActionEventId("speech"); @@ -137,13 +170,16 @@ Setup_Interface::Setup_Interface(): mPickupParticleCheckBox->addActionListener(this); mNameCheckBox->addActionListener(this); mNPCLogCheckBox->addActionListener(this); + mThemeDropDown->addActionListener(this); mAlphaSlider->addActionListener(this); mSpeechSlider->addActionListener(this); mSpeechLabel->setCaption(speechModeToString(mSpeechMode)); mSpeechSlider->setValue(mSpeechMode); - mFontSizeDropDown->setSelected(mFontSize - 10); + mThemeDropDown->setSelected(ThemesListModel::getThemeIndex(config.theme)); + + mFontSizeDropDown->setSelected(config.fontSize - 10); mFontSizeDropDown->adjustHeight(); // Do the layout @@ -162,27 +198,35 @@ Setup_Interface::Setup_Interface(): place(0, 5, space, 1, 1); - place(0, 6, fontSizeLabel, 2); - place(2, 6, mFontSizeDropDown, 2); + place(0, 6, themeLabel, 2); + place(2, 6, mThemeDropDown, 2).setPadding(2); - place(0, 7, space, 1, 1); + place(0, 7, fontSizeLabel, 2); + place(2, 7, mFontSizeDropDown, 2).setPadding(2); - place(0, 8, mAlphaSlider, 2); - place(2, 8, alphaLabel, 2); + place(0, 8, space, 1, 1); - place(0, 9, mSpeechSlider, 2); - place(2, 9, speechLabel, 2); - place(4, 9, mSpeechLabel, 2).setPadding(2); -} + place(0, 9, mAlphaSlider, 2); + place(2, 9, alphaLabel, 2); -Setup_Interface::~Setup_Interface() -{ - delete mFontSizeListModel; + place(0, 10, mSpeechSlider, 2); + place(2, 10, speechLabel, 2); + place(4, 10, mSpeechLabel, 2).setPadding(2); } +Setup_Interface::~Setup_Interface() = default; + void Setup_Interface::apply() { - config.fontSize = mFontSizeDropDown->getSelected() + 10; + auto &theme = gui->getAvailableThemes().at(mThemeDropDown->getSelected()); + auto fontSize = mFontSizeDropDown->getSelected() + 10; + if (config.theme != theme.getPath() || config.fontSize != fontSize) + { + new OkDialog(_("Changing Theme or Font Size"), + _("Theme and font size changes will apply after restart.")); + } + config.theme = theme.getPath(); + config.fontSize = fontSize; mShowMonsterDamageEnabled = config.showMonstersTakedDamage; mVisibleNamesEnabled = config.visibleNames; @@ -201,6 +245,8 @@ void Setup_Interface::cancel() mSpeechSlider->setValue(mSpeechMode); mNameCheckBox->setSelected(mNameEnabled); mNPCLogCheckBox->setSelected(mNPCLogEnabled); + mThemeDropDown->setSelected(ThemesListModel::getThemeIndex(config.theme)); + mFontSizeDropDown->setSelected(config.fontSize - 10); mAlphaSlider->setValue(mOpacity); //mAlphaSlider->setEnabled(!mSDLTransparencyDisabled); diff --git a/src/gui/setup_interface.h b/src/gui/setup_interface.h index 530d575a..027526a2 100644 --- a/src/gui/setup_interface.h +++ b/src/gui/setup_interface.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_SETUP_INTERFACE_H -#define GUI_SETUP_INTERFACE_H +#pragma once #include "being.h" #include "guichanfwd.h" @@ -30,8 +29,6 @@ #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> -class FontSizeChoiceListModel; - class Setup_Interface : public SetupTab, public gcn::ActionListener, public gcn::KeyListener { @@ -54,11 +51,8 @@ class Setup_Interface : public SetupTab, public gcn::ActionListener, double mOpacity; Being::Speech mSpeechMode; - FontSizeChoiceListModel *mFontSizeListModel; - - gcn::Label *speechLabel; - gcn::Label *alphaLabel; - gcn::Label *fontSizeLabel; + std::unique_ptr<gcn::ListModel> mThemesListModel; + std::unique_ptr<gcn::ListModel> mFontSizeListModel; gcn::CheckBox *mShowMonsterDamageCheckBox; gcn::CheckBox *mVisibleNamesCheckBox; @@ -73,9 +67,7 @@ class Setup_Interface : public SetupTab, public gcn::ActionListener, gcn::Label *mSpeechLabel; gcn::Slider *mAlphaSlider; - int mFontSize; + gcn::DropDown *mThemeDropDown; gcn::DropDown *mFontSizeDropDown; }; - -#endif diff --git a/src/gui/setup_joystick.h b/src/gui/setup_joystick.h index e51ccbe8..8b319ad2 100644 --- a/src/gui/setup_joystick.h +++ b/src/gui/setup_joystick.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_SETUP_JOYSTICK_H -#define GUI_SETUP_JOYSTICK_H +#pragma once #include "guichanfwd.h" @@ -44,5 +43,3 @@ class Setup_Joystick : public SetupTab, public gcn::ActionListener bool mJoystickEnabled; gcn::CheckBox *mJoystickCheckBox; }; - -#endif diff --git a/src/gui/setup_keyboard.h b/src/gui/setup_keyboard.h index 1c9d1733..e953bdf3 100644 --- a/src/gui/setup_keyboard.h +++ b/src/gui/setup_keyboard.h @@ -20,8 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_SETUP_KEYBOARD_H -#define GUI_SETUP_KEYBOARD_H +#pragma once #include "guichanfwd.h" @@ -71,5 +70,3 @@ class Setup_Keyboard : public SetupTab, public gcn::ActionListener bool mKeySetting; /**< flag to check if key being set. */ }; - -#endif diff --git a/src/gui/setup_players.cpp b/src/gui/setup_players.cpp index 67fe79da..7cfa572a 100644 --- a/src/gui/setup_players.cpp +++ b/src/gui/setup_players.cpp @@ -267,13 +267,13 @@ Setup_Players::Setup_Players(): place(0, 0, mPlayerTitleTable, 4); place(0, 1, mPlayerScrollArea, 4, 4).setPadding(2); place(0, 5, mDeleteButton); - place(0, 6, mShowGenderCheckBox, 2).setPadding(2); - place(0, 7, mEnableChatLogCheckBox, 2).setPadding(2); + place(0, 6, mShowGenderCheckBox, 2); + place(0, 7, mEnableChatLogCheckBox, 2); place(2, 5, ignore_action_label); - place(2, 6, mIgnoreActionChoicesBox, 2).setPadding(2); + place(2, 6, mIgnoreActionChoicesBox, 2); place(0, 8, mDefaultTrading); place(0, 9, mDefaultWhisper); - place(0, 10, mWhisperTabCheckBox, 4).setPadding(4); + place(0, 10, mWhisperTabCheckBox, 4); player_relations.addListener(this); } diff --git a/src/gui/setup_players.h b/src/gui/setup_players.h index 126d621b..3ca422d2 100644 --- a/src/gui/setup_players.h +++ b/src/gui/setup_players.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_SETUP_PLAYERS_H -#define GUI_SETUP_PLAYERS_H +#pragma once #include "guichanfwd.h" #include "playerrelations.h" @@ -70,5 +69,3 @@ private: gcn::CheckBox *mShowGenderCheckBox; gcn::CheckBox *mEnableChatLogCheckBox; }; - -#endif diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index 38602a5c..313badd8 100644 --- a/src/gui/setup_video.cpp +++ b/src/gui/setup_video.cpp @@ -25,13 +25,13 @@ #include "configuration.h" #include "game.h" #include "graphics.h" -#include "gui/widgets/dropdown.h" #include "localplayer.h" #include "particle.h" #include "gui/okdialog.h" #include "gui/widgets/checkbox.h" +#include "gui/widgets/dropdown.h" #include "gui/widgets/label.h" #include "gui/widgets/layout.h" #include "gui/widgets/slider.h" @@ -325,17 +325,17 @@ Setup_Video::Setup_Video(): place(0, 2, mDisableSDLTransparencyCheckBox, 4); place(0, 3, mFpsCheckBox); - place(1, 3, mFpsSlider, 2); + place(1, 3, mFpsSlider, 2).setVAlign(LayoutCell::CENTER); place(3, 3, mFpsLabel); place(0, 4, mParticleEffectsCheckBox, 4); place(0, 5, particleDetailLabel); - place(1, 5, mParticleDetailSlider, 2); + place(1, 5, mParticleDetailSlider, 2).setVAlign(LayoutCell::CENTER); place(3, 5, mParticleDetailField); place(0, 6, overlayDetailLabel); - place(1, 6, mOverlayDetailSlider, 2); + place(1, 6, mOverlayDetailSlider, 2).setVAlign(LayoutCell::CENTER); place(3, 6, mOverlayDetailField); } @@ -557,6 +557,6 @@ void Setup_Video::refreshScaleList() } mScaleListModel->setVideoSettings(mVideoSettings); - mScaleDropDown->setListModel(mScaleListModel.get()); + mScaleDropDown->adjustHeight(); mScaleDropDown->setSelected(mVideoSettings.userScale); } diff --git a/src/gui/setup_video.h b/src/gui/setup_video.h index 6d4fbe77..969d8e8f 100644 --- a/src/gui/setup_video.h +++ b/src/gui/setup_video.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_SETUP_VIDEO_H -#define GUI_SETUP_VIDEO_H +#pragma once #include "guichanfwd.h" @@ -30,6 +29,7 @@ #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> +class DropDown; class ResolutionListModel; class ScaleListModel; @@ -60,7 +60,7 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, gcn::DropDown *mWindowModeDropDown; gcn::DropDown *mResolutionDropDown; - gcn::DropDown *mScaleDropDown; + DropDown *mScaleDropDown; gcn::CheckBox *mVSyncCheckBox; gcn::CheckBox *mOpenGLCheckBox; gcn::CheckBox *mCustomCursorCheckBox; @@ -80,5 +80,3 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, gcn::CheckBox *mDisableSDLTransparencyCheckBox; }; - -#endif diff --git a/src/gui/shortcutwindow.cpp b/src/gui/shortcutwindow.cpp index 7d299d2c..2cffbb81 100644 --- a/src/gui/shortcutwindow.cpp +++ b/src/gui/shortcutwindow.cpp @@ -31,6 +31,7 @@ static constexpr int GRAB_MARGIN = 4; ShortcutWindow::ShortcutWindow(const std::string &title, ShortcutContainer *content) + : Window(SkinType::ToolWindow, std::string()) { setWindowName(title); // no title presented, title bar gets some extra space so window can be moved. @@ -41,7 +42,11 @@ ShortcutWindow::ShortcutWindow(const std::string &title, setSaveVisible(true); setupWindow->registerWindowForReset(this); - const int border = getPadding() * 2; + auto scrollArea = new ScrollArea(content); + scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + scrollArea->setOpaque(false); + + const int border = (getPadding() + content->getFrameSize()) * 2; setMinWidth(content->getBoxWidth() + border); setMinHeight(content->getBoxHeight() + border + GRAB_MARGIN); setMaxWidth(content->getBoxWidth() * content->getMaxItems() + border); @@ -49,10 +54,6 @@ ShortcutWindow::ShortcutWindow(const std::string &title, setDefaultSize(getMinWidth(), getMaxHeight(), ImageRect::LOWER_RIGHT); - auto scrollArea = new ScrollArea(content); - scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); - scrollArea->setOpaque(false); - place(0, 0, scrollArea, 5, 5).setPadding(0); Layout &layout = getLayout(); diff --git a/src/gui/shortcutwindow.h b/src/gui/shortcutwindow.h index 7494dfed..68e031ad 100644 --- a/src/gui/shortcutwindow.h +++ b/src/gui/shortcutwindow.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SHORTCUTWINDOW_H -#define SHORTCUTWINDOW_H +#pragma once #include "gui/widgets/window.h" @@ -39,5 +38,3 @@ class ShortcutWindow : public Window extern ShortcutWindow *itemShortcutWindow; extern ShortcutWindow *emoteShortcutWindow; - -#endif diff --git a/src/gui/skilldialog.cpp b/src/gui/skilldialog.cpp index 5b85252d..40421daf 100644 --- a/src/gui/skilldialog.cpp +++ b/src/gui/skilldialog.cpp @@ -30,7 +30,6 @@ #include "gui/widgets/button.h" #include "gui/widgets/label.h" #include "gui/widgets/listbox.h" -#include "gui/widgets/progressbar.h" #include "gui/widgets/scrollarea.h" #include "gui/widgets/tab.h" #include "gui/widgets/tabbedarea.h" @@ -43,7 +42,6 @@ #include "resources/resourcemanager.h" #include "resources/theme.h" -#include "utils/dtor.h" #include "utils/gettext.h" #include "utils/stringutils.h" #include "utils/xml.h" @@ -61,7 +59,7 @@ struct SkillInfo { unsigned short id; std::string name; - Image *icon = nullptr; + ResourceRef<Image> icon; bool modifiable; bool visible; SkillModel *model = nullptr; @@ -73,25 +71,16 @@ struct SkillInfo float progress; gcn::Color color; - ~SkillInfo() - { - if (icon) - icon->decRef(); - } + ~SkillInfo() = default; void setIcon(const std::string &iconPath) { ResourceManager *res = ResourceManager::getInstance(); if (!iconPath.empty()) - { icon = res->getImage(iconPath); - } if (!icon) - { - icon = Theme::getImageFromTheme( - paths.getStringValue("unknownItemFile")); - } + icon = Theme::getImageFromTheme(paths.getStringValue("unknownItemFile")); } void update(); @@ -113,19 +102,19 @@ public: void updateVisibilities(); - void addSkill(SkillInfo *info) - { mSkills.push_back(info); } + void addSkill(std::unique_ptr<SkillInfo> info) + { mSkills.push_back(std::move(info)); } private: - std::vector<SkillInfo *> mSkills; + std::vector<std::unique_ptr<SkillInfo>> mSkills; std::vector<SkillInfo *> mVisibleSkills; }; class SkillListBox : public ListBox { public: - SkillListBox(SkillModel *model): - ListBox(model) + SkillListBox(SkillModel *model) + : ListBox(model) {} SkillInfo *getSelectedInfo() @@ -142,19 +131,17 @@ public: if (!mListModel) return; - auto* model = static_cast<SkillModel*>(mListModel); - - updateAlpha(); + auto *model = static_cast<SkillModel *>(mListModel); + auto *graphics = static_cast<Graphics *>(gcnGraphics); - auto *graphics = static_cast<Graphics*>(gcnGraphics); - - graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, - (int) (mAlpha * 255.0f))); graphics->setFont(getFont()); // Draw filled rectangle around the selected list element if (mSelected >= 0) { + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + highlightColor.a = gui->getTheme()->getGuiAlpha(); + graphics->setColor(highlightColor); graphics->fillRectangle(gcn::Rectangle(0, getRowHeight() * mSelected, getWidth(), getRowHeight())); } @@ -165,12 +152,8 @@ public: i < model->getNumberOfElements(); ++i, y += getRowHeight()) { - SkillInfo *e = model->getSkillAt(i); - - if (e) - { + if (SkillInfo *e = model->getSkillAt(i)) e->draw(graphics, y, getWidth()); - } } } @@ -180,8 +163,8 @@ public: class SkillTab : public Tab { public: - SkillTab(const std::string &name, SkillListBox *listBox): - mListBox(listBox) + SkillTab(const std::string &name, SkillListBox *listBox) + : mListBox(listBox) { setCaption(name); } @@ -189,7 +172,6 @@ public: ~SkillTab() override { delete mListBox; - mListBox = nullptr; } SkillInfo *getSelectedInfo() @@ -215,11 +197,11 @@ SkillDialog::SkillDialog(): setMinWidth(240); setupWindow->registerWindowForReset(this); - mTabs = new TabbedArea(); + mTabbedArea = new TabbedArea; mPointsLabel = new Label("0"); mIncreaseButton = new Button(_("Up"), "inc", this); - place(0, 0, mTabs, 5, 5); + place(0, 0, mTabbedArea, 5, 5); place(0, 5, mPointsLabel, 4); place(4, 5, mIncreaseButton); @@ -236,7 +218,7 @@ void SkillDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "inc") { - auto *tab = static_cast<SkillTab*>(mTabs->getSelectedTab()); + auto *tab = static_cast<SkillTab*>(mTabbedArea->getSelectedTab()); if (SkillInfo *info = tab->getSelectedInfo()) Net::getPlayerHandler()->increaseSkill(info->id); } @@ -249,12 +231,11 @@ void SkillDialog::action(const gcn::ActionEvent &event) std::string SkillDialog::update(int id) { auto i = mSkills.find(id); - if (i != mSkills.end()) { - SkillInfo *info = i->second; - info->update(); - return info->name; + SkillInfo &info = *i->second; + info.update(); + return info.name; } return std::string(); @@ -267,9 +248,7 @@ void SkillDialog::update() mPointsLabel->adjustSize(); for (auto &skill : mSkills) - { skill.second->update(); - } } void SkillDialog::event(Event::Channel channel, const Event &event) @@ -291,20 +270,11 @@ void SkillDialog::event(Event::Channel channel, const Event &event) void SkillDialog::clearSkills() { - // Fixes issues with removing tabs - if (mTabs->getSelectedTabIndex() != -1) - { - mTabs->setSelectedTab((unsigned int) 0); + for (auto &tab : mTabs) + mTabbedArea->removeTab(tab.get()); - while (mTabs->getSelectedTabIndex() != -1) - { - gcn::Tab *tab = mTabs->getSelectedTab(); - mTabs->removeTabWithIndex(mTabs->getSelectedTabIndex()); - delete tab; - } - } - - delete_all(mSkills); + mTabs.clear(); + mSkillModels.clear(); mSkills.clear(); } @@ -317,40 +287,40 @@ void SkillDialog::loadSkills() int setCount = 0; std::string setName; - ScrollArea *scroll; - SkillListBox *listbox; - SkillTab *tab; if (!root || root.name() != "skills") { logger->log("Error loading skills file: %s", SKILLS_FILE); - if (Net::getNetworkType() == ServerType::TMWATHENA) + if (Net::getNetworkType() == ServerType::TmwAthena) { - auto *model = new SkillModel(); - auto *skill = new SkillInfo; + auto model = std::make_unique<SkillModel>(); + auto skill = std::make_unique<SkillInfo>(); skill->id = 1; skill->name = "basic"; skill->setIcon(std::string()); skill->modifiable = true; skill->visible = true; - skill->model = model; + skill->model = model.get(); skill->update(); - model->addSkill(skill); - mSkills[1] = skill; + mSkills[1] = skill.get(); + model->addSkill(std::move(skill)); model->updateVisibilities(); - listbox = new SkillListBox(model); - scroll = new ScrollArea(listbox); + auto listbox = new SkillListBox(model.get()); + auto scroll = std::make_unique<ScrollArea>(listbox); scroll->setOpaque(false); scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER); scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS); - tab = new SkillTab("Skills", listbox); + auto tab = std::make_unique<SkillTab>("Skills", listbox); + mTabbedArea->addTab(tab.get(), scroll.get()); - mTabs->addTab(tab, scroll); + mTabs.push_back(std::move(tab)); + mTabWidgets.push_back(std::move(scroll)); + mSkillModels.push_back(std::move(model)); update(); } @@ -365,7 +335,7 @@ void SkillDialog::loadSkills() setCount++; setName = set.getProperty("name", strprintf(_("Skill Set %d"), setCount)); - auto *model = new SkillModel(); + auto model = std::make_unique<SkillModel>(); for (auto node : set.children()) { @@ -375,34 +345,39 @@ void SkillDialog::loadSkills() std::string name = node.getProperty("name", strprintf(_("Skill %d"), id)); std::string icon = node.getProperty("icon", ""); - auto *skill = new SkillInfo; + auto skill = std::make_unique<SkillInfo>(); skill->id = id; skill->name = name; skill->setIcon(icon); skill->modifiable = false; skill->visible = false; - skill->model = model; + skill->model = model.get(); skill->update(); - model->addSkill(skill); + mSkills[id] = skill.get(); - mSkills[id] = skill; + model->addSkill(std::move(skill)); } } model->updateVisibilities(); - listbox = new SkillListBox(model); - scroll = new ScrollArea(listbox); + auto listbox = new SkillListBox(model.get()); + auto scroll = std::make_unique<ScrollArea>(listbox); scroll->setOpaque(false); scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER); scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS); - tab = new SkillTab(setName, listbox); + auto tab = std::make_unique<SkillTab>(setName, listbox); + + mTabbedArea->addTab(tab.get(), scroll.get()); - mTabs->addTab(tab, scroll); + mTabs.push_back(std::move(tab)); + mTabWidgets.push_back(std::move(scroll)); + mSkillModels.push_back(std::move(model)); } } + update(); } @@ -412,9 +387,9 @@ void SkillDialog::setModifiable(int id, bool modifiable) if (it != mSkills.end()) { - SkillInfo *info = it->second; - info->modifiable = modifiable; - info->update(); + SkillInfo &info = *it->second; + info.modifiable = modifiable; + info.update(); } } @@ -423,12 +398,8 @@ void SkillModel::updateVisibilities() mVisibleSkills.clear(); for (auto &skill : mSkills) - { if (skill->visible) - { - mVisibleSkills.push_back(skill); - } - } + mVisibleSkills.push_back(skill.get()); } void SkillInfo::update() @@ -504,8 +475,7 @@ void SkillInfo::draw(Graphics *graphics, int y, int width) if (!skillExp.empty()) { - gcn::Rectangle rect(33, y + 15, width - 33, 17); - - ProgressBar::render(graphics, rect, color, progress, skillExp); + const gcn::Rectangle rect(33, y + 15, width - 33, 17); + gui->getTheme()->drawProgressBar(graphics, rect, color, progress, skillExp); } } diff --git a/src/gui/skilldialog.h b/src/gui/skilldialog.h index e88c279f..83dc8dd2 100644 --- a/src/gui/skilldialog.h +++ b/src/gui/skilldialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SKILLDIALOG_H -#define SKILLDIALOG_H +#pragma once #include "gui/widgets/window.h" #include "eventlistener.h" @@ -28,10 +27,13 @@ #include <guichan/actionlistener.hpp> #include <map> +#include <memory> +#include <vector> class Button; class Label; class ScrollArea; +class SkillModel; class Tab; class TabbedArea; @@ -46,7 +48,6 @@ class SkillDialog : public Window, public gcn::ActionListener, public EventListe { public: SkillDialog(); - ~SkillDialog() override; void event(Event::Channel channel, const Event &event) override; @@ -75,12 +76,13 @@ class SkillDialog : public Window, public gcn::ActionListener, public EventListe bool hasSkills() { return !mSkills.empty(); } private: - std::map<int, SkillInfo *> mSkills; - TabbedArea *mTabs; + std::vector<std::unique_ptr<SkillModel>> mSkillModels; + std::vector<std::unique_ptr<Tab>> mTabs; + std::vector<std::unique_ptr<gcn::Widget>> mTabWidgets; + std::map<int, SkillInfo*> mSkills; + TabbedArea *mTabbedArea; Label *mPointsLabel; Button *mIncreaseButton; }; extern SkillDialog *skillDialog; - -#endif diff --git a/src/gui/socialwindow.cpp b/src/gui/socialwindow.cpp index 5a15da8f..265fb166 100644 --- a/src/gui/socialwindow.cpp +++ b/src/gui/socialwindow.cpp @@ -34,6 +34,7 @@ #include "gui/widgets/avatarlistbox.h" #include "gui/widgets/browserbox.h" #include "gui/widgets/button.h" +#include "gui/widgets/layout.h" #include "gui/widgets/linkhandler.h" #include "gui/widgets/popup.h" #include "gui/widgets/scrollarea.h" @@ -170,7 +171,7 @@ public: { setCaption(party->getName()); - setTabColor(&Theme::getThemeColor(Theme::PARTY_SOCIAL_TAB)); + setTabColor(&Theme::getThemeColor(Theme::PARTY_TAB)); mList = std::make_unique<AvatarListBox>(party); mScroll = std::make_unique<ScrollArea>(mList.get()); @@ -236,6 +237,11 @@ private: class PlayerList : public AvatarListModel { public: + ~PlayerList() override + { + delete_all(mPlayers); + } + void setPlayers(const std::vector<Avatar*> &players) { delete_all(mPlayers); @@ -273,7 +279,7 @@ public: mScroll->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_AUTO); } - ~PlayerListTab() + ~PlayerListTab() override { delete mPlayerList; } @@ -355,13 +361,8 @@ SocialWindow::SocialWindow() : setResizable(true); setSaveVisible(true); setCloseButton(true); - setMinWidth(120); - setMinHeight(55); - setDefaultSize(590, 200, 150, 124); setupWindow->registerWindowForReset(this); - loadWindowState(); - mCreateButton = new Button(_("Create"), "create", this); mInviteButton = new Button(_("Invite"), "invite", this); mLeaveButton = new Button(_("Leave"), "leave", this); @@ -372,19 +373,23 @@ SocialWindow::SocialWindow() : place(2, 0, mLeaveButton); place(0, 1, mTabs, 4, 4); - widgetResized(nullptr); + // Determine minimum size + int width = 0, height = 0; + getLayout().reflow(width, height); + setMinimumContentSize(width, height); + + setDefaultSize(590, 200, 150, 124); + loadWindowState(); mCreatePopup = new CreatePopup; mPlayerListTab = new PlayerListTab; - mPlayerListTab->setCaption(strprintf(_("Online (%zu)"), 0ul)); + mPlayerListTab->setCaption(strprintf(_("Online (%u)"), 0u)); mTabs->addTab(mPlayerListTab, mPlayerListTab->mScroll.get()); if (local_player->getParty()) - { addTab(local_player->getParty()); - } else updateButtons(); } @@ -673,7 +678,9 @@ void SocialWindow::showPartyCreate() void SocialWindow::setPlayersOnline(const std::vector<Avatar*> &players) { mPlayerListTab->setPlayers(players); - mPlayerListTab->setCaption(strprintf(_("Online (%zu)"), players.size())); + + unsigned playerCount = static_cast<unsigned>(players.size()); + mPlayerListTab->setCaption(strprintf(_("Online (%u)"), playerCount)); } void SocialWindow::logic() diff --git a/src/gui/socialwindow.h b/src/gui/socialwindow.h index 350f919c..d23f1b86 100644 --- a/src/gui/socialwindow.h +++ b/src/gui/socialwindow.h @@ -18,8 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SOCIALWINDOW_H -#define SOCIALWINDOW_H +#pragma once #include "utils/time.h" @@ -111,5 +110,3 @@ protected: }; extern SocialWindow *socialWindow; - -#endif // SOCIALWINDOW_H diff --git a/src/gui/speechbubble.cpp b/src/gui/speechbubble.cpp index 58747d57..72ec8bd2 100644 --- a/src/gui/speechbubble.cpp +++ b/src/gui/speechbubble.cpp @@ -27,14 +27,11 @@ #include "gui/widgets/label.h" #include "gui/widgets/textbox.h" -#include "resources/theme.h" - #include <guichan/font.hpp> - #include <guichan/widgets/label.hpp> -SpeechBubble::SpeechBubble(): - Popup("Speech", "speechbubble.xml") +SpeechBubble::SpeechBubble() + : Popup("Speech", SkinType::SpeechBubble) { setMinWidth(0); setMinHeight(0); @@ -45,7 +42,7 @@ SpeechBubble::SpeechBubble(): mSpeechBox = new TextBox; mSpeechBox->setEditable(false); mSpeechBox->setOpaque(false); - mSpeechBox->setTextColor(&Theme::getThemeColor(Theme::CHAT)); + mSpeechBox->setTextColor(&Theme::getThemeColor(Theme::BUBBLE_TEXT)); add(mCaption); add(mSpeechBox); @@ -60,11 +57,9 @@ void SpeechBubble::setCaption(const std::string &name, const gcn::Color *color) void SpeechBubble::setText(const std::string &text, bool showName) { - if (text == mText && (mCaption->getWidth() <= mSpeechBox->getMinWidth())) + if (text == mText && mCaption->getWidth() <= mSpeechBox->getMinWidth()) return; - mSpeechBox->setTextColor(&Theme::getThemeColor(Theme::TEXT)); - int width = mCaption->getWidth(); mSpeechBox->setTextWrapped(text, 130 > width ? 130 : width); const int speechWidth = mSpeechBox->getMinWidth(); diff --git a/src/gui/speechbubble.h b/src/gui/speechbubble.h index da677dcd..fbb1a21e 100644 --- a/src/gui/speechbubble.h +++ b/src/gui/speechbubble.h @@ -20,8 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SPEECHBUBBLE_H -#define SPEECHBUBBLE_H +#pragma once #include "gui/widgets/popup.h" @@ -51,5 +50,3 @@ class SpeechBubble : public Popup gcn::Label *mCaption; TextBox *mSpeechBox; }; - -#endif diff --git a/src/gui/statuswindow.cpp b/src/gui/statuswindow.cpp index ffcfa350..108d68cc 100644 --- a/src/gui/statuswindow.cpp +++ b/src/gui/statuswindow.cpp @@ -290,7 +290,7 @@ void StatusWindow::event(Event::Channel channel, it->second->update(); } - if (Net::getNetworkType() == ServerType::TMWATHENA && + if (Net::getNetworkType() == ServerType::TmwAthena && id == TmwAthena::MATK) { updateMPBar(mMpBar, true); diff --git a/src/gui/statuswindow.h b/src/gui/statuswindow.h index 99c3b46a..fc7ad67c 100644 --- a/src/gui/statuswindow.h +++ b/src/gui/statuswindow.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef STATUS_H -#define STATUS_H +#pragma once #include "eventlistener.h" @@ -85,5 +84,3 @@ class StatusWindow : public Window, public EventListener }; extern StatusWindow *statusWindow; - -#endif diff --git a/src/gui/textdialog.h b/src/gui/textdialog.h index 66ab3e53..d9634434 100644 --- a/src/gui/textdialog.h +++ b/src/gui/textdialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_GUILD_DIALOG_H -#define GUI_GUILD_DIALOG_H +#pragma once #include "gui/widgets/window.h" @@ -59,5 +58,3 @@ private: TextField *mTextField; gcn::Button *mOkButton; }; - -#endif diff --git a/src/gui/textpopup.h b/src/gui/textpopup.h index b1c6b2b3..db7d48e7 100644 --- a/src/gui/textpopup.h +++ b/src/gui/textpopup.h @@ -21,8 +21,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef TEXTPOPUP_H -#define TEXTPOPUP_H +#pragma once #include "gui/widgets/popup.h" @@ -51,5 +50,3 @@ class TextPopup : public Popup gcn::Label *mText1; gcn::Label *mText2; }; - -#endif // TEXTPOPUP_H diff --git a/src/gui/tradewindow.cpp b/src/gui/tradewindow.cpp index db4b6490..e24845b7 100644 --- a/src/gui/tradewindow.cpp +++ b/src/gui/tradewindow.cpp @@ -24,7 +24,6 @@ #include "event.h" #include "inventory.h" #include "item.h" -#include "localplayer.h" #include "playerinfo.h" #include "units.h" @@ -39,7 +38,6 @@ #include "gui/widgets/textfield.h" #include "gui/widgets/layout.h" -#include "net/inventoryhandler.h" #include "net/net.h" #include "net/tradehandler.h" @@ -48,8 +46,6 @@ #include <guichan/font.hpp> -#include <sstream> - #define CAPTION_PROPOSE _("Propose trade") #define CAPTION_CONFIRMED _("Confirmed. Waiting...") #define CAPTION_ACCEPT _("Agree trade") @@ -125,9 +121,7 @@ TradeWindow::TradeWindow(): reset(); } -TradeWindow::~TradeWindow() -{ -} +TradeWindow::~TradeWindow() = default; void TradeWindow::setMoney(int amount) { @@ -138,7 +132,10 @@ void TradeWindow::setMoney(int amount) void TradeWindow::addItem(int id, bool own, int quantity) { - (own ? mMyInventory : mPartnerInventory)->addItem(id, quantity); + if (own) + mMyInventory->addItem(id, quantity); + else + mPartnerInventory->addItem(id, quantity); } void TradeWindow::changeQuantity(int index, bool own, int quantity) @@ -186,22 +183,15 @@ void TradeWindow::receivedOk(bool own) } } -void TradeWindow::tradeItem(Item *item, int quantity) -{ - Net::getTradeHandler()->addItem(item, quantity); -} - void TradeWindow::valueChanged(const gcn::SelectionEvent &event) { - const Item *item; - /* If an item is selected in one container, make sure no item is selected * in the other container. */ if (event.getSource() == mMyItemContainer && - (item = mMyItemContainer->getSelectedItem())) + mMyItemContainer->getSelectedItem()) mPartnerItemContainer->selectNone(); - else if ((item = mPartnerItemContainer->getSelectedItem())) + else if (mPartnerItemContainer->getSelectedItem()) mMyItemContainer->selectNone(); } diff --git a/src/gui/tradewindow.h b/src/gui/tradewindow.h index e9d8d4f1..85b2f97d 100644 --- a/src/gui/tradewindow.h +++ b/src/gui/tradewindow.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TRADE_H -#define TRADE_H +#pragma once #include "gui/widgets/window.h" @@ -82,11 +81,6 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener void receivedOk(bool own); /** - * Send trade packet. - */ - void tradeItem(Item *item, int quantity); - - /** * Updates the labels and makes sure only one item is selected in * either my inventory or partner inventory. */ @@ -134,5 +128,3 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener }; extern TradeWindow *tradeWindow; - -#endif diff --git a/src/gui/truetypefont.cpp b/src/gui/truetypefont.cpp index 30037f84..444641b5 100644 --- a/src/gui/truetypefont.cpp +++ b/src/gui/truetypefont.cpp @@ -56,16 +56,16 @@ bool operator==(SDL_Color lhs, SDL_Color rhs) class TextChunk { public: - TextChunk(const std::string &text, SDL_Color color) + TextChunk(const std::string &text) : text(text) - , color(color) {} void generate(TTF_Font *font) { + // Always render in white, we'll use color modulation when rendering SDL_Surface *surface = TTF_RenderUTF8_Blended(font, getSafeUtf8String(text), - color); + SDL_Color { 255, 255, 255, 255 }); if (!surface) return; @@ -77,7 +77,6 @@ public: std::unique_ptr<Image> img; const std::string text; - const SDL_Color color; }; std::list<TrueTypeFont*> TrueTypeFont::mFonts; @@ -124,24 +123,13 @@ void TrueTypeFont::drawString(gcn::Graphics *graphics, return; auto *g = static_cast<Graphics *>(graphics); - const gcn::Color col = g->getColor(); - - /* The alpha value is ignored at image generation to avoid caching the - * same text with different alpha values. - */ - const SDL_Color color = { - static_cast<Uint8>(col.r), - static_cast<Uint8>(col.g), - static_cast<Uint8>(col.b), - 255 - }; bool found = false; for (auto i = mCache.begin(); i != mCache.end(); ++i) { auto &chunk = *i; - if (chunk.text == text && chunk.color == color) + if (chunk.text == text) { // Raise priority: move it to front mCache.splice(mCache.begin(), mCache, i); @@ -154,18 +142,17 @@ void TrueTypeFont::drawString(gcn::Graphics *graphics, { if (mCache.size() >= CACHE_SIZE) mCache.pop_back(); - mCache.emplace_front(text, color); + mCache.emplace_front(text); mCache.front().generate(mFont); } if (auto img = mCache.front().img.get()) { - img->setAlpha(col.a / 255.0f); g->drawRescaledImageF(img, 0, 0, x, y, img->getWidth(), img->getHeight(), img->getWidth() / mScale, - img->getHeight() / mScale); + img->getHeight() / mScale, true); } } diff --git a/src/gui/truetypefont.h b/src/gui/truetypefont.h index 9aa308b1..a479537d 100644 --- a/src/gui/truetypefont.h +++ b/src/gui/truetypefont.h @@ -20,8 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TRUETYPEFONT_H -#define TRUETYPEFONT_H +#pragma once #include <guichan/font.hpp> @@ -82,5 +81,3 @@ class TrueTypeFont : public gcn::Font static std::list<TrueTypeFont*> mFonts; static float mScale; }; - -#endif diff --git a/src/gui/unregisterdialog.h b/src/gui/unregisterdialog.h index c82d7a78..0c94e0af 100644 --- a/src/gui/unregisterdialog.h +++ b/src/gui/unregisterdialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef UNREGISTERDIALOG_H -#define UNREGISTERDIALOG_H +#pragma once #include "gui/widgets/window.h" @@ -57,5 +56,3 @@ class UnRegisterDialog : public Window, public gcn::ActionListener LoginData *mLoginData; }; - -#endif diff --git a/src/gui/updaterwindow.cpp b/src/gui/updaterwindow.cpp index 772df725..5cfb45cd 100644 --- a/src/gui/updaterwindow.cpp +++ b/src/gui/updaterwindow.cpp @@ -46,8 +46,8 @@ #include <iostream> #include <fstream> -const std::string xmlUpdateFile = "resources.xml"; -const std::string txtUpdateFile = "resources2.txt"; +constexpr char xmlUpdateFile[] = "resources.xml"; +constexpr char txtUpdateFile[] = "resources2.txt"; /** * Load the given file into a vector of updateFiles. @@ -123,7 +123,6 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, Window(_("Updating...")), mUpdateHost(updateHost), mUpdatesDir(updatesDir), - mCurrentFile("news.txt"), mLoadUpdates(applyUpdates), mLinkHandler(std::make_unique<ItemLinkHandler>(this)) { @@ -141,7 +140,6 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, mPlayButton = new Button(_("Play"), "play", this); mBrowserBox->setLinkHandler(mLinkHandler.get()); - mBrowserBox->setFrameSize(4); mProgressBar->setSmoothProgress(false); mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); mPlayButton->setEnabled(false); @@ -161,40 +159,22 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, setVisible(true); mCancelButton->requestFocus(); - // Try to download the updates list - download(); + startDownload("news.txt", true); } UpdaterWindow::~UpdaterWindow() { if (mLoadUpdates) loadUpdates(); - - if (mDownload) - { - mDownload->cancel(); - - delete mDownload; - mDownload = nullptr; - } - free(mMemoryBuffer); -} - -void UpdaterWindow::setProgress(float progress) -{ - // Do delayed progress bar update, since Guichan isn't thread-safe - MutexLocker lock(&mDownloadMutex); - mDownloadProgress = progress; } void UpdaterWindow::setLabel(const std::string &str) { - // Do delayed label text update, since Guichan isn't thread-safe - MutexLocker lock(&mDownloadMutex); - mNewLabelCaption = str; + mLabel->setCaption(str); + mLabel->adjustSize(); } -void UpdaterWindow::enable() +void UpdaterWindow::enablePlay() { mCancelButton->setEnabled(false); mPlayButton->setEnabled(true); @@ -204,20 +184,9 @@ void UpdaterWindow::enable() void UpdaterWindow::action(const gcn::ActionEvent &event) { if (event.getId() == "cancel") - { - // Register the user cancel - mUserCancel = true; - // Skip the updating process - if (mDownloadStatus != UPDATE_COMPLETE) - { - mDownload->cancel(); - mDownloadStatus = UPDATE_ERROR; - } - } + cancel(); else if (event.getId() == "play") - { - Client::setState(STATE_LOAD_DATA); - } + play(); } void UpdaterWindow::keyPressed(gcn::KeyEvent &keyEvent) @@ -226,140 +195,63 @@ void UpdaterWindow::keyPressed(gcn::KeyEvent &keyEvent) if (key.getValue() == Key::ESCAPE) { - action(gcn::ActionEvent(nullptr, mCancelButton->getActionEventId())); - Client::setState(STATE_WORLD_SELECT); + if (!cancel()) + { + mLoadUpdates = false; + Client::setState(STATE_WORLD_SELECT); + } } else if (key.getValue() == Key::ENTER) { - if (mDownloadStatus == UPDATE_COMPLETE || - mDownloadStatus == UPDATE_ERROR) - { - action(gcn::ActionEvent(nullptr, mPlayButton->getActionEventId())); - } - else - { - action(gcn::ActionEvent(nullptr, mCancelButton->getActionEventId())); - } + play(); } } -void UpdaterWindow::loadNews() +bool UpdaterWindow::cancel() { - if (!mMemoryBuffer) + // Skip the updating process + if (mDialogState != DialogState::Done) { - logger->log("Couldn't load news"); - return; - } - - // Reallocate and include terminating 0 character - mMemoryBuffer = (char*)realloc(mMemoryBuffer, mDownloadedBytes + 1); - mMemoryBuffer[mDownloadedBytes] = '\0'; - - mBrowserBox->clearRows(); - - // Tokenize and add each line separately - char *line = strtok(mMemoryBuffer, "\n"); - while (line) - { - mBrowserBox->addRow(line); - line = strtok(nullptr, "\n"); + mDownload->cancel(); + return true; } - - // Free the memory buffer now that we don't need it anymore - free(mMemoryBuffer); - mMemoryBuffer = nullptr; - - mScrollArea->setVerticalScrollAmount(0); + return false; } -int UpdaterWindow::updateProgress(void *ptr, DownloadStatus status, - size_t dt, size_t dn) +void UpdaterWindow::play() { - auto *uw = reinterpret_cast<UpdaterWindow *>(ptr); - - if (status == DOWNLOAD_STATUS_COMPLETE) - { - uw->mDownloadComplete = true; - } - else if (status == DOWNLOAD_STATUS_ERROR || - status == DOWNLOAD_STATUS_CANCELLED) - { - uw->mDownloadStatus = UPDATE_ERROR; - } - - float progress = (float) dn / dt; - - if (progress != progress) - progress = 0.0f; // check for NaN - if (progress < 0.0f) - progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway. - if (progress > 1.0f) - progress = 1.0f; - - uw->setLabel( - uw->mCurrentFile + " (" + toString((int) (progress * 100)) + "%)"); - uw->setProgress(progress); - - if (Client::getState() != STATE_UPDATE || uw->mDownloadStatus == UPDATE_ERROR) - { - // If the action was canceled return an error code to stop the mThread - return -1; - } - - return 0; + if (mPlayButton->isEnabled()) + Client::setState(STATE_LOAD_DATA); } -size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, void *stream) +void UpdaterWindow::loadNews() { - auto *uw = reinterpret_cast<UpdaterWindow *>(stream); - size_t totalMem = size * nmemb; - uw->mMemoryBuffer = (char*) realloc(uw->mMemoryBuffer, - uw->mDownloadedBytes + totalMem); - if (uw->mMemoryBuffer) - { - memcpy(&(uw->mMemoryBuffer[uw->mDownloadedBytes]), ptr, totalMem); - uw->mDownloadedBytes += totalMem; - } + mBrowserBox->clearRows(); + mBrowserBox->addRows(mDownload->getBuffer()); - return totalMem; + mScrollArea->setVerticalScrollAmount(0); } -void UpdaterWindow::download() +void UpdaterWindow::startDownload(const std::string &fileName, + bool storeInMemory, + std::optional<unsigned long> adler32) { - mDownload = new Net::Download(this, mUpdateHost + "/" + mCurrentFile, - updateProgress); + mDownload = std::make_unique<Net::Download>(mUpdateHost + "/" + fileName); + mCurrentFile = fileName; - if (mStoreInMemory) - { - mDownload->setWriteFunction(UpdaterWindow::memoryWrite); - } + if (storeInMemory) + mDownload->setUseBuffer(); else - { - if (mDownloadStatus == UPDATE_RESOURCES) - { - mDownload->setFile(mUpdatesDir + "/" + mCurrentFile, - mCurrentChecksum); - } - else - { - mDownload->setFile(mUpdatesDir + "/" + mCurrentFile); - } - } + mDownload->setFile(mUpdatesDir + "/" + fileName, adler32); - if (mDownloadStatus != UPDATE_RESOURCES) + if (mDialogState != DialogState::DownloadResources) mDownload->noCache(); - setLabel(mCurrentFile + " (0%)"); - mDownloadComplete = false; - - // TODO: check return mDownload->start(); } void UpdaterWindow::loadUpdates() { - ResourceManager *resman = ResourceManager::getInstance(); - if (mUpdateFiles.empty()) { // updates not downloaded @@ -367,142 +259,144 @@ void UpdaterWindow::loadUpdates() if (mUpdateFiles.empty()) { logger->log("Warning this server does not have a" - " %s file falling back to %s", xmlUpdateFile.c_str(), - txtUpdateFile.c_str()); + " %s file falling back to %s", xmlUpdateFile, + txtUpdateFile); mUpdateFiles = loadTxtFile(mUpdatesDir + "/" + txtUpdateFile); } } for (const UpdateFile &file : mUpdateFiles) - { - resman->addToSearchPath(mUpdatesDir + "/" + file.name, false); - } + ResourceManager::addToSearchPath(mUpdatesDir + "/" + file.name, false); } void UpdaterWindow::logic() { - const std::string xmlUpdateFile = "resources.xml"; - const std::string txtUpdateFile = "resources2.txt"; + Window::logic(); - // Update Scroll logic - mScrollArea->logic(); + if (mDialogState == DialogState::Done) + return; - // Synchronize label caption when necessary - { - MutexLocker lock(&mDownloadMutex); + const auto state = mDownload->getState(); + float progress = 0.0f; - if (mLabel->getCaption() != mNewLabelCaption) - { - mLabel->setCaption(mNewLabelCaption); - mLabel->adjustSize(); - } + switch (state.status) { + case DownloadStatus::InProgress: { + setLabel(mCurrentFile + " (" + toString((int) (state.progress * 100)) + "%)"); + progress = state.progress; + break; + } + + case DownloadStatus::Canceled: + mDialogState = DialogState::Done; + + enablePlay(); + setLabel(_("Download canceled")); + break; + + case DownloadStatus::Error: { + mDialogState = DialogState::Done; - mProgressBar->setProgress(mDownloadProgress); + std::string error = "##1"; + error += mDownload->getError(); + error += "\n\n##1"; + error += _("The update process is incomplete. " + "It is strongly recommended that you try again later."); + mBrowserBox->addRows(error); + + int maxScroll = mScrollArea->getVerticalMaxScroll(); + mScrollArea->setVerticalScrollAmount(maxScroll); + + enablePlay(); + setLabel(_("Error while downloading")); + break; } - switch (mDownloadStatus) + case DownloadStatus::Complete: + downloadCompleted(); + break; + } + + mProgressBar->setProgress(progress); +} + +void UpdaterWindow::downloadCompleted() +{ + switch (mDialogState) { - case UPDATE_ERROR: - // TODO: Only send complete sentences to gettext - mBrowserBox->addRow(std::string()); - mBrowserBox->addRow(_("##1 The update process is incomplete.")); - // TRANSLATORS: Continues "you try again later.". - mBrowserBox->addRow(_("##1 It is strongly recommended that")); - // TRANSLATORS: Begins "It is strongly recommended that". - mBrowserBox->addRow(_("##1 you try again later.")); - - mBrowserBox->addRow(mDownload->getError()); - mScrollArea->setVerticalScrollAmount( - mScrollArea->getVerticalMaxScroll()); - mDownloadStatus = UPDATE_COMPLETE; - break; - case UPDATE_NEWS: - if (mDownloadComplete) - { - // Parse current memory buffer as news and dispose of the data - loadNews(); + case DialogState::DownloadNews: + loadNews(); - mCurrentFile = xmlUpdateFile; - mStoreInMemory = false; - mDownloadStatus = UPDATE_LIST; - download(); // download() changes mDownloadComplete to false - } - break; - case UPDATE_LIST: - if (mDownloadComplete) + mDialogState = DialogState::DownloadList; + startDownload(xmlUpdateFile, false); + break; + + case DialogState::DownloadList: + if (mCurrentFile == xmlUpdateFile) + { + mUpdateFiles = loadXMLFile(mUpdatesDir + "/" + xmlUpdateFile); + if (mUpdateFiles.empty()) { - if (mCurrentFile == xmlUpdateFile) - { - mUpdateFiles = loadXMLFile(mUpdatesDir + "/" + xmlUpdateFile); - if (mUpdateFiles.empty()) - { - logger->log("Warning this server does not have a %s" - " file falling back to %s", - xmlUpdateFile.c_str(), txtUpdateFile.c_str()); - - // If the resources.xml file fails, fall back onto a older version - mCurrentFile = txtUpdateFile; - mStoreInMemory = false; - mDownloadStatus = UPDATE_LIST; - download(); - break; - } - } - else if (mCurrentFile == txtUpdateFile) - { - mUpdateFiles = loadTxtFile(mUpdatesDir + "/" + txtUpdateFile); - } - mStoreInMemory = false; - mDownloadStatus = UPDATE_RESOURCES; + logger->log("Warning this server does not have a %s" + " file falling back to %s", + xmlUpdateFile, txtUpdateFile); + + // If the resources.xml file fails, fall back onto a older version + mDialogState = DialogState::DownloadList; + startDownload(txtUpdateFile, false); + break; } - break; - case UPDATE_RESOURCES: - if (mDownloadComplete) + } + else if (mCurrentFile == txtUpdateFile) + { + mUpdateFiles = loadTxtFile(mUpdatesDir + "/" + txtUpdateFile); + } + + mDialogState = DialogState::DownloadResources; + break; + + case DialogState::DownloadResources: + if (mUpdateIndex < mUpdateFiles.size()) + { + const UpdateFile &thisFile = mUpdateFiles[mUpdateIndex]; + if (!thisFile.required) { - if (mUpdateIndex < mUpdateFiles.size()) + if (!(thisFile.type == "music" && config.downloadMusic)) { - const UpdateFile &thisFile = mUpdateFiles[mUpdateIndex]; - if (!thisFile.required) - { - if (!(thisFile.type == "music" && config.downloadMusic)) - { - mUpdateIndex++; - break; - } - } - mCurrentFile = thisFile.name; - std::stringstream ss(thisFile.hash); - ss >> std::hex >> mCurrentChecksum; - - std::string filename = mUpdatesDir + "/" + mCurrentFile; - FILE *file = fopen(filename.c_str(), "r+b"); - - if (!file || Net::Download::fadler32(file) != mCurrentChecksum) - { - if (file) - fclose(file); - download(); - } - else - { - fclose(file); - logger->log("%s already here", mCurrentFile.c_str()); - } mUpdateIndex++; - } - else - { - // Download of updates completed - mDownloadStatus = UPDATE_COMPLETE; + break; } } - break; - case UPDATE_COMPLETE: - enable(); + + unsigned long checksum; + std::stringstream ss(thisFile.hash); + ss >> std::hex >> checksum; + + std::string filename = mUpdatesDir + "/" + thisFile.name; + FILE *file = fopen(filename.c_str(), "r+b"); + + if (!file || Net::Download::fadler32(file) != checksum) + { + if (file) + fclose(file); + startDownload(thisFile.name, false, checksum); + } + else + { + fclose(file); + logger->log("%s already here", thisFile.name.c_str()); + } + mUpdateIndex++; + } + else + { + // Download of updates completed + mDialogState = DialogState::Done; + enablePlay(); setLabel(_("Completed")); - mDownloadStatus = UPDATE_IDLE; - break; - case UPDATE_IDLE: - break; + } + break; + + case DialogState::Done: + break; } } diff --git a/src/gui/updaterwindow.h b/src/gui/updaterwindow.h index 6ea1d754..dd1400ba 100644 --- a/src/gui/updaterwindow.h +++ b/src/gui/updaterwindow.h @@ -19,15 +19,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef UPDATERWINDOW_H -#define UPDATERWINDOW_H +#pragma once #include "gui/widgets/window.h" #include "net/download.h" -#include "utils/mutex.h" - #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> @@ -65,7 +62,7 @@ class UpdaterWindow : public Window, public gcn::ActionListener, * @param updateHost Host where to get the updated files. * @param updatesDir Directory where to store updates (should be absolute * and already created). - * @param applyUpdates If true, the update window will pass the updates to teh + * @param applyUpdates If true, the update window will pass the updates to the * resource manager */ UpdaterWindow(const std::string &updateHost, @@ -74,27 +71,6 @@ class UpdaterWindow : public Window, public gcn::ActionListener, ~UpdaterWindow() override; - /** - * Set's progress bar status - */ - void setProgress(float progress); - - /** - * Set's label above progress - */ - void setLabel(const std::string &); - - /** - * Enables play button - */ - void enable(); - - /** - * Loads and display news. Assumes the news file contents have been loaded - * into the memory buffer. - */ - void loadNews(); - void action(const gcn::ActionEvent &event) override; void keyPressed(gcn::KeyEvent &keyEvent) override; @@ -102,38 +78,38 @@ class UpdaterWindow : public Window, public gcn::ActionListener, void logic() override; private: - void download(); + bool cancel(); + void play(); - /** - * Loads the updates this window has gotten into the resource manager - */ - void loadUpdates(); + void setLabel(const std::string &); + void enablePlay(); + void startDownload(const std::string &fileName, + bool storeInMemory, + std::optional<unsigned long> adler32 = {}); + void downloadCompleted(); /** - * A download callback for progress updates. + * Loads and display news. Assumes the news file contents have been loaded + * into the memory buffer. */ - static int updateProgress(void *ptr, DownloadStatus status, - size_t dt, size_t dn); + void loadNews(); /** - * A libcurl callback for writing to memory. + * Loads the updates this window has gotten into the resource manager */ - static size_t memoryWrite(void *ptr, size_t size, size_t nmemb, - void *stream); + void loadUpdates(); - enum UpdateDownloadStatus + enum class DialogState { - UPDATE_ERROR, - UPDATE_IDLE, - UPDATE_LIST, - UPDATE_COMPLETE, - UPDATE_NEWS, - UPDATE_RESOURCES + DownloadNews, + DownloadList, + DownloadResources, + Done, }; /** Status of the current download. */ - UpdateDownloadStatus mDownloadStatus = UPDATE_NEWS; + DialogState mDialogState = DialogState::DownloadNews; /** Host where we get the updated files. */ std::string mUpdateHost; @@ -144,35 +120,8 @@ private: /** The file currently downloading. */ std::string mCurrentFile; - /** The new label caption to be set in the logic method. */ - std::string mNewLabelCaption; - - /** The new progress value to be set in the logic method. */ - float mDownloadProgress = 0.0f; - - /** The mutex used to guard access to mNewLabelCaption and mDownloadProgress. */ - Mutex mDownloadMutex; - - /** The Adler32 checksum of the file currently downloading. */ - unsigned long mCurrentChecksum = 0; - - /** A flag to indicate whether to use a memory buffer or a regular file. */ - bool mStoreInMemory = true; - - /** Flag that show if current download is complete. */ - bool mDownloadComplete = true; - - /** Flag that show if the user has canceled the update. */ - bool mUserCancel = false; - - /** Byte count currently downloaded in mMemoryBuffer. */ - int mDownloadedBytes = 0; - - /** Buffer for files downloaded to memory. */ - char *mMemoryBuffer = nullptr; - /** Download handle. */ - Net::Download *mDownload = nullptr; + std::unique_ptr<Net::Download> mDownload; /** List of files to download. */ std::vector<UpdateFile> mUpdateFiles; @@ -191,5 +140,3 @@ private: ScrollArea *mScrollArea; /**< Used to scroll news box. */ std::unique_ptr<LinkHandler> mLinkHandler; }; - -#endif diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index 558ce0e6..9e529063 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -32,7 +32,6 @@ #include "textmanager.h" #include "gui/gui.h" -#include "gui/ministatuswindow.h" #include "gui/popupmenu.h" #include "gui/beingpopup.h" @@ -237,9 +236,6 @@ void Viewport::draw(gcn::Graphics *gcnGraphics) } } - if (miniStatusWindow) - miniStatusWindow->drawIcons(graphics); - // Draw contained widgets WindowContainer::draw(gcnGraphics); } @@ -309,10 +305,10 @@ void Viewport::_drawDebugPath(Graphics *graphics) unsigned char walkMask; switch (Net::getNetworkType()) { - case ServerType::TMWATHENA: + case ServerType::TmwAthena: walkMask = Map::BLOCKMASK_WALL | Map::BLOCKMASK_CHARACTER; break; - case ServerType::MANASERV: + case ServerType::ManaServ: default: walkMask = Map::BLOCKMASK_WALL; break; @@ -579,18 +575,18 @@ void Viewport::updateCursorType() gui->setCursorType(mHoverBeing->getHoverCursor()); break; default: - gui->setCursorType(Cursor::POINTER); + gui->setCursorType(Cursor::Pointer); break; } // Item mouseover } else if (mHoverItem) { - gui->setCursorType(Cursor::PICKUP); + gui->setCursorType(Cursor::PickUp); } else { - gui->setCursorType(Cursor::POINTER); + gui->setCursorType(Cursor::Pointer); } } diff --git a/src/gui/viewport.h b/src/gui/viewport.h index b69a73b6..bf73f8ca 100644 --- a/src/gui/viewport.h +++ b/src/gui/viewport.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef VIEWPORT_H -#define VIEWPORT_H +#pragma once #include "eventlistener.h" #include "position.h" @@ -221,5 +220,3 @@ class Viewport : public WindowContainer, public gcn::MouseListener, }; extern Viewport *viewport; /**< The viewport. */ - -#endif diff --git a/src/gui/widgets/avatarlistbox.cpp b/src/gui/widgets/avatarlistbox.cpp index ec3327b2..4a806d04 100644 --- a/src/gui/widgets/avatarlistbox.cpp +++ b/src/gui/widgets/avatarlistbox.cpp @@ -32,33 +32,21 @@ #include <guichan/font.hpp> -int AvatarListBox::instances = 0; -Image *AvatarListBox::onlineIcon = nullptr; -Image *AvatarListBox::offlineIcon = nullptr; - AvatarListBox::AvatarListBox(AvatarListModel *model): ListBox(model) { - instances++; - - if (instances == 1) - { - onlineIcon = Theme::getImageFromTheme("circle-green.png"); - offlineIcon = Theme::getImageFromTheme("circle-gray.png"); - } - setWidth(200); } -AvatarListBox::~AvatarListBox() +unsigned int AvatarListBox::getRowHeight() const { - instances--; + auto rowHeight = ListBox::getRowHeight(); - if (instances == 0) - { - onlineIcon->decRef(); - offlineIcon->decRef(); - } + auto theme = gui->getTheme(); + if (auto onlineIcon = theme->getIcon("online")) + rowHeight = std::max<unsigned>(rowHeight, onlineIcon->getHeight() + 2); + + return rowHeight; } void AvatarListBox::draw(gcn::Graphics *gcnGraphics) @@ -66,34 +54,42 @@ void AvatarListBox::draw(gcn::Graphics *gcnGraphics) if (!mListModel) return; - auto* model = static_cast<AvatarListModel*>(mListModel); + auto *model = static_cast<AvatarListModel *>(mListModel); + auto *graphics = static_cast<Graphics *>(gcnGraphics); - updateAlpha(); - - auto *graphics = static_cast<Graphics*>(gcnGraphics); - - graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, - (int) (mAlpha * 255.0f))); graphics->setFont(getFont()); - const int fontHeight = getFont()->getHeight(); + const int rowHeight = getRowHeight(); // Draw filled rectangle around the selected list element if (mSelected >= 0) - graphics->fillRectangle(gcn::Rectangle(0, fontHeight * mSelected, - getWidth(), fontHeight)); + { + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + highlightColor.a = gui->getTheme()->getGuiAlpha(); + graphics->setColor(highlightColor); + graphics->fillRectangle(gcn::Rectangle(0, rowHeight * mSelected, + getWidth(), rowHeight)); + } + + auto theme = gui->getTheme(); + auto onlineIcon = theme->getIcon("online"); + auto offlineIcon = theme->getIcon("offline"); // Draw the list elements graphics->setColor(Theme::getThemeColor(Theme::TEXT)); for (int i = 0, y = 0; i < model->getNumberOfElements(); - ++i, y += fontHeight) + ++i, y += rowHeight) { Avatar *a = model->getAvatarAt(i); + int x = 1; + // Draw online status - Image *icon = a->getOnline() ? onlineIcon : offlineIcon; - if (icon) - graphics->drawImage(icon, 2, y + 1); + if (const Image *icon = a->getOnline() ? onlineIcon : offlineIcon) + { + graphics->drawImage(icon, x, y + (rowHeight - icon->getHeight()) / 2); + x += icon->getWidth() + 4; + } if (a->getDisplayBold()) graphics->setFont(boldFont); @@ -111,7 +107,7 @@ void AvatarListBox::draw(gcn::Graphics *gcnGraphics) } // Draw Name - graphics->drawText(text, 15, y); + graphics->drawText(text, x, y); if (a->getDisplayBold()) graphics->setFont(getFont()); diff --git a/src/gui/widgets/avatarlistbox.h b/src/gui/widgets/avatarlistbox.h index 7ee36d1e..1f51935f 100644 --- a/src/gui/widgets/avatarlistbox.h +++ b/src/gui/widgets/avatarlistbox.h @@ -18,8 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_GUILDLISTBOX_H -#define GUI_GUILDLISTBOX_H +#pragma once #include "avatar.h" @@ -43,7 +42,7 @@ class AvatarListBox : public ListBox public: AvatarListBox(AvatarListModel *model); - ~AvatarListBox() override; + unsigned int getRowHeight() const override; /** * Draws the list box. @@ -51,11 +50,4 @@ public: void draw(gcn::Graphics *gcnGraphics) override; void mousePressed(gcn::MouseEvent &event) override; - -private: - static int instances; - static Image *onlineIcon; - static Image *offlineIcon; }; - -#endif diff --git a/src/gui/widgets/browserbox.cpp b/src/gui/widgets/browserbox.cpp index 9eee9448..4fe85ae5 100644 --- a/src/gui/widgets/browserbox.cpp +++ b/src/gui/widgets/browserbox.cpp @@ -22,6 +22,9 @@ #include "gui/widgets/browserbox.h" +#include "keyboardconfig.h" +#include "textrenderer.h" + #include "gui/gui.h" #include "gui/truetypefont.h" #include "gui/widgets/linkhandler.h" @@ -38,9 +41,44 @@ #include <algorithm> +/** + * Check for key replacements in format "###key;" + */ +static void replaceKeys(std::string &text) +{ + auto keyStart = text.find("###"); + + while (keyStart != std::string::npos) + { + const auto keyEnd = text.find(";", keyStart + 3); + if (keyEnd == std::string::npos) + break; + + std::string_view key(text.data() + keyStart + 3, keyEnd - keyStart - 3); + + // Remove "key" prefix + if (key.size() > 3 && key.substr(0, 3) == "key") + key.remove_prefix(3); + + const auto keyName = keyboard.getKeyName(key); + if (!keyName.empty()) + { + text.replace(keyStart, keyEnd - keyStart + 1, keyName); + keyStart = text.find("###", keyStart + keyName.size()); + } + else + { + keyStart = text.find("###", keyEnd + 1); + } + } +} + + struct LayoutContext { - LayoutContext(gcn::Font *font); + LayoutContext(gcn::Font *font, const Palette &palette); + + LinePart linePart(int x, std::string text); int y = 0; gcn::Font *font; @@ -48,23 +86,39 @@ struct LayoutContext const int minusWidth; const int tildeWidth; int lineHeight; - gcn::Color selColor; const gcn::Color textColor; + const std::optional<gcn::Color> textOutlineColor; + gcn::Color color; + std::optional<gcn::Color> outlineColor; }; -LayoutContext::LayoutContext(gcn::Font *font) +inline LayoutContext::LayoutContext(gcn::Font *font, const Palette &palette) : font(font) , fontHeight(font->getHeight()) , minusWidth(font->getWidth("-")) , tildeWidth(font->getWidth("~")) , lineHeight(fontHeight) - , selColor(Theme::getThemeColor(Theme::TEXT)) - , textColor(Theme::getThemeColor(Theme::TEXT)) + , textColor(palette.getColor(Theme::TEXT)) + , textOutlineColor(palette.getOutlineColor(Theme::TEXT)) + , color(textColor) + , outlineColor(textOutlineColor) { if (auto *trueTypeFont = dynamic_cast<const TrueTypeFont*>(font)) lineHeight = trueTypeFont->getLineHeight(); } +inline LinePart LayoutContext::linePart(int x, std::string text) +{ + return { + x, + y, + color, + outlineColor, + std::move(text), + font + }; +} + BrowserBox::BrowserBox(Mode mode): mMode(mode) @@ -75,28 +129,38 @@ BrowserBox::BrowserBox(Mode mode): BrowserBox::~BrowserBox() = default; -void BrowserBox::addRow(const std::string &row) +void BrowserBox::addRows(std::string_view rows) +{ + std::string_view::size_type start = 0; + std::string_view::size_type end = 0; + while (end != std::string::npos) + { + end = rows.find('\n', start); + addRow(rows.substr(start, end - start)); + start = end + 1; + } +} + +void BrowserBox::addRow(std::string_view row) { TextRow &newRow = mTextRows.emplace_back(); // Use links and user defined colors if (mUseLinksAndUserColors) { - std::string tmp = row; - // Check for links in format "@@link|Caption@@" - auto idx1 = tmp.find("@@"); - while (idx1 != std::string::npos) + auto linkStart = row.find("@@"); + while (linkStart != std::string::npos) { - const auto idx2 = tmp.find("|", idx1); - const auto idx3 = tmp.find("@@", idx2); + const auto linkSep = row.find("|", linkStart); + const auto linkEnd = row.find("@@", linkSep); - if (idx2 == std::string::npos || idx3 == std::string::npos) + if (linkSep == std::string::npos || linkEnd == std::string::npos) break; BrowserLink &link = newRow.links.emplace_back(); - link.link = tmp.substr(idx1 + 2, idx2 - (idx1 + 2)); - link.caption = tmp.substr(idx2 + 1, idx3 - (idx2 + 1)); + link.link = row.substr(linkStart + 2, linkSep - (linkStart + 2)); + link.caption = row.substr(linkSep + 1, linkEnd - (linkSep + 1)); if (link.caption.empty()) { @@ -107,18 +171,18 @@ void BrowserBox::addRow(const std::string &row) link.caption = link.link; } - newRow.text += tmp.substr(0, idx1); + newRow.text += row.substr(0, linkStart); newRow.text += "##<" + link.caption; - tmp.erase(0, idx3 + 2); - if (!tmp.empty()) + row = row.substr(linkEnd + 2); + if (!row.empty()) { newRow.text += "##>"; } - idx1 = tmp.find("@@"); + linkStart = row.find("@@"); } - newRow.text += tmp; + newRow.text += row; } // Don't use links and user defined colors else @@ -126,8 +190,11 @@ void BrowserBox::addRow(const std::string &row) newRow.text = row; } + if (mEnableKeys) + replaceKeys(newRow.text); + // Layout the newly added row - LayoutContext context(getFont()); + LayoutContext context(getFont(), gui->getTheme()->getPalette(mPalette)); context.y = getHeight(); layoutTextRow(newRow, context); @@ -175,14 +242,14 @@ void BrowserBox::mousePressed(gcn::MouseEvent &event) if (mHoveredLink) { mLinkHandler->handleLink(mHoveredLink->link); - gui->setCursorType(Cursor::POINTER); + gui->setCursorType(Cursor::Pointer); } } void BrowserBox::mouseMoved(gcn::MouseEvent &event) { updateHoveredLink(event.getX(), event.getY()); - gui->setCursorType(mHoveredLink ? Cursor::HAND : Cursor::POINTER); + gui->setCursorType(mHoveredLink ? Cursor::Hand : Cursor::Pointer); event.consume(); // Suppress mouse cursor change by parent } @@ -204,19 +271,20 @@ void BrowserBox::draw(gcn::Graphics *graphics) if (mHoveredLink) { + auto &palette = gui->getTheme()->getPalette(mPalette); auto &link = *mHoveredLink; const gcn::Rectangle &rect = link.rect; if (mHighlightMode & BACKGROUND) { - graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT)); + graphics->setColor(palette.getColor(Theme::HIGHLIGHT)); graphics->fillRectangle(rect); } if (mHighlightMode & UNDERLINE) { - graphics->setColor(Theme::getThemeColor(Theme::HYPERLINK)); + graphics->setColor(palette.getColor(Theme::HYPERLINK)); graphics->drawLine(rect.x, rect.y + rect.height, rect.x + rect.width, @@ -233,34 +301,16 @@ void BrowserBox::draw(gcn::Graphics *graphics) if (part.y > yEnd) return; - auto font = part.font; - - // Handle text shadows - if (mShadows) - { - graphics->setColor(Theme::getThemeColor(Theme::SHADOW, - part.color.a / 2)); - - if (mOutline) - font->drawString(graphics, part.text, part.x + 2, part.y + 2); - else - font->drawString(graphics, part.text, part.x + 1, part.y + 1); - } - - if (mOutline) - { - // Text outline - graphics->setColor(Theme::getThemeColor(Theme::OUTLINE, - part.color.a / 4)); - font->drawString(graphics, part.text, part.x + 1, part.y); - font->drawString(graphics, part.text, part.x - 1, part.y); - font->drawString(graphics, part.text, part.x, part.y + 1); - font->drawString(graphics, part.text, part.x, part.y - 1); - } - - // the main text - graphics->setColor(part.color); - font->drawString(graphics, part.text, part.x, part.y); + TextRenderer::renderText(graphics, + part.text, + part.x, + part.y, + Graphics::LEFT, + part.color, + part.font, + part.outlineColor.has_value() || mOutline, + mShadows, + part.outlineColor); } } } @@ -270,13 +320,13 @@ void BrowserBox::draw(gcn::Graphics *graphics) */ void BrowserBox::relayoutText() { - LayoutContext context(getFont()); + LayoutContext context(getFont(), gui->getTheme()->getPalette(mPalette)); for (auto &row : mTextRows) layoutTextRow(row, context); mLastLayoutWidth = getWidth(); - mLayoutTimer.set(100); + mLayoutTimer.set(33); setHeight(context.y); } @@ -288,7 +338,8 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) { // each line starts with normal font in default color context.font = getFont(); - context.selColor = context.textColor; + context.color = context.textColor; + context.outlineColor = context.textOutlineColor; const int startY = context.y; row.parts.clear(); @@ -301,15 +352,7 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) if (startsWith(row.text, "---")) { for (x = 0; x < getWidth(); x += context.minusWidth - 1) - { - row.parts.push_back(LinePart { - x, - context.y, - context.selColor, - "-", - context.font - }); - } + row.parts.push_back(context.linePart(x, "-")); context.y += row.height; @@ -318,7 +361,9 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) return; } - gcn::Color prevColor = context.selColor; + auto &palette = gui->getTheme()->getPalette(mPalette); + auto prevColor = context.color; + auto prevOutlineColor = context.outlineColor; // TODO: Check if we must take texture size limits into account here // TODO: Check if some of the O(n) calls can be removed @@ -342,44 +387,38 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) const char c = row.text.at(start + 2); start += 3; - bool valid; - const gcn::Color &col = Theme::getThemeColor(c, valid); - - if (c == '>') - { - context.selColor = prevColor; - } - else if (c == '<') + switch (c) { - prevColor = context.selColor; - context.selColor = col; - } - else if (c == 'B') - { - context.font = boldFont; - } - else if (c == 'b') - { - context.font = getFont(); - } - else if (valid) - { - context.selColor = col; - } - else switch (c) - { - case '1': context.selColor = RED; break; - case '2': context.selColor = GREEN; break; - case '3': context.selColor = BLUE; break; - case '4': context.selColor = ORANGE; break; - case '5': context.selColor = YELLOW; break; - case '6': context.selColor = PINK; break; - case '7': context.selColor = PURPLE; break; - case '8': context.selColor = GRAY; break; - case '9': context.selColor = BROWN; break; - case '0': - default: - context.selColor = context.textColor; + case '>': + context.color = prevColor; + context.outlineColor = prevOutlineColor; + break; + case '<': + prevColor = context.color; + prevOutlineColor = context.outlineColor; + context.color = palette.getColor(Theme::HYPERLINK); + context.outlineColor = palette.getOutlineColor(Theme::HYPERLINK); + break; + case 'B': + context.font = boldFont; + break; + case 'b': + context.font = getFont(); + break; + default: { + const auto colorId = Theme::getColorIdForChar(c); + if (colorId) + { + context.color = palette.getColor(*colorId); + context.outlineColor = palette.getOutlineColor(*colorId); + } + else + { + context.color = context.textColor; + context.outlineColor = context.textOutlineColor; + } + break; + } } // Update the position of the links @@ -452,7 +491,8 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) row.parts.push_back(LinePart { getWidth() - context.tildeWidth, context.y, - context.selColor, + context.color, + context.outlineColor, "~", getFont() }); @@ -466,14 +506,7 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) wrapped = true; } - row.parts.push_back(LinePart { - x, - context.y, - context.selColor, - std::move(part), - context.font - }); - + row.parts.push_back(context.linePart(x, std::move(part))); row.width = std::max(row.width, x + partWidth); if (mMode == AUTO_WRAP && partWidth == 0) @@ -506,7 +539,7 @@ void BrowserBox::updateHoveredLink(int x, int y) void BrowserBox::maybeRelayoutText() { // Reduce relayouting frequency when there is a lot of text - if (mTextRows.size() > 100) + if (mTextRows.size() > 1000) if (!mLayoutTimer.passed()) return; diff --git a/src/gui/widgets/browserbox.h b/src/gui/widgets/browserbox.h index 7278bb59..142bba63 100644 --- a/src/gui/widgets/browserbox.h +++ b/src/gui/widgets/browserbox.h @@ -20,8 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef BROWSERBOX_H -#define BROWSERBOX_H +#pragma once #include "utils/time.h" @@ -52,6 +51,7 @@ struct LinePart int x; int y; gcn::Color color; + std::optional<gcn::Color> outlineColor; std::string text; gcn::Font *font; }; @@ -87,6 +87,8 @@ class BrowserBox : public gcn::Widget, */ void setLinkHandler(LinkHandler *handler) { mLinkHandler = handler; } + void setPalette(int palette) { mPalette = palette; } + /** * Sets the Highlight mode for links. */ @@ -118,9 +120,19 @@ class BrowserBox : public gcn::Widget, void disableLinksAndUserColors() { mUseLinksAndUserColors = false; } /** + * Enable or disable the replacement of keys. + */ + void setEnableKeys(bool enable) { mEnableKeys = enable; } + + /** + * Adds one or more text rows to the browser, separated by '\n'. + */ + void addRows(std::string_view rows); + + /** * Adds a text row to the browser. */ - void addRow(const std::string &row); + void addRow(std::string_view row); /** * Remove all rows. @@ -145,29 +157,6 @@ class BrowserBox : public gcn::Widget, void drawFrame(gcn::Graphics *) override {} /** - * BrowserBox colors. - * - * NOTES (by Javila): - * - color values is "0x" prefix followed by HTML color style. - * - we can add up to 10 different colors: [0..9]. - * - not all colors will be fine with all backgrounds due transparent - * windows and widgets. So, I think it's better keep BrowserBox - * opaque (white background) by default. - */ - enum - { - RED = 0xff0000, /**< Color 1 */ - GREEN = 0x009000, /**< Color 2 */ - BLUE = 0x0000ff, /**< Color 3 */ - ORANGE = 0xe0980e, /**< Color 4 */ - YELLOW = 0xf1dc27, /**< Color 5 */ - PINK = 0xff00d8, /**< Color 6 */ - PURPLE = 0x8415e2, /**< Color 7 */ - GRAY = 0x919191, /**< Color 8 */ - BROWN = 0x8e4c17 /**< Color 9 */ - }; - - /** * Highlight modes for links. * This can be used for a bitmask. */ @@ -186,16 +175,16 @@ class BrowserBox : public gcn::Widget, std::deque<TextRow> mTextRows; LinkHandler *mLinkHandler = nullptr; + int mPalette = 0; Mode mMode; unsigned int mHighlightMode = UNDERLINE | BACKGROUND; int mWrapIndent = 0; bool mShadows = false; bool mOutline = false; bool mUseLinksAndUserColors = true; + bool mEnableKeys = false; std::optional<BrowserLink> mHoveredLink; unsigned int mMaxRows = 0; int mLastLayoutWidth = 0; Timer mLayoutTimer; }; - -#endif diff --git a/src/gui/widgets/button.cpp b/src/gui/widgets/button.cpp index 274f329b..31c3a677 100644 --- a/src/gui/widgets/button.cpp +++ b/src/gui/widgets/button.cpp @@ -21,25 +21,22 @@ #include "gui/widgets/button.h" -#include "configuration.h" #include "graphics.h" +#include "gui/gui.h" #include "gui/textpopup.h" #include "resources/image.h" #include "resources/theme.h" - -#include "utils/dtor.h" +#include "textrenderer.h" #include <guichan/exception.hpp> #include <guichan/font.hpp> int Button::mInstances = 0; -float Button::mAlpha = 1.0; -ImageRect *Button::mButton; TextPopup *Button::mTextPopup = nullptr; -enum{ +enum { BUTTON_STANDARD, // 0 BUTTON_HIGHLIGHTED, // 1 BUTTON_PRESSED, // 2 @@ -47,20 +44,6 @@ enum{ BUTTON_COUNT // 4 - Must be last. }; -struct ButtonData -{ - char const *file; - int gridX; - int gridY; -}; - -static ButtonData const data[BUTTON_COUNT] = { - { "button.png", 0, 0 }, - { "buttonhi.png", 9, 4 }, - { "buttonpress.png", 16, 19 }, - { "button_disabled.png", 25, 23 } -}; - Button::Button() { init(); @@ -80,18 +63,19 @@ Button::Button(const std::string &caption, const std::string &actionEventId, adjustSize(); } -bool Button::setButtonIcon(const std::string& iconFile) +Button::~Button() = default; + +bool Button::setButtonIcon(const std::string &iconFile) { // We clean up possible older references. - if (mButtonIcon) - removeButtonIcon(); + removeButtonIcon(); // If nothing relevant was set, we can quit now. if (iconFile.empty()) return false; // Load the icon frames. - Image *btnIcons = Theme::getImageFromTheme(iconFile); + auto btnIcons = Theme::getImageFromTheme(iconFile); if (!btnIcons) return false; @@ -101,141 +85,76 @@ bool Button::setButtonIcon(const std::string& iconFile) if (frameWidth > 0 && frameHeight > 0) { - mButtonIcon = new Image*[BUTTON_COUNT]; + mButtonIcon.resize(BUTTON_COUNT); + for (int mode = 0; mode < BUTTON_COUNT; ++mode) { - mButtonIcon[mode] = btnIcons->getSubImage(mode * frameWidth, 0, - frameWidth, frameHeight); + mButtonIcon[mode].reset( + btnIcons->getSubImage(mode * frameWidth, 0, frameWidth, frameHeight)); } adjustSize(); } - btnIcons->decRef(); - return (mButtonIcon); + return !mButtonIcon.empty(); } -void Button::removeButtonIcon(bool adjustButtonSize) +void Button::removeButtonIcon() { - if (!mButtonIcon) + if (mButtonIcon.empty()) return; - // Delete potential button icons - for (int mode = 0; mode < BUTTON_COUNT; ++mode) - { - delete mButtonIcon[mode]; - mButtonIcon[mode] = nullptr; - } - delete[] mButtonIcon; - mButtonIcon = nullptr; - - if (adjustButtonSize) - adjustSize(); + mButtonIcon.clear(); + adjustSize(); } void Button::init() { - setFrameSize(0); + auto &skin = gui->getTheme()->getSkin(SkinType::Button); + setFrameSize(skin.frameSize); + setSpacing(skin.padding); if (mInstances == 0) { - // Load the skin - mButton = new ImageRect[BUTTON_COUNT]; - - for (int mode = 0; mode < BUTTON_COUNT; ++mode) - { - Image *modeImage = Theme::getImageFromTheme(data[mode].file); - int a = 0; - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - mButton[mode].grid[a] = modeImage->getSubImage( - data[x].gridX, data[y].gridY, - data[x + 1].gridX - data[x].gridX + 1, - data[y + 1].gridY - data[y].gridY + 1); - a++; - } - } - modeImage->decRef(); - } - updateAlpha(); - - // Load the popup + // Create the tooltip popup. It is shared by all buttons and will get + // deleted by the WindowContainer. if (!mTextPopup) - mTextPopup = new TextPopup(); + mTextPopup = new TextPopup; } mInstances++; } -Button::~Button() +void Button::draw(gcn::Graphics *graphics) { - mInstances--; - - if (mInstances == 0) - { - for (int mode = 0; mode < BUTTON_COUNT; ++mode) - { - std::for_each(mButton[mode].grid, mButton[mode].grid + 9, - dtor<Image*>()); - } - delete[] mButton; + WidgetState widgetState(this); + if (mHasMouse) + widgetState.flags |= STATE_HOVERED; + if (isPressed()) + widgetState.flags |= STATE_SELECTED; - // Remove the popup - delete mTextPopup; - mTextPopup = nullptr; - } - // Don' try to readjust the size when it's about to be deleted. - removeButtonIcon(false); -} + auto &skin = gui->getTheme()->getSkin(SkinType::Button); + skin.draw(static_cast<Graphics *>(graphics), widgetState); -void Button::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); + auto skinState = skin.getState(widgetState.flags); + auto font = (skinState && skinState->textFormat.bold) ? boldFont : getFont(); - if (mAlpha != alpha) - { - mAlpha = alpha; - for (int mode = 0; mode < BUTTON_COUNT; ++mode) - { - mButton[mode].setAlpha(mAlpha); - } - } -} - -void Button::draw(gcn::Graphics *graphics) -{ int mode; - if (!isEnabled()) + if (widgetState.flags & STATE_DISABLED) mode = BUTTON_DISABLED; - else if (isPressed()) + else if (widgetState.flags & STATE_SELECTED) mode = BUTTON_PRESSED; - else if (mHasMouse || isFocused()) + else if (widgetState.flags & (STATE_HOVERED | STATE_FOCUSED)) mode = BUTTON_HIGHLIGHTED; else mode = BUTTON_STANDARD; - updateAlpha(); - - static_cast<Graphics*>(graphics)-> - drawImageRect(0, 0, getWidth(), getHeight(), mButton[mode]); - - if (mode == BUTTON_DISABLED) - graphics->setColor(Theme::getThemeColor(Theme::BUTTON_DISABLED)); - else - graphics->setColor(Theme::getThemeColor(Theme::BUTTON)); - + Image *icon = mButtonIcon.empty() ? nullptr : mButtonIcon[mode].get(); int textX = 0; - int textY = getHeight() / 2 - getFont()->getHeight() / 2; + int textY = getHeight() / 2 - font->getHeight() / 2; int btnIconX = 0; - int btnIconY = getHeight() / 2 - - ((mButtonIcon && mButtonIcon[mode]) ? - mButtonIcon[mode]->getHeight() / 2 : 0); - - int btnIconWidth = (mButtonIcon && mButtonIcon[mode]) ? - mButtonIcon[mode]->getWidth() : 0; + int btnIconY = getHeight() / 2 - (icon ? icon->getHeight() / 2 : 0); + int btnIconWidth = icon ? icon->getWidth() : 0; switch (getAlignment()) { @@ -243,7 +162,7 @@ void Button::draw(gcn::Graphics *graphics) if (btnIconWidth) { btnIconX = 4; - textX = btnIconX + mButtonIcon[mode]->getWidth() + 2; + textX = btnIconX + icon->getWidth() + 2; } else { @@ -253,9 +172,8 @@ void Button::draw(gcn::Graphics *graphics) case gcn::Graphics::CENTER: if (btnIconWidth) { - btnIconX = getWidth() / 2 - (getFont()->getWidth(mCaption) - + mButtonIcon[mode]->getWidth() + 2) / 2; - textX = getWidth() / 2 + mButtonIcon[mode]->getWidth() / 2 + 2; + btnIconX = (getWidth() - font->getWidth(mCaption) - icon->getWidth() - 2) / 2; + textX = (getWidth() + icon->getWidth()) / 2 + 2; } else { @@ -264,15 +182,13 @@ void Button::draw(gcn::Graphics *graphics) break; case gcn::Graphics::RIGHT: if (btnIconWidth) - btnIconX = getWidth() - 4 - getFont()->getWidth(mCaption) - 2; + btnIconX = getWidth() - 4 - font->getWidth(mCaption) - 2; textX = getWidth() - 4; break; default: throw GCN_EXCEPTION("Button::draw(). Unknown alignment."); } - graphics->setFont(getFont()); - if (isPressed()) { textX++; textY++; @@ -280,23 +196,32 @@ void Button::draw(gcn::Graphics *graphics) } if (btnIconWidth) - static_cast<Graphics*>(graphics)->drawImage(mButtonIcon[mode], - btnIconX, btnIconY); - graphics->drawText(getCaption(), textX, textY, getAlignment()); + static_cast<Graphics *>(graphics)->drawImage(icon, btnIconX, btnIconY); + + if (auto skinState = skin.getState(widgetState.flags)) + { + auto &textFormat = skinState->textFormat; + TextRenderer::renderText(static_cast<Graphics *>(graphics), + getCaption(), + textX, + textY, + getAlignment(), + font, + textFormat); + } } void Button::adjustSize() { // Size of the image button. int iconWidth = 0, iconHeight = 0; - if (mButtonIcon) + if (!mButtonIcon.empty()) { for (int mode = 0; mode < BUTTON_COUNT; ++mode) { - iconWidth = std::max(iconWidth, mButtonIcon[mode] ? - mButtonIcon[mode]->getWidth() + 2 : 0); - iconHeight = std::max(iconHeight, mButtonIcon[mode] ? - mButtonIcon[mode]->getHeight() : 0); + const Image *icon = mButtonIcon[mode].get(); + iconWidth = std::max(iconWidth, icon->getWidth() + 2); + iconHeight = std::max(iconHeight, icon->getHeight()); } } @@ -311,16 +236,9 @@ void Button::setCaption(const std::string& caption) adjustSize(); } -void Button::logic() -{ - gcn::Button::logic(); - mTextPopup->logic(); -} - void Button::mouseMoved(gcn::MouseEvent &event) { gcn::Button::mouseMoved(event); - mTextPopup->mouseMoved(event); int x = event.getX(); int y = event.getY(); @@ -344,7 +262,6 @@ void Button::mouseMoved(gcn::MouseEvent &event) void Button::mouseExited(gcn::MouseEvent &event) { gcn::Button::mouseExited(event); - mTextPopup->mouseExited(event); mTextPopup->setVisible(false); } diff --git a/src/gui/widgets/button.h b/src/gui/widgets/button.h index a09b4445..97f7307b 100644 --- a/src/gui/widgets/button.h +++ b/src/gui/widgets/button.h @@ -19,12 +19,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef BUTTON_H -#define BUTTON_H +#pragma once #include <guichan/widgets/button.hpp> -class ImageRect; +#include <memory> +#include <vector> + class Image; class TextPopup; @@ -55,11 +56,6 @@ class Button : public gcn::Button */ void draw(gcn::Graphics *graphics) override; - /** - * Update the alpha value to the button components. - */ - void updateAlpha(); - void adjustSize(); void setCaption(const std::string &caption); @@ -82,27 +78,22 @@ class Button : public gcn::Button void setButtonPopupText(const std::string &text) { mPopupText = text; } - void logic() override; void mouseMoved(gcn::MouseEvent &event) override; void mouseExited(gcn::MouseEvent &event) override; private: void init(); - void removeButtonIcon(bool adjustButtonSize = true); + void removeButtonIcon(); - static ImageRect* mButton; /**< Button state graphics */ static int mInstances; /**< Number of button instances */ - static float mAlpha; - Image** mButtonIcon = nullptr; /**< Button Icons graphics */ + std::vector<std::unique_ptr<Image>> mButtonIcon; /**< Button Icons graphics */ /** * The buttons popup * @note: This is a global object. One for all the buttons. */ - static TextPopup* mTextPopup; + static TextPopup *mTextPopup; std::string mPopupText; /**< the current button text */ }; - -#endif diff --git a/src/gui/widgets/channeltab.h b/src/gui/widgets/channeltab.h index 2894dacd..d6dc1268 100644 --- a/src/gui/widgets/channeltab.h +++ b/src/gui/widgets/channeltab.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CHANNELTAB_H -#define CHANNELTAB_H +#pragma once #include "chattab.h" @@ -51,5 +50,3 @@ class ChannelTab : public ChatTab private: Channel *mChannel; }; - -#endif // CHANNELTAB_H diff --git a/src/gui/widgets/chattab.cpp b/src/gui/widgets/chattab.cpp index 485de566..03c3270a 100644 --- a/src/gui/widgets/chattab.cpp +++ b/src/gui/widgets/chattab.cpp @@ -160,7 +160,7 @@ void ChatTab::chatLog(std::string line, Own own, bool ignoreRecord) tmp.nick = strprintf(_("Global announcement from %s:"), tmp.nick.c_str()); tmp.nick += " "; - lineColor = "##1"; // Equiv. to BrowserBox::RED + lineColor = "##g"; } break; case BY_PLAYER: diff --git a/src/gui/widgets/chattab.h b/src/gui/widgets/chattab.h index 3e770fe1..dfc07638 100644 --- a/src/gui/widgets/chattab.h +++ b/src/gui/widgets/chattab.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CHATTAB_H -#define CHATTAB_H +#pragma once #include "gui/chatwindow.h" @@ -135,5 +134,3 @@ class ChatTab : public Tab, public AutoCompleteLister, public EventListener }; extern ChatTab *localChatTab; - -#endif // CHATTAB_H diff --git a/src/gui/widgets/checkbox.cpp b/src/gui/widgets/checkbox.cpp index 274855fd..e6079f2f 100644 --- a/src/gui/widgets/checkbox.cpp +++ b/src/gui/widgets/checkbox.cpp @@ -21,121 +21,43 @@ #include "gui/widgets/checkbox.h" -#include "configuration.h" -#include "graphics.h" +#include "textrenderer.h" -#include "resources/image.h" +#include "gui/gui.h" #include "resources/theme.h" -int CheckBox::instances = 0; -float CheckBox::mAlpha = 1.0; -Image *CheckBox::checkBoxNormal; -Image *CheckBox::checkBoxChecked; -Image *CheckBox::checkBoxDisabled; -Image *CheckBox::checkBoxDisabledChecked; -Image *CheckBox::checkBoxNormalHi; -Image *CheckBox::checkBoxCheckedHi; +#include <guichan/font.hpp> -CheckBox::CheckBox(const std::string &caption, bool selected): - gcn::CheckBox(caption, selected) +CheckBox::CheckBox(const std::string &caption, bool selected) + : gcn::CheckBox(caption, selected) { - if (instances == 0) - { - Image *checkBox = Theme::getImageFromTheme("checkbox.png"); - checkBoxNormal = checkBox->getSubImage(0, 0, 9, 10); - checkBoxChecked = checkBox->getSubImage(9, 0, 9, 10); - checkBoxDisabled = checkBox->getSubImage(18, 0, 9, 10); - checkBoxDisabledChecked = checkBox->getSubImage(27, 0, 9, 10); - checkBoxNormalHi = checkBox->getSubImage(36, 0, 9, 10); - checkBoxCheckedHi = checkBox->getSubImage(45, 0, 9, 10); - checkBoxNormal->setAlpha(mAlpha); - checkBoxChecked->setAlpha(mAlpha); - checkBoxDisabled->setAlpha(mAlpha); - checkBoxDisabledChecked->setAlpha(mAlpha); - checkBoxNormalHi->setAlpha(mAlpha); - checkBoxCheckedHi->setAlpha(mAlpha); - checkBox->decRef(); - } - - instances++; -} - -CheckBox::~CheckBox() -{ - instances--; - - if (instances == 0) - { - delete checkBoxNormal; - delete checkBoxChecked; - delete checkBoxDisabled; - delete checkBoxDisabledChecked; - delete checkBoxNormalHi; - delete checkBoxCheckedHi; - } + auto &skin = gui->getTheme()->getSkin(SkinType::CheckBox); + setWidth(skin.getMinWidth() + 2 * skin.padding + skin.spacing + getFont()->getWidth(caption)); + setHeight(skin.getMinHeight() + 2 * skin.padding); } void CheckBox::draw(gcn::Graphics* graphics) { - drawBox(graphics); - - graphics->setFont(getFont()); - graphics->setColor(Theme::getThemeColor(Theme::TEXT)); - - const int h = getHeight() + getHeight() / 2; - - graphics->drawText(getCaption(), h - 2, 0); -} - -void CheckBox::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (mAlpha != alpha) - { - mAlpha = alpha; - checkBoxNormal->setAlpha(mAlpha); - checkBoxChecked->setAlpha(mAlpha); - checkBoxDisabled->setAlpha(mAlpha); - checkBoxDisabledChecked->setAlpha(mAlpha); - checkBoxNormal->setAlpha(mAlpha); - checkBoxCheckedHi->setAlpha(mAlpha); - } -} + WidgetState widgetState(this); + if (mHasMouse) + widgetState.flags |= STATE_HOVERED; + if (isSelected()) + widgetState.flags |= STATE_SELECTED; -void CheckBox::drawBox(gcn::Graphics* graphics) -{ - Image *box; + auto &skin = gui->getTheme()->getSkin(SkinType::CheckBox); + skin.draw(static_cast<Graphics *>(graphics), widgetState); - if (isEnabled()) - { - if (isSelected()) - { - if (mHasMouse) - box = checkBoxCheckedHi; - else - box = checkBoxChecked; - } - else - { - if (mHasMouse) - box = checkBoxNormalHi; - else - box = checkBoxNormal; - } - } - else + if (auto skinState = skin.getState(widgetState.flags)) { - if (isSelected()) - box = checkBoxDisabledChecked; - else - box = checkBoxDisabled; + auto &textFormat = skinState->textFormat; + TextRenderer::renderText(static_cast<Graphics *>(graphics), + getCaption(), + skin.getMinWidth() + skin.padding + skin.spacing, + skin.padding, + Graphics::LEFT, + textFormat.bold ? boldFont : getFont(), + textFormat); } - - updateAlpha(); - - static_cast<Graphics*>(graphics)->drawImage(box, 2, 2); } void CheckBox::mouseEntered(gcn::MouseEvent& event) diff --git a/src/gui/widgets/checkbox.h b/src/gui/widgets/checkbox.h index f77b1761..ea1a20e7 100644 --- a/src/gui/widgets/checkbox.h +++ b/src/gui/widgets/checkbox.h @@ -19,13 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CHECKBOX_H -#define CHECKBOX_H +#pragma once #include <guichan/widgets/checkbox.hpp> -class Image; - /** * Check box widget. Same as the Guichan check box but with custom look. * @@ -36,43 +33,26 @@ class CheckBox : public gcn::CheckBox public: CheckBox(const std::string &caption, bool selected = false); - ~CheckBox() override; - /** * Draws the caption, then calls drawBox to draw the check box. */ - void draw(gcn::Graphics* graphics) override; - - /** - * Update the alpha value to the checkbox components. - */ - void updateAlpha(); + void draw(gcn::Graphics *graphics) override; /** - * Draws the check box, not the caption. + * Overridden because box is drawn in CheckBox::draw. */ - void drawBox(gcn::Graphics* graphics) override; + void drawBox(gcn::Graphics *graphics) override {} /** * Called when the mouse enteres the widget area. */ - void mouseEntered(gcn::MouseEvent& event) override; + void mouseEntered(gcn::MouseEvent &event) override; /** * Called when the mouse leaves the widget area. */ - void mouseExited(gcn::MouseEvent& event) override; + void mouseExited(gcn::MouseEvent &event) override; private: - static int instances; - static float mAlpha; bool mHasMouse = false; - static Image *checkBoxNormal; - static Image *checkBoxChecked; - static Image *checkBoxDisabled; - static Image *checkBoxDisabledChecked; - static Image *checkBoxNormalHi; - static Image *checkBoxCheckedHi; }; - -#endif diff --git a/src/gui/widgets/container.h b/src/gui/widgets/container.h index ef44c8cd..54b7950b 100644 --- a/src/gui/widgets/container.h +++ b/src/gui/widgets/container.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_CONTAINER_H -#define GUI_CONTAINER_H +#pragma once #include <guichan/widgets/container.hpp> @@ -33,7 +32,7 @@ class LayoutHelper; * A widget container. * * The main difference between the standard Guichan container and this one is - * that childs added to this container are automatically deleted when the + * that children added to this container are automatically deleted when the * container is deleted. * * This container is also non-opaque by default. @@ -44,6 +43,9 @@ class Container : public gcn::Container Container(); ~Container() override; + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics *graphics) override {} + protected: /** * Gets the layout handler for this container. @@ -63,5 +65,3 @@ class Container : public gcn::Container private: LayoutHelper *mLayoutHelper = nullptr; }; - -#endif diff --git a/src/gui/widgets/desktop.cpp b/src/gui/widgets/desktop.cpp index c8ded9f5..e424beec 100644 --- a/src/gui/widgets/desktop.cpp +++ b/src/gui/widgets/desktop.cpp @@ -105,7 +105,7 @@ void Desktop::setBestFittingWallpaper() return; ResourceManager *resman = ResourceManager::getInstance(); - auto wallpaper = resman->getImageRef(wallpaperName); + auto wallpaper = resman->getImage(wallpaperName); if (wallpaper) { diff --git a/src/gui/widgets/desktop.h b/src/gui/widgets/desktop.h index 5909ac72..a7aa4a1e 100644 --- a/src/gui/widgets/desktop.h +++ b/src/gui/widgets/desktop.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef DESKTOP_H -#define DESKTOP_H +#pragma once #include "guichanfwd.h" @@ -66,5 +65,3 @@ class Desktop : public Container, gcn::WidgetListener ResourceRef<Image> mWallpaper; gcn::Label *mVersionLabel; }; - -#endif // DESKTOP_H diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp index 8811eb8d..7d8ad0e5 100644 --- a/src/gui/widgets/dropdown.cpp +++ b/src/gui/widgets/dropdown.cpp @@ -21,127 +21,44 @@ #include "gui/widgets/dropdown.h" -#include "configuration.h" #include "graphics.h" +#include "gui/gui.h" #include "gui/sdlinput.h" #include "gui/widgets/listbox.h" #include "gui/widgets/scrollarea.h" -#include "resources/image.h" #include "resources/theme.h" -#include "utils/dtor.h" - -#include <algorithm> - -int DropDown::instances = 0; -Image *DropDown::buttons[2][2]; -ImageRect DropDown::skin; -float DropDown::mAlpha = 1.0; +#include <guichan/font.hpp> DropDown::DropDown(gcn::ListModel *listModel): gcn::DropDown::DropDown(listModel, new ScrollArea, new ListBox(listModel)) { - setFrameSize(2); - - // Initialize graphics - if (instances == 0) - { - // Load the background skin - - // Get the button skin - buttons[1][0] = Theme::getImageFromTheme("vscroll_up_default.png"); - buttons[0][0] = Theme::getImageFromTheme("vscroll_down_default.png"); - buttons[1][1] = Theme::getImageFromTheme("vscroll_up_pressed.png"); - buttons[0][1] = Theme::getImageFromTheme("vscroll_down_pressed.png"); - - buttons[0][0]->setAlpha(mAlpha); - buttons[0][1]->setAlpha(mAlpha); - buttons[1][0]->setAlpha(mAlpha); - buttons[1][1]->setAlpha(mAlpha); - - // get the border skin - Image *boxBorder = Theme::getImageFromTheme("deepbox.png"); - int gridx[4] = {0, 3, 28, 31}; - int gridy[4] = {0, 3, 28, 31}; - int a = 0; - - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - skin.grid[a] = boxBorder->getSubImage(gridx[x], gridy[y], - gridx[x + 1] - - gridx[x] + 1, - gridy[y + 1] - - gridy[y] + 1); - a++; - } - } + auto &skin = gui->getTheme()->getSkin(SkinType::DropDownFrame); + setFrameSize(skin.frameSize); + mPadding = skin.padding; - skin.setAlpha(mAlpha); - - boxBorder->decRef(); - } - - instances++; + setHeight(getFont()->getHeight() + 2 * mPadding); } DropDown::~DropDown() { - instances--; - // Free images memory - if (instances == 0) - { - buttons[0][0]->decRef(); - buttons[0][1]->decRef(); - buttons[1][0]->decRef(); - buttons[1][1]->decRef(); - - std::for_each(skin.grid, skin.grid + 9, dtor<Image*>()); - } - delete mScrollArea; } -void DropDown::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (mAlpha != alpha) - { - mAlpha = alpha; - - buttons[0][0]->setAlpha(mAlpha); - buttons[0][1]->setAlpha(mAlpha); - buttons[1][0]->setAlpha(mAlpha); - buttons[1][1]->setAlpha(mAlpha); - - skin.setAlpha(mAlpha); - } -} - void DropDown::draw(gcn::Graphics* graphics) { - int h; + const int h = mDroppedDown ? mFoldedUpHeight : getHeight(); - if (mDroppedDown) - h = mFoldedUpHeight; - else - h = getHeight(); - - updateAlpha(); - - const int alpha = (int) (mAlpha * 255.0f); + const int alpha = gui->getTheme()->getGuiAlpha(); gcn::Color faceColor = getBaseColor(); faceColor.a = alpha; - const gcn::Color *highlightColor = &Theme::getThemeColor(Theme::HIGHLIGHT, - alpha); + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + highlightColor.a = alpha; gcn::Color shadowColor = faceColor - 0x303030; shadowColor.a = alpha; @@ -149,13 +66,16 @@ void DropDown::draw(gcn::Graphics* graphics) { graphics->setFont(getFont()); graphics->setColor(Theme::getThemeColor(Theme::TEXT)); - graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()), 1, 0); + graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()), + mPadding, + mPadding); } if (isFocused()) { - graphics->setColor(*highlightColor); - graphics->drawRectangle(gcn::Rectangle(0, 0, getWidth() - h, h)); + graphics->setColor(highlightColor); + graphics->drawRectangle( + gcn::Rectangle(mPadding, mPadding, getWidth() - h - mPadding * 2, h - 2 * mPadding)); } drawButton(graphics); @@ -166,7 +86,7 @@ void DropDown::draw(gcn::Graphics* graphics) // Draw two lines separating the ListBox with selected // element view. - graphics->setColor(*highlightColor); + graphics->setColor(highlightColor); graphics->drawLine(0, h, getWidth(), h); graphics->setColor(shadowColor); graphics->drawLine(0, h + 1, getWidth(), h + 1); @@ -176,18 +96,64 @@ void DropDown::draw(gcn::Graphics* graphics) void DropDown::drawFrame(gcn::Graphics *graphics) { const int bs = getFrameSize(); - const int w = getWidth() + bs * 2; - const int h = getHeight() + bs * 2; - static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, skin); + WidgetState state(this); + state.width += bs * 2; + state.height += bs * 2; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::DropDownFrame, state); +} + +// Overridden so that we can take mPadding into account +void DropDown::adjustHeight() +{ + const int listBoxHeight = mListBox->getHeight(); + int height = getFont()->getHeight() + 2 * mPadding; + + // The addition/subtraction of 2 compensates for the seperation lines + // seperating the selected element view and the scroll area. + + if (mDroppedDown && getParent()) + { + int availableHeight = getParent()->getChildrenArea().height - getY(); + + if (listBoxHeight > availableHeight - height - 2) + { + mScrollArea->setHeight(availableHeight - height - 2); + height = availableHeight; + } + else + { + height += listBoxHeight + 2; + mScrollArea->setHeight(listBoxHeight); + } + } + + setHeight(height); + + mScrollArea->setWidth(getWidth()); + // Resize the ListBox to exactly fit the ScrollArea. + mListBox->setWidth(mScrollArea->getChildrenArea().width); + mScrollArea->setPosition(0, 0); } void DropDown::drawButton(gcn::Graphics *graphics) { - int height = mDroppedDown ? mFoldedUpHeight : getHeight(); + WidgetState state(this); + if (mDroppedDown) + { + state.height = mFoldedUpHeight; + state.flags |= STATE_SELECTED; + } + if (mPushed) + state.flags |= STATE_HOVERED; + + auto &skin = gui->getTheme()->getSkin(SkinType::DropDownButton); + + // FIXME: Needs support for setting alignment in the theme. + state.x = state.width - skin.getMinWidth(); - static_cast<Graphics*>(graphics)-> - drawImage(buttons[mDroppedDown][mPushed], getWidth() - height + 2, 1); + skin.draw(static_cast<Graphics *>(graphics), state); } // -- KeyListener notifications @@ -253,3 +219,32 @@ void DropDown::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent) mouseEvent.consume(); distributeActionEvent(); } + +// Overridden to call our version of adjustHeight +void DropDown::dropDown() +{ + if (!mDroppedDown) + { + mDroppedDown = true; + mFoldedUpHeight = getHeight(); + adjustHeight(); + + if (getParent()) + { + getParent()->moveToTop(this); + } + } + + mListBox->requestFocus(); +} + +// Overridden to call our version of adjustHeight +void DropDown::foldUp() +{ + if (mDroppedDown) + { + mDroppedDown = false; + adjustHeight(); + mInternalFocusHandler.focusNone(); + } +} diff --git a/src/gui/widgets/dropdown.h b/src/gui/widgets/dropdown.h index f92c7dd5..a5e2e2f7 100644 --- a/src/gui/widgets/dropdown.h +++ b/src/gui/widgets/dropdown.h @@ -19,14 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef DROPDOWN_H -#define DROPDOWN_H +#pragma once #include <guichan/widgets/dropdown.hpp> -class Image; -class ImageRect; - /** * A drop down box from which you can select different values. * @@ -47,15 +43,12 @@ class DropDown : public gcn::DropDown ~DropDown() override; - /** - * Update the alpha value to the graphic components. - */ - static void updateAlpha(); - void draw(gcn::Graphics *graphics) override; void drawFrame(gcn::Graphics *graphics) override; + void adjustHeight(); + // Inherited from FocusListener void focusLost(const gcn::Event& event) override; @@ -80,12 +73,8 @@ class DropDown : public gcn::DropDown */ void drawButton(gcn::Graphics *graphics) override; - // Add own Images. - static int instances; - static Image *buttons[2][2]; - static ImageRect skin; - static float mAlpha; -}; - -#endif // end DROPDOWN_H + void dropDown() override; + void foldUp() override; + int mPadding = 1; +}; diff --git a/src/gui/widgets/emoteshortcutcontainer.cpp b/src/gui/widgets/emoteshortcutcontainer.cpp index 8ecbc9bf..06d80ec2 100644 --- a/src/gui/widgets/emoteshortcutcontainer.cpp +++ b/src/gui/widgets/emoteshortcutcontainer.cpp @@ -21,13 +21,12 @@ #include "gui/widgets/emoteshortcutcontainer.h" -#include "configuration.h" #include "emoteshortcut.h" #include "graphics.h" -#include "imagesprite.h" -#include "item.h" #include "keyboardconfig.h" +#include "gui/gui.h" + #include "resources/emotedb.h" #include "resources/image.h" #include "resources/theme.h" @@ -36,65 +35,52 @@ static const int MAX_ITEMS = 12; EmoteShortcutContainer::EmoteShortcutContainer() { - addMouseListener(this); - addWidgetListener(this); - - mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png"); - - mBackgroundImg->setAlpha(config.guiAlpha); - mMaxItems = std::min(EmoteDB::getEmoteCount(), MAX_ITEMS); - - mBoxHeight = mBackgroundImg->getHeight(); - mBoxWidth = mBackgroundImg->getWidth(); -} - -EmoteShortcutContainer::~EmoteShortcutContainer() -{ - mBackgroundImg->decRef(); } void EmoteShortcutContainer::draw(gcn::Graphics *graphics) { - if (config.guiAlpha != mAlpha) - { - mAlpha = config.guiAlpha; - mBackgroundImg->setAlpha(mAlpha); - } - auto *g = static_cast<Graphics*>(graphics); + auto theme = gui->getTheme(); graphics->setFont(getFont()); for (int i = 0; i < mMaxItems; i++) { - const int emoteX = (i % mGridWidth) * mBoxWidth; - const int emoteY = (i / mGridWidth) * mBoxHeight; - - g->drawImage(mBackgroundImg, emoteX, emoteY); + WidgetState state; + state.x = (i % mGridWidth) * mBoxWidth; + state.y = (i / mGridWidth) * mBoxHeight; + theme->drawSkin(g, SkinType::ShortcutBox, state); // Draw emote keyboard shortcut. const char *key = SDL_GetKeyName( keyboard.getKeyValue(KeyboardConfig::KEY_EMOTE_1 + i)); graphics->setColor(Theme::getThemeColor(Theme::TEXT)); - g->drawText(key, emoteX + 2, emoteY + 2, gcn::Graphics::LEFT); + g->drawText(key, state.x + 2, state.y + 2, gcn::Graphics::LEFT); int emoteId = emoteShortcut->getEmote(i); if (emoteId != -1) { - EmoteDB::get(emoteId).sprite->draw(g, emoteX + 2, emoteY + 10); + if (auto image = EmoteDB::get(emoteId).image) + { + image->setAlpha(1.0f); + g->drawImage(image, state.x + 2, state.y + 10); + } } } if (mEmoteMoved != -1) { // Draw the emote image being dragged by the cursor. - const ImageSprite *sprite = EmoteDB::get(mEmoteMoved).sprite.get(); + if (auto image = EmoteDB::get(mEmoteMoved).image) + { + image->setAlpha(1.0f); - const int tPosX = mCursorPosX - (sprite->getWidth() / 2); - const int tPosY = mCursorPosY - (sprite->getHeight() / 2); + const int tPosX = mCursorPosX - (image->getWidth() / 2); + const int tPosY = mCursorPosY - (image->getHeight() / 2); - sprite->draw(g, tPosX, tPosY); + g->drawImage(image, tPosX, tPosY); + } } } @@ -115,6 +101,7 @@ void EmoteShortcutContainer::mouseDragged(gcn::MouseEvent &event) emoteShortcut->removeEmote(index); } } + if (mEmoteMoved != -1) { mCursorPosX = event.getX(); @@ -126,7 +113,6 @@ void EmoteShortcutContainer::mouseDragged(gcn::MouseEvent &event) void EmoteShortcutContainer::mousePressed(gcn::MouseEvent &event) { const int index = getIndexFromGrid(event.getX(), event.getY()); - if (index == -1) return; @@ -170,4 +156,3 @@ void EmoteShortcutContainer::mouseReleased(gcn::MouseEvent &event) mEmoteClicked = false; } } - diff --git a/src/gui/widgets/emoteshortcutcontainer.h b/src/gui/widgets/emoteshortcutcontainer.h index ecd41736..57d5efd2 100644 --- a/src/gui/widgets/emoteshortcutcontainer.h +++ b/src/gui/widgets/emoteshortcutcontainer.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef EMOTESHORTCUTCONTAINER_H -#define EMOTESHORTCUTCONTAINER_H +#pragma once #include "gui/widgets/shortcutcontainer.h" @@ -34,8 +33,6 @@ class EmoteShortcutContainer : public ShortcutContainer public: EmoteShortcutContainer(); - ~EmoteShortcutContainer() override; - /** * Draws the items. */ @@ -60,5 +57,3 @@ class EmoteShortcutContainer : public ShortcutContainer bool mEmoteClicked = false; int mEmoteMoved = -1; }; - -#endif diff --git a/src/gui/widgets/flowcontainer.h b/src/gui/widgets/flowcontainer.h index 21daae16..46be0919 100644 --- a/src/gui/widgets/flowcontainer.h +++ b/src/gui/widgets/flowcontainer.h @@ -18,8 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef FLOWCONTAINER_H -#define FLOWCONTAINER_H +#pragma once #include "container.h" @@ -56,5 +55,3 @@ class FlowContainer : public Container, int mGridWidth = 1; int mGridHeight = 1; }; - -#endif diff --git a/src/gui/widgets/icon.cpp b/src/gui/widgets/icon.cpp index 67fd8384..61506a6b 100644 --- a/src/gui/widgets/icon.cpp +++ b/src/gui/widgets/icon.cpp @@ -27,7 +27,7 @@ #include "resources/resourcemanager.h" Icon::Icon(const std::string &file) - : Icon(ResourceManager::getInstance()->getImageRef(file)) + : Icon(ResourceManager::getInstance()->getImage(file)) { } diff --git a/src/gui/widgets/icon.h b/src/gui/widgets/icon.h index 3ebc2c16..5e61520c 100644 --- a/src/gui/widgets/icon.h +++ b/src/gui/widgets/icon.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef ICON_H -#define ICON_H +#pragma once #include "resources/resource.h" @@ -68,5 +67,3 @@ class Icon : public gcn::Widget private: ResourceRef<Image> mImage; }; - -#endif // ICON_H diff --git a/src/gui/widgets/inttextfield.h b/src/gui/widgets/inttextfield.h index d5829404..bebad71d 100644 --- a/src/gui/widgets/inttextfield.h +++ b/src/gui/widgets/inttextfield.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef INTTEXTFIELD_H -#define INTTEXTFIELD_H +#pragma once #include "textfield.h" @@ -71,5 +70,3 @@ class IntTextField : public TextField int mDefault; /**< Default value */ int mValue; /**< Current value */ }; - -#endif diff --git a/src/gui/widgets/itemcontainer.cpp b/src/gui/widgets/itemcontainer.cpp index 940c69f4..d0d24c51 100644 --- a/src/gui/widgets/itemcontainer.cpp +++ b/src/gui/widgets/itemcontainer.cpp @@ -25,7 +25,6 @@ #include "inventory.h" #include "item.h" #include "itemshortcut.h" -#include "log.h" #include "gui/chatwindow.h" #include "gui/itempopup.h" @@ -44,19 +43,12 @@ // TODO: Add support for adding items to the item shortcut window (global // itemShortcut). -static const int BOX_WIDTH = 35; -static const int BOX_HEIGHT = 43; - ItemContainer::ItemContainer(Inventory *inventory): mInventory(inventory) { mItemPopup = new ItemPopup; setFocusable(true); - mSelImg = Theme::getImageFromTheme("selection.png"); - if (!mSelImg) - logger->error("Unable to load selection.png"); - addKeyListener(this); addMouseListener(this); addWidgetListener(this); @@ -64,7 +56,6 @@ ItemContainer::ItemContainer(Inventory *inventory): ItemContainer::~ItemContainer() { - mSelImg->decRef(); delete mItemPopup; } @@ -113,39 +104,47 @@ void ItemContainer::draw(gcn::Graphics *graphics) } } + auto theme = gui->getTheme(); + auto &slotSkin = theme->getSkin(SkinType::ItemSlot); + WidgetState slotState; + for (int i = 0; i < mGridColumns; i++) { for (int j = 0; j < mGridRows; j++) { - int itemX = i * BOX_WIDTH; - int itemY = j * BOX_HEIGHT; + int itemX = i * slotSkin.width; + int itemY = j * slotSkin.height; int itemIndex = j * mGridColumns + i; + slotState.x = itemX; + slotState.y = itemY; + slotState.flags = 0; + + if (itemIndex == mSelectedIndex) + { + slotState.flags |= STATE_SELECTED; + + if (mSelectionStatus == SEL_DRAGGING) + { + // Reposition the coords to that of the cursor. + itemX = mDragPosX - (slotSkin.width / 2); + itemY = mDragPosY - (slotSkin.height / 2); + } + } + + slotSkin.draw(g, slotState); + Item *item = getItemAt(itemIndex); if (!item || item->getId() == 0) continue; - Image *image = item->getImage(); - if (image) + if (Image *image = item->getImage()) { - if (itemIndex == mSelectedIndex) - { - if (mSelectionStatus == SEL_DRAGGING) - { - // Reposition the coords to that of the cursor. - itemX = mDragPosX - (BOX_WIDTH / 2); - itemY = mDragPosY - (BOX_HEIGHT / 2); - } - else - { - // Draw selection border image. - g->drawImage(mSelImg, itemX, itemY); - } - } - image->setAlpha(1.0f); // ensure the image if fully drawn... - g->drawImage(image, itemX, itemY); + image->setAlpha(1.0f); + g->drawImage(image, itemX + slotSkin.padding, itemY + slotSkin.padding); } + // Draw item caption std::string caption; if (item->getQuantity() > 1) @@ -154,22 +153,22 @@ void ItemContainer::draw(gcn::Graphics *graphics) caption = "Eq."; if (item->isEquipped()) - g->setColor(Theme::getThemeColor(Theme::ITEM_EQUIPPED)); + g->setColor(theme->getColor(Theme::ITEM_EQUIPPED)); else g->setColor(gcn::Color(0, 0, 0)); - g->drawText(caption, itemX + BOX_WIDTH / 2, - itemY + BOX_HEIGHT - 14, gcn::Graphics::CENTER); + g->drawText(caption, itemX + slotSkin.width / 2, + itemY + slotSkin.height - 14, gcn::Graphics::CENTER); } } // Draw an orange box around the selected item if (isFocused() && mHighlightedIndex != -1) { - const int itemX = (mHighlightedIndex % mGridColumns) * BOX_WIDTH; - const int itemY = (mHighlightedIndex / mGridColumns) * BOX_HEIGHT; + const int itemX = (mHighlightedIndex % mGridColumns) * slotSkin.width; + const int itemY = (mHighlightedIndex / mGridColumns) * slotSkin.height; g->setColor(gcn::Color(255, 128, 0)); - g->drawRectangle(gcn::Rectangle(itemX, itemY, BOX_WIDTH, BOX_HEIGHT)); + g->drawRectangle(gcn::Rectangle(itemX, itemY, slotSkin.width, slotSkin.height)); } } @@ -355,9 +354,7 @@ void ItemContainer::mouseReleased(gcn::MouseEvent &event) // Show ItemTooltip void ItemContainer::mouseMoved(gcn::MouseEvent &event) { - Item *item = getItemAt(getSlotIndex(event.getX(), event.getY())); - - if (item) + if (Item *item = getItemAt(getSlotIndex(event.getX(), event.getY()))) { mItemPopup->setItem(item->getInfo()); mItemPopup->position(viewport->getMouseX(), viewport->getMouseY()); @@ -376,26 +373,36 @@ void ItemContainer::mouseExited(gcn::MouseEvent &event) void ItemContainer::widgetResized(const gcn::Event &event) { - mGridColumns = std::max(1, getWidth() / BOX_WIDTH); + auto &slotSkin = gui->getTheme()->getSkin(SkinType::ItemSlot); + + mGridColumns = std::max(1, getWidth() / slotSkin.width); adjustHeight(); } void ItemContainer::adjustHeight() { + auto &slotSkin = gui->getTheme()->getSkin(SkinType::ItemSlot); + mGridRows = (mLastUsedSlot + 1) / mGridColumns; if (mGridRows == 0 || (mLastUsedSlot + 1) % mGridColumns > 0) ++mGridRows; - setHeight(mGridRows * BOX_HEIGHT); + setHeight(mGridRows * slotSkin.height); } int ItemContainer::getSlotIndex(int x, int y) const { - if (x < getWidth() && y < getHeight()) - { - return (y / BOX_HEIGHT) * mGridColumns + (x / BOX_WIDTH); - } - return Inventory::NO_SLOT_INDEX; + if (x >= getWidth() || y >= getHeight()) + return Inventory::NO_SLOT_INDEX; + + auto &slotSkin = gui->getTheme()->getSkin(SkinType::ItemSlot); + const auto row = y / slotSkin.height; + const auto column = x / slotSkin.width; + + if (row < 0 || row >= mGridRows || column < 0 || column >= mGridColumns) + return Inventory::NO_SLOT_INDEX; + + return (row * mGridColumns) + column; } void ItemContainer::keyAction() diff --git a/src/gui/widgets/itemcontainer.h b/src/gui/widgets/itemcontainer.h index 51807aba..c1d611b9 100644 --- a/src/gui/widgets/itemcontainer.h +++ b/src/gui/widgets/itemcontainer.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef ITEMCONTAINER_H -#define ITEMCONTAINER_H +#pragma once #include <guichan/keylistener.hpp> #include <guichan/mouselistener.hpp> @@ -71,6 +70,9 @@ class ItemContainer : public gcn::Widget, */ void draw(gcn::Graphics *graphics) override; + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics *graphics) override {} + // KeyListener void keyPressed(gcn::KeyEvent &event) override; void keyReleased(gcn::KeyEvent &event) override; @@ -178,7 +180,6 @@ class ItemContainer : public gcn::Widget, Inventory *mInventory; int mGridColumns = 1; int mGridRows = 1; - Image *mSelImg; int mSelectedIndex = -1; int mHighlightedIndex = -1; int mLastUsedSlot = -1; @@ -196,5 +197,3 @@ class ItemContainer : public gcn::Widget, std::list<gcn::SelectionListener *> mSelectionListeners; }; - -#endif // ITEMCONTAINER_H diff --git a/src/gui/widgets/itemlinkhandler.h b/src/gui/widgets/itemlinkhandler.h index 28e9c11c..58202d33 100644 --- a/src/gui/widgets/itemlinkhandler.h +++ b/src/gui/widgets/itemlinkhandler.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef ITEM_LINK_HANDLER_H -#define ITEM_LINK_HANDLER_H +#pragma once #include "gui/widgets/linkhandler.h" @@ -49,5 +48,3 @@ class ItemLinkHandler : public LinkHandler, gcn::ActionListener Window *mParent = nullptr; std::string mLink; }; - -#endif diff --git a/src/gui/widgets/itemshortcutcontainer.cpp b/src/gui/widgets/itemshortcutcontainer.cpp index 0b8f0c8c..b47fa29d 100644 --- a/src/gui/widgets/itemshortcutcontainer.cpp +++ b/src/gui/widgets/itemshortcutcontainer.cpp @@ -21,7 +21,6 @@ #include "gui/widgets/itemshortcutcontainer.h" -#include "configuration.h" #include "graphics.h" #include "inventory.h" #include "item.h" @@ -39,64 +38,45 @@ #include "utils/stringutils.h" ItemShortcutContainer::ItemShortcutContainer() + : mItemPopup(new ItemPopup) { - addMouseListener(this); - addWidgetListener(this); - - mItemPopup = new ItemPopup; - - mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png"); mMaxItems = itemShortcut->getItemCount(); - - mBackgroundImg->setAlpha(config.guiAlpha); - - mBoxHeight = mBackgroundImg->getHeight(); - mBoxWidth = mBackgroundImg->getWidth(); } -ItemShortcutContainer::~ItemShortcutContainer() -{ - mBackgroundImg->decRef(); - delete mItemPopup; -} +ItemShortcutContainer::~ItemShortcutContainer() = default; void ItemShortcutContainer::draw(gcn::Graphics *graphics) { - if (config.guiAlpha != mAlpha) - { - mAlpha = config.guiAlpha; - mBackgroundImg->setAlpha(mAlpha); - } - auto *g = static_cast<Graphics*>(graphics); + auto theme = gui->getTheme(); + auto &skin = theme->getSkin(SkinType::ShortcutBox); graphics->setFont(getFont()); for (int i = 0; i < mMaxItems; i++) { - const int itemX = (i % mGridWidth) * mBoxWidth; - const int itemY = (i / mGridWidth) * mBoxHeight; - - g->drawImage(mBackgroundImg, itemX, itemY); + WidgetState state; + state.x = (i % mGridWidth) * mBoxWidth; + state.y = (i / mGridWidth) * mBoxHeight; + skin.draw(g, state); // Draw item keyboard shortcut. const char *key = SDL_GetKeyName( keyboard.getKeyValue(KeyboardConfig::KEY_SHORTCUT_1 + i)); graphics->setColor(Theme::getThemeColor(Theme::TEXT)); - g->drawText(key, itemX + 2, itemY + 2, gcn::Graphics::LEFT); + g->drawText(key, + state.x + skin.padding + 2, + state.y + skin.padding + 2, + gcn::Graphics::LEFT); - if (itemShortcut->getItem(i) < 0) + const int itemId = itemShortcut->getItem(i); + if (itemId < 0) continue; - Item *item = - PlayerInfo::getInventory()->findItem(itemShortcut->getItem(i)); - - if (item) + if (Item *item = PlayerInfo::getInventory()->findItem(itemId)) { // Draw item icon. - Image* image = item->getImage(); - - if (image) + if (Image *image = item->getImage()) { std::string caption; if (item->getQuantity() > 1) @@ -105,11 +85,13 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) caption = "Eq."; image->setAlpha(1.0f); - g->drawImage(image, itemX, itemY); + g->drawImage(image, state.x + skin.padding, state.y + skin.padding); if (item->isEquipped()) g->setColor(Theme::getThemeColor(Theme::ITEM_EQUIPPED)); - g->drawText(caption, itemX + mBoxWidth / 2, - itemY + mBoxHeight - 14, gcn::Graphics::CENTER); + g->drawText(caption, + state.x + mBoxWidth / 2, + state.y + mBoxHeight - 14, + gcn::Graphics::CENTER); } } } @@ -117,7 +99,7 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) if (mItemMoved) { // Draw the item image being dragged by the cursor. - if (Image* image = mItemMoved->getImage()) + if (Image *image = mItemMoved->getImage()) { const int tPosX = mCursorPosX - (image->getWidth() / 2); const int tPosY = mCursorPosY - (image->getHeight() / 2); @@ -137,23 +119,20 @@ void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) if (!mItemMoved && mItemClicked) { const int index = getIndexFromGrid(event.getX(), event.getY()); - if (index == -1) return; const int itemId = itemShortcut->getItem(index); - if (itemId < 0) return; - Item *item = PlayerInfo::getInventory()->findItem(itemId); - - if (item) + if (Item *item = PlayerInfo::getInventory()->findItem(itemId)) { mItemMoved = item; itemShortcut->removeItem(index); } } + if (mItemMoved) { mCursorPosX = event.getX(); @@ -225,19 +204,7 @@ void ItemShortcutContainer::mouseReleased(gcn::MouseEvent &event) // Show ItemTooltip void ItemShortcutContainer::mouseMoved(gcn::MouseEvent &event) { - const int index = getIndexFromGrid(event.getX(), event.getY()); - - if (index == -1) - return; - - const int itemId = itemShortcut->getItem(index); - - if (itemId < 0) - return; - - Item *item = PlayerInfo::getInventory()->findItem(itemId); - - if (item) + if (Item *item = getItemAt(event.getX(), event.getY())) { mItemPopup->setItem(item->getInfo()); mItemPopup->position(viewport->getMouseX(), viewport->getMouseY()); @@ -248,6 +215,19 @@ void ItemShortcutContainer::mouseMoved(gcn::MouseEvent &event) } } +Item *ItemShortcutContainer::getItemAt(int x, int y) const +{ + const int index = getIndexFromGrid(x, y); + if (index == -1) + return nullptr; + + const int itemId = itemShortcut->getItem(index); + if (itemId < 0) + return nullptr; + + return PlayerInfo::getInventory()->findItem(itemId); +} + // Hide ItemTooltip void ItemShortcutContainer::mouseExited(gcn::MouseEvent &event) { diff --git a/src/gui/widgets/itemshortcutcontainer.h b/src/gui/widgets/itemshortcutcontainer.h index 243920a0..a01857db 100644 --- a/src/gui/widgets/itemshortcutcontainer.h +++ b/src/gui/widgets/itemshortcutcontainer.h @@ -19,13 +19,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef ITEMSHORTCUTCONTAINER_H -#define ITEMSHORTCUTCONTAINER_H +#pragma once #include "gui/widgets/shortcutcontainer.h" #include <guichan/mouselistener.hpp> +#include <memory> + class Image; class Item; class ItemPopup; @@ -66,10 +67,10 @@ class ItemShortcutContainer : public ShortcutContainer void mouseExited(gcn::MouseEvent &event) override; void mouseMoved(gcn::MouseEvent &event) override; + Item *getItemAt(int x, int y) const; + bool mItemClicked = false; Item *mItemMoved = nullptr; - ItemPopup *mItemPopup; + std::unique_ptr<ItemPopup> mItemPopup; }; - -#endif diff --git a/src/gui/widgets/label.cpp b/src/gui/widgets/label.cpp index af5220ef..a2ed8820 100644 --- a/src/gui/widgets/label.cpp +++ b/src/gui/widgets/label.cpp @@ -21,8 +21,13 @@ #include "gui/widgets/label.h" +#include "textrenderer.h" + #include "resources/theme.h" +#include <guichan/exception.hpp> +#include <guichan/font.hpp> + Label::Label() { setForegroundColor(Theme::getThemeColor(Theme::TEXT)); @@ -36,5 +41,33 @@ Label::Label(const std::string &caption) : void Label::draw(gcn::Graphics *graphics) { - gcn::Label::draw(static_cast<gcn::Graphics*>(graphics)); + int textX; + int textY = (getHeight() - getFont()->getHeight()) / 2; + + switch (getAlignment()) + { + case Graphics::LEFT: + textX = 0; + break; + case Graphics::CENTER: + textX = getWidth() / 2; + break; + case Graphics::RIGHT: + textX = getWidth(); + break; + default: + throw GCN_EXCEPTION("Unknown alignment."); + } + + TextRenderer::renderText(static_cast<Graphics *>(graphics), + getCaption(), + textX, + textY, + getAlignment(), + getForegroundColor(), + getFont(), + mOutlineColor.has_value(), + mShadowColor.has_value(), + mOutlineColor, + mShadowColor); } diff --git a/src/gui/widgets/label.h b/src/gui/widgets/label.h index cb7a8b1c..85bcbe23 100644 --- a/src/gui/widgets/label.h +++ b/src/gui/widgets/label.h @@ -19,14 +19,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef LABEL_H -#define LABEL_H +#pragma once #include <guichan/widgets/label.hpp> +#include <optional> /** * Label widget. Same as the Guichan label but modified to use the palette - * system. + * system and support outlines and shadows. * * \ingroup GUI */ @@ -42,9 +42,31 @@ class Label : public gcn::Label Label(const std::string &caption); /** + * Sets the color of the outline. + */ + void setOutlineColor(std::optional<gcn::Color> color); + + /** + * Sets the color of the shadow. + */ + void setShadowColor(std::optional<gcn::Color> color); + + /** * Draws the label. */ void draw(gcn::Graphics *graphics) override; + + private: + std::optional<gcn::Color> mOutlineColor; + std::optional<gcn::Color> mShadowColor; }; -#endif +inline void Label::setOutlineColor(std::optional<gcn::Color> color) +{ + mOutlineColor = color; +} + +inline void Label::setShadowColor(std::optional<gcn::Color> color) +{ + mShadowColor = color; +} diff --git a/src/gui/widgets/layout.h b/src/gui/widgets/layout.h index 4e4b28c5..42f08758 100644 --- a/src/gui/widgets/layout.h +++ b/src/gui/widgets/layout.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef WIDGET_LAYOUT_H -#define WIDGET_LAYOUT_H +#pragma once #include <guichan/widgets/container.hpp> @@ -168,9 +167,12 @@ class LayoutCell }; LayoutCell() = default; - ~LayoutCell(); + // Copy not allowed, as the cell may own an array. + LayoutCell(LayoutCell const &) = delete; + LayoutCell &operator=(LayoutCell const &) = delete; + /** * Sets the padding around the cell content. */ @@ -232,10 +234,6 @@ class LayoutCell void computeSizes(); private: - // Copy not allowed, as the cell may own an array. - LayoutCell(LayoutCell const &); - LayoutCell &operator=(LayoutCell const &); - union { gcn::Widget *mWidget; @@ -310,5 +308,3 @@ class Layout : public LayoutCell private: bool mComputed; }; - -#endif // WIDGET_LAYOUT_H diff --git a/src/gui/widgets/layouthelper.h b/src/gui/widgets/layouthelper.h index 26360a9a..ad01c565 100644 --- a/src/gui/widgets/layouthelper.h +++ b/src/gui/widgets/layouthelper.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef LAYOUTHELPER_H -#define LAYOUTHELPER_H +#pragma once #include "gui/widgets/layout.h" @@ -74,5 +73,3 @@ class LayoutHelper : public gcn::WidgetListener Layout mLayout; /**< Layout handler */ gcn::Container *mContainer; /**< Managed container */ }; - -#endif // LAYOUTHELPER_H diff --git a/src/gui/widgets/linkhandler.h b/src/gui/widgets/linkhandler.h index 33263a25..48b182a1 100644 --- a/src/gui/widgets/linkhandler.h +++ b/src/gui/widgets/linkhandler.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef LINK_HANDLER_H -#define LINK_HANDLER_H +#pragma once #include <string> @@ -35,5 +34,3 @@ class LinkHandler virtual void handleLink(const std::string &link) = 0; }; - -#endif diff --git a/src/gui/widgets/listbox.cpp b/src/gui/widgets/listbox.cpp index 55f0f422..112de232 100644 --- a/src/gui/widgets/listbox.cpp +++ b/src/gui/widgets/listbox.cpp @@ -21,8 +21,7 @@ #include "gui/widgets/listbox.h" -#include "configuration.h" - +#include "gui/gui.h" #include "gui/sdlinput.h" #include "resources/theme.h" @@ -32,39 +31,29 @@ #include <guichan/key.hpp> #include <guichan/listmodel.hpp> -float ListBox::mAlpha = 1.0; - ListBox::ListBox(gcn::ListModel *listModel): gcn::ListBox(listModel) { } -void ListBox::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (mAlpha != alpha) - mAlpha = alpha; -} - void ListBox::draw(gcn::Graphics *graphics) { if (!mListModel) return; - updateAlpha(); - - graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, - (int) (mAlpha * 255.0f))); graphics->setFont(getFont()); const int height = getRowHeight(); // Draw filled rectangle around the selected list element if (mSelected >= 0) + { + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + highlightColor.a = gui->getTheme()->getGuiAlpha(); + graphics->setColor(highlightColor); graphics->fillRectangle(gcn::Rectangle(0, height * mSelected, getWidth(), height)); + } // Draw the list elements graphics->setColor(Theme::getThemeColor(Theme::TEXT)); diff --git a/src/gui/widgets/listbox.h b/src/gui/widgets/listbox.h index d16256b1..40bc2fbc 100644 --- a/src/gui/widgets/listbox.h +++ b/src/gui/widgets/listbox.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef LISTBOX_H -#define LISTBOX_H +#pragma once #include <guichan/widgets/listbox.hpp> @@ -43,10 +42,8 @@ class ListBox : public gcn::ListBox */ void draw(gcn::Graphics *graphics) override; - /** - * Update the alpha value to the graphic components. - */ - static void updateAlpha(); + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics *graphics) override {} // Inherited from KeyListener @@ -61,9 +58,4 @@ class ListBox : public gcn::ListBox void mouseWheelMovedDown(gcn::MouseEvent& mouseEvent) override; void mouseDragged(gcn::MouseEvent &event) override; - - protected: - static float mAlpha; }; - -#endif diff --git a/src/gui/widgets/passwordfield.h b/src/gui/widgets/passwordfield.h index 4bed0e05..36964843 100644 --- a/src/gui/widgets/passwordfield.h +++ b/src/gui/widgets/passwordfield.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PASSWORDFIELD_H -#define PASSWORDFIELD_H +#pragma once #include "textfield.h" @@ -42,5 +41,3 @@ class PasswordField : public TextField */ void draw(gcn::Graphics *graphics) override; }; - -#endif diff --git a/src/gui/widgets/playerbox.cpp b/src/gui/widgets/playerbox.cpp index 3bdd6bd1..f251035d 100644 --- a/src/gui/widgets/playerbox.cpp +++ b/src/gui/widgets/playerbox.cpp @@ -22,86 +22,22 @@ #include "gui/widgets/playerbox.h" #include "being.h" -#include "configuration.h" #include "graphics.h" -#include "resources/image.h" -#include "resources/theme.h" - -#include "utils/dtor.h" - -int PlayerBox::instances = 0; -float PlayerBox::mAlpha = 1.0; -ImageRect PlayerBox::background; - -PlayerBox::PlayerBox(const Being *being): - mBeing(being) +PlayerBox::PlayerBox(const Being *being) + : mBeing(being) { - setFrameSize(2); - - if (instances == 0) - { - // Load the background skin - Image *textbox = Theme::getImageFromTheme("deepbox.png"); - int bggridx[4] = {0, 3, 28, 31}; - int bggridy[4] = {0, 3, 28, 31}; - int a = 0; - - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - background.grid[a] = textbox->getSubImage( - bggridx[x], bggridy[y], - bggridx[x + 1] - bggridx[x] + 1, - bggridy[y + 1] - bggridy[y] + 1); - a++; - } - } - - background.setAlpha(config.guiAlpha); - - textbox->decRef(); - } - - instances++; -} - -PlayerBox::~PlayerBox() -{ - instances--; - - mBeing = nullptr; - - if (instances == 0) - { - std::for_each(background.grid, background.grid + 9, dtor<Image*>()); - } } void PlayerBox::draw(gcn::Graphics *graphics) { + ScrollArea::draw(graphics); + if (mBeing) { // Draw character - const int bs = getFrameSize(); - const int x = getWidth() / 2 + bs; - const int y = getHeight() - bs; + const int x = getWidth() / 2; + const int y = (getHeight() + mBeing->getHeight()) / 2 - 12; mBeing->drawSpriteAt(static_cast<Graphics*>(graphics), x, y); } - - if (config.guiAlpha != mAlpha) - { - mAlpha = config.guiAlpha; - background.setAlpha(config.guiAlpha); - } -} - -void PlayerBox::drawFrame(gcn::Graphics *graphics) -{ - const int bs = getFrameSize(); - const int w = getWidth() + bs * 2; - const int h = getHeight() + bs * 2; - - static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, background); } diff --git a/src/gui/widgets/playerbox.h b/src/gui/widgets/playerbox.h index 68dd670e..39392c63 100644 --- a/src/gui/widgets/playerbox.h +++ b/src/gui/widgets/playerbox.h @@ -19,20 +19,18 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PLAYERBOX_H -#define PLAYERBOX_H +#pragma once -#include <guichan/widgets/scrollarea.hpp> +#include "scrollarea.h" class Being; -class ImageRect; /** * A box showing a player character. * * \ingroup GUI */ -class PlayerBox : public gcn::ScrollArea +class PlayerBox : public ScrollArea { public: /** @@ -41,8 +39,6 @@ class PlayerBox : public gcn::ScrollArea */ PlayerBox(const Being *being = nullptr); - ~PlayerBox() override; - /** * Sets a new player character to be displayed by this box. Setting the * player to <code>NULL</code> causes the box not to draw any @@ -52,21 +48,10 @@ class PlayerBox : public gcn::ScrollArea { mBeing = being; } /** - * Draws the scroll area. + * Draws the scroll area and the player. */ void draw(gcn::Graphics *graphics) override; - /** - * Draws the background and border of the scroll area. - */ - void drawFrame(gcn::Graphics *graphics) override; - private: const Being *mBeing; /**< The character used for display */ - - static float mAlpha; - static int instances; - static ImageRect background; }; - -#endif diff --git a/src/gui/widgets/popup.cpp b/src/gui/widgets/popup.cpp index 94d8cf85..b245b9e6 100644 --- a/src/gui/widgets/popup.cpp +++ b/src/gui/widgets/popup.cpp @@ -22,31 +22,32 @@ #include "gui/widgets/popup.h" +#include "browserbox.h" #include "graphics.h" #include "log.h" +#include "textbox.h" +#include "gui/gui.h" #include "gui/viewport.h" - +#include "gui/widgets/label.h" #include "gui/widgets/windowcontainer.h" -#include "resources/theme.h" - #include <guichan/exception.hpp> -Popup::Popup(const std::string &name, const std::string &skin): - mPopupName(name), - mMaxWidth(graphics->getWidth()), - mMaxHeight(graphics->getHeight()) +Popup::Popup(const std::string &name, SkinType skinType) + : mPopupName(name) + , mMaxWidth(graphics->getWidth()) + , mMaxHeight(graphics->getHeight()) + , mSkinType(skinType) { logger->log("Popup::Popup(\"%s\")", name.c_str()); if (!windowContainer) throw GCN_EXCEPTION("Popup::Popup(): no windowContainer set"); - setPadding(6); - - // Loads the skin - mSkin = Theme::instance()->load(skin); + auto &skin = getSkin(); + setFrameSize(skin.frameSize); + setPadding(skin.padding); // Add this window to the window container windowContainer->add(this); @@ -58,8 +59,6 @@ Popup::Popup(const std::string &name, const std::string &skin): Popup::~Popup() { logger->log("Popup::~Popup(\"%s\")", mPopupName.c_str()); - - mSkin->instances--; } void Popup::setWindowContainer(WindowContainer *wc) @@ -67,15 +66,57 @@ void Popup::setWindowContainer(WindowContainer *wc) windowContainer = wc; } -void Popup::draw(gcn::Graphics *graphics) +void Popup::add(gcn::Widget *widget) +{ + Container::add(widget); + widgetAdded(widget); +} + +void Popup::add(gcn::Widget *widget, int x, int y) +{ + Container::add(widget, x, y); + widgetAdded(widget); +} + +void Popup::widgetAdded(gcn::Widget *widget) const { - auto *g = static_cast<Graphics*>(graphics); + if (const int paletteId = getSkin().palette) + { + if (auto browserBox = dynamic_cast<BrowserBox*>(widget)) + { + browserBox->setPalette(paletteId); + } + else if (auto label = dynamic_cast<Label*>(widget)) + { + auto &palette = gui->getTheme()->getPalette(paletteId); + label->setForegroundColor(palette.getColor(Theme::TEXT)); + label->setOutlineColor(palette.getOutlineColor(Theme::TEXT)); + } + else if (auto textBox = dynamic_cast<TextBox*>(widget)) + { + auto &palette = gui->getTheme()->getPalette(paletteId); + textBox->setTextColor(&palette.getColor(Theme::TEXT)); + textBox->setOutlineColor(palette.getOutlineColor(Theme::TEXT)); + } + } +} - g->drawImageRect(0, 0, getWidth(), getHeight(), mSkin->getBorder()); +void Popup::draw(gcn::Graphics *graphics) +{ + if (getFrameSize() == 0) + drawFrame(graphics); drawChildren(graphics); } +void Popup::drawFrame(gcn::Graphics *graphics) +{ + WidgetState state(this); + state.width += getFrameSize() * 2; + state.height += getFrameSize() * 2; + getSkin().draw(static_cast<Graphics *>(graphics), state); +} + gcn::Rectangle Popup::getChildrenArea() { return gcn::Rectangle(getPadding(), getPadding(), @@ -116,12 +157,12 @@ void Popup::setLocationRelativeTo(gcn::Widget *widget) void Popup::setMinWidth(int width) { - mMinWidth = std::max(width, mSkin->getMinWidth()); + mMinWidth = std::max(getSkin().getMinWidth(), width); } void Popup::setMinHeight(int height) { - mMinHeight = std::max(height, mSkin->getMinHeight()); + mMinHeight = std::max(getSkin().getMinHeight(), height); } void Popup::setMaxWidth(int width) @@ -156,6 +197,11 @@ void Popup::position(int x, int y) requestMoveToTop(); } +const Skin &Popup::getSkin() const +{ + return gui->getTheme()->getSkin(mSkinType); +} + void Popup::mouseMoved(gcn::MouseEvent &event) { if (viewport) diff --git a/src/gui/widgets/popup.h b/src/gui/widgets/popup.h index c77bf814..b88cafc9 100644 --- a/src/gui/widgets/popup.h +++ b/src/gui/widgets/popup.h @@ -20,12 +20,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef POPUP_H -#define POPUP_H +#pragma once #include "guichanfwd.h" #include "gui/widgets/container.h" +#include "resources/theme.h" #include <guichan/mouselistener.hpp> @@ -53,10 +53,10 @@ class Popup : public Container, public gcn::MouseListener * * @param name A human readable name for the popup. Only useful for * debugging purposes. - * @param skin The location where the Popup's skin XML can be found. + * @param skinType The skin type used when drawing the popup. */ - Popup(const std::string &name = std::string(), - const std::string &skin = "window.xml"); + explicit Popup(const std::string &name = std::string(), + SkinType skinType = SkinType::Popup); /** * Destructor. Deletes all the added widgets. @@ -68,12 +68,21 @@ class Popup : public Container, public gcn::MouseListener */ static void setWindowContainer(WindowContainer *windowContainer); + // Container interface + void add(gcn::Widget *widget) override; + void add(gcn::Widget *widget, int x, int y) override; + /** * Draws the popup. */ void draw(gcn::Graphics *graphics) override; /** + * Draws the popup frame. + */ + void drawFrame(gcn::Graphics *graphics) override; + + /** * Sets the size of this popup. */ void setContentSize(int width, int height); @@ -151,15 +160,20 @@ class Popup : public Container, public gcn::MouseListener */ void position(int x, int y); + /** + * Returns the Skin used by this popup. + */ + const Skin &getSkin() const; + private: - std::string mPopupName; /**< Name of the popup */ - int mMinWidth = 100; /**< Minimum popup width */ - int mMinHeight = 40; /**< Minimum popup height */ - int mMaxWidth; /**< Maximum popup width */ - int mMaxHeight; /**< Maximum popup height */ - int mPadding; /**< Holds the padding of the popup. */ - - Skin *mSkin; /**< Skin in use by this popup */ -}; + void widgetAdded(gcn::Widget *widget) const; -#endif + std::string mPopupName; /**< Name of the popup */ + int mMinWidth = 100; /**< Minimum popup width */ + int mMinHeight = 40; /**< Minimum popup height */ + int mMaxWidth; /**< Maximum popup width */ + int mMaxHeight; /**< Maximum popup height */ + int mPadding; /**< Holds the padding of the popup. */ + + SkinType mSkinType; /**< The skin type used when drawing the popup widget. */ +}; diff --git a/src/gui/widgets/progressbar.cpp b/src/gui/widgets/progressbar.cpp index 9d41d1af..5cf1b05a 100644 --- a/src/gui/widgets/progressbar.cpp +++ b/src/gui/widgets/progressbar.cpp @@ -21,23 +21,14 @@ #include "gui/widgets/progressbar.h" -#include "configuration.h" #include "graphics.h" -#include "textrenderer.h" #include "gui/gui.h" -#include "resources/image.h" #include "resources/theme.h" -#include "utils/dtor.h" - #include <guichan/font.hpp> -ImageRect ProgressBar::mBorder; -int ProgressBar::mInstances = 0; -float ProgressBar::mAlpha = 1.0; - ProgressBar::ProgressBar(float progress, int width, int height, int color): @@ -53,36 +44,6 @@ ProgressBar::ProgressBar(float progress, mProgress); setSize(width, height); - - if (mInstances == 0) - { - Image *dBorders = Theme::getImageFromTheme("vscroll_grey.png"); - mBorder.grid[0] = dBorders->getSubImage(0, 0, 4, 4); - mBorder.grid[1] = dBorders->getSubImage(4, 0, 3, 4); - mBorder.grid[2] = dBorders->getSubImage(7, 0, 4, 4); - mBorder.grid[3] = dBorders->getSubImage(0, 4, 4, 10); - mBorder.grid[4] = dBorders->getSubImage(4, 4, 3, 10); - mBorder.grid[5] = dBorders->getSubImage(7, 4, 4, 10); - mBorder.grid[6] = dBorders->getSubImage(0, 15, 4, 4); - mBorder.grid[7] = dBorders->getSubImage(4, 15, 3, 4); - mBorder.grid[8] = dBorders->getSubImage(7, 15, 4, 4); - - mBorder.setAlpha(mAlpha); - - dBorders->decRef(); - } - - mInstances++; -} - -ProgressBar::~ProgressBar() -{ - mInstances--; - - if (mInstances == 0) - { - std::for_each(mBorder.grid, mBorder.grid + 9, dtor<Image*>()); - } } void ProgressBar::logic() @@ -114,30 +75,19 @@ void ProgressBar::logic() } } -void ProgressBar::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (mAlpha != alpha) - { - mAlpha = alpha; - mBorder.setAlpha(mAlpha); - } -} - void ProgressBar::draw(gcn::Graphics *graphics) { - updateAlpha(); - - mColor.a = (int) (mAlpha * 255); + mColor.a = gui->getTheme()->getGuiAlpha(); gcn::Rectangle rect = getDimension(); rect.x = 0; rect.y = 0; - render(static_cast<Graphics*>(graphics), rect, mColor, - mProgress, mText); + gui->getTheme()->drawProgressBar(static_cast<Graphics *>(graphics), + rect, + mColor, + mProgress, + mText); } void ProgressBar::setProgress(float progress) @@ -149,9 +99,7 @@ void ProgressBar::setProgress(float progress) mProgress = p; if (mProgressPalette >= 0) - { mColorToGo = Theme::getProgressColor(mProgressPalette, progress); - } } void ProgressBar::setProgressPalette(int progressPalette) @@ -160,9 +108,7 @@ void ProgressBar::setProgressPalette(int progressPalette) mProgressPalette = progressPalette; if (mProgressPalette != oldPalette && mProgressPalette >= 0) - { mColorToGo = Theme::getProgressColor(mProgressPalette, mProgressToGo); - } } void ProgressBar::setColor(const gcn::Color &color) @@ -172,37 +118,3 @@ void ProgressBar::setColor(const gcn::Color &color) if (!mSmoothColorChange) mColor = color; } - -void ProgressBar::render(Graphics *graphics, const gcn::Rectangle &area, - const gcn::Color &color, float progress, - const std::string &text) -{ - gcn::Font *oldFont = graphics->getFont(); - gcn::Color oldColor = graphics->getColor(); - - graphics->drawImageRect(area, mBorder); - - // The bar - if (progress > 0) - { - graphics->setColor(color); - graphics->fillRectangle(gcn::Rectangle(area.x + 4, area.y + 4, - (int) (progress * (area.width - 8)), - area.height - 8)); - } - - // The label - if (!text.empty()) - { - const int textX = area.x + area.width / 2; - const int textY = area.y + (area.height - boldFont->getHeight()) / 2; - - TextRenderer::renderText(graphics, text, textX, textY, - gcn::Graphics::CENTER, - Theme::getThemeColor(Theme::PROGRESS_BAR), - gui->getFont(), true, false); - } - - graphics->setFont(oldFont); - graphics->setColor(oldColor); -} diff --git a/src/gui/widgets/progressbar.h b/src/gui/widgets/progressbar.h index 2f9e665f..52904f5a 100644 --- a/src/gui/widgets/progressbar.h +++ b/src/gui/widgets/progressbar.h @@ -19,16 +19,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PROGRESSBAR_H -#define PROGRESSBAR_H +#pragma once #include <guichan/widget.hpp> #include <string> -class Graphics; -class ImageRect; - /** * A progress bar. * @@ -44,19 +40,12 @@ class ProgressBar : public gcn::Widget int width = 40, int height = 7, int color = -1); - ~ProgressBar() override; - /** * Performs progress bar logic (fading colors) */ void logic() override; /** - * Update the alpha value to the graphic components. - */ - static void updateAlpha(); - - /** * Draws the progress bar. */ void draw(gcn::Graphics *graphics) override; @@ -111,13 +100,6 @@ class ProgressBar : public gcn::Widget void setSmoothColorChange(bool smoothColorChange) { mSmoothColorChange = smoothColorChange; } - /** - * Renders a progressbar with the given properties. - */ - static void render(Graphics *graphics, const gcn::Rectangle &area, - const gcn::Color &color, float progress, - const std::string &text = std::string()); - private: float mProgress, mProgressToGo; bool mSmoothProgress = true; @@ -128,12 +110,4 @@ class ProgressBar : public gcn::Widget bool mSmoothColorChange = true; std::string mText; - - static ImageRect mBorder; - static int mInstances; - static float mAlpha; - - static const gcn::Color TEXT_COLOR; }; - -#endif diff --git a/src/gui/widgets/progressindicator.cpp b/src/gui/widgets/progressindicator.cpp index 496bd8a1..ccd4fd54 100644 --- a/src/gui/widgets/progressindicator.cpp +++ b/src/gui/widgets/progressindicator.cpp @@ -21,10 +21,10 @@ #include "progressindicator.h" #include "graphics.h" +#include "gui/gui.h" #include "simpleanimation.h" #include "resources/animation.h" -#include "resources/imageset.h" #include "resources/resourcemanager.h" #include "resources/theme.h" @@ -32,12 +32,12 @@ ProgressIndicator::ProgressIndicator() { - ImageSet *images = Theme::getImageSetFromTheme("progress-indicator.png", - 32, 32); + const std::string path = gui->getTheme()->resolvePath("progress-indicator.png"); + mImageSet = ResourceManager::getInstance()->getImageSet(path, 32, 32); Animation anim; - for (size_t i = 0; i < images->size(); ++i) - anim.addFrame(images->get(i), 100, 0, 0); + for (size_t i = 0; i < mImageSet->size(); ++i) + anim.addFrame(mImageSet->get(i), 100, 0, 0); mIndicator = std::make_unique<SimpleAnimation>(std::move(anim)); diff --git a/src/gui/widgets/progressindicator.h b/src/gui/widgets/progressindicator.h index 428bbd02..4a6ea339 100644 --- a/src/gui/widgets/progressindicator.h +++ b/src/gui/widgets/progressindicator.h @@ -18,8 +18,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PROGRESSINDICATOR_H -#define PROGRESSINDICATOR_H +#pragma once + +#include "resources/imageset.h" #include <guichan/widget.hpp> @@ -42,6 +43,5 @@ public: private: std::unique_ptr<SimpleAnimation> mIndicator; + ResourceRef<ImageSet> mImageSet; }; - -#endif // PROGRESSINDICATOR_H diff --git a/src/gui/widgets/radiobutton.cpp b/src/gui/widgets/radiobutton.cpp index 92cdacd1..ceba78eb 100644 --- a/src/gui/widgets/radiobutton.cpp +++ b/src/gui/widgets/radiobutton.cpp @@ -21,109 +21,43 @@ #include "gui/widgets/radiobutton.h" -#include "configuration.h" -#include "graphics.h" +#include "textrenderer.h" -#include "resources/image.h" +#include "gui/gui.h" #include "resources/theme.h" -int RadioButton::instances = 0; -float RadioButton::mAlpha = 1.0; -Image *RadioButton::radioNormal; -Image *RadioButton::radioChecked; -Image *RadioButton::radioDisabled; -Image *RadioButton::radioDisabledChecked; -Image *RadioButton::radioNormalHi; -Image *RadioButton::radioCheckedHi; - -RadioButton::RadioButton(const std::string &caption, const std::string &group, - bool marked): - gcn::RadioButton(caption, group, marked) +RadioButton::RadioButton(const std::string &caption, + const std::string &group, + bool marked) + : gcn::RadioButton(caption, group, marked) { - if (instances == 0) - { - radioNormal = Theme::getImageFromTheme("radioout.png"); - radioChecked = Theme::getImageFromTheme("radioin.png"); - radioDisabled = Theme::getImageFromTheme("radioout.png"); - radioDisabledChecked = Theme::getImageFromTheme("radioin.png"); - radioNormalHi = Theme::getImageFromTheme("radioout_highlight.png"); - radioCheckedHi = Theme::getImageFromTheme("radioin_highlight.png"); - radioNormal->setAlpha(mAlpha); - radioChecked->setAlpha(mAlpha); - radioDisabled->setAlpha(mAlpha); - radioDisabledChecked->setAlpha(mAlpha); - radioNormalHi->setAlpha(mAlpha); - radioCheckedHi->setAlpha(mAlpha); - } - - instances++; + auto &skin = gui->getTheme()->getSkin(SkinType::RadioButton); + setWidth(skin.getMinWidth() + 2 * skin.padding + skin.spacing + getFont()->getWidth(caption)); + setHeight(skin.getMinHeight() + 2 * skin.padding); } -RadioButton::~RadioButton() +void RadioButton::draw(gcn::Graphics* graphics) { - instances--; + WidgetState widgetState(this); + if (mHasMouse) + widgetState.flags |= STATE_HOVERED; + if (isSelected()) + widgetState.flags |= STATE_SELECTED; - if (instances == 0) - { - radioNormal->decRef(); - radioChecked->decRef(); - radioDisabled->decRef(); - radioDisabledChecked->decRef(); - radioNormalHi->decRef(); - radioCheckedHi->decRef(); - } -} + auto &skin = gui->getTheme()->getSkin(SkinType::RadioButton); + skin.draw(static_cast<Graphics *>(graphics), widgetState); -void RadioButton::drawBox(gcn::Graphics* graphics) -{ - if (config.guiAlpha != mAlpha) + if (auto skinState = skin.getState(widgetState.flags)) { - mAlpha = config.guiAlpha; - radioNormal->setAlpha(mAlpha); - radioChecked->setAlpha(mAlpha); - radioDisabled->setAlpha(mAlpha); - radioDisabledChecked->setAlpha(mAlpha); - radioNormalHi->setAlpha(mAlpha); - radioCheckedHi->setAlpha(mAlpha); + auto &textFormat = skinState->textFormat; + TextRenderer::renderText(static_cast<Graphics *>(graphics), + getCaption(), + skin.getMinWidth() + skin.padding + skin.spacing, + skin.padding, + Graphics::LEFT, + textFormat.bold ? boldFont : getFont(), + textFormat); } - - Image *box = nullptr; - - if (isEnabled()) - if (isSelected()) - if (mHasMouse) - box = radioCheckedHi; - else - box = radioChecked; - else - if (mHasMouse) - box = radioNormalHi; - else - box = radioNormal; - else - if (isSelected()) - box = radioDisabledChecked; - else - box = radioDisabled; - - if (box) - static_cast<Graphics*>(graphics)->drawImage(box, 2, 2); -} - -void RadioButton::draw(gcn::Graphics* graphics) -{ - graphics->pushClipArea(gcn::Rectangle(1, 1, getWidth() - 1, - getHeight() - 1)); - - drawBox(graphics); - - graphics->popClipArea(); - - graphics->setFont(getFont()); - graphics->setColor(getForegroundColor()); - - int h = getHeight() + getHeight() / 2; - graphics->drawText(getCaption(), h - 2, 0); } void RadioButton::mouseEntered(gcn::MouseEvent& event) diff --git a/src/gui/widgets/radiobutton.h b/src/gui/widgets/radiobutton.h index 2a96ff6e..fda43d01 100644 --- a/src/gui/widgets/radiobutton.h +++ b/src/gui/widgets/radiobutton.h @@ -19,55 +19,40 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef RADIOBUTTON_H -#define RADIOBUTTON_H +#pragma once #include <guichan/widgets/radiobutton.hpp> -class Image; - /** * Guichan based RadioButton with custom look */ class RadioButton : public gcn::RadioButton { public: - RadioButton(const std::string &caption,const std::string &group, - bool marked = false); - - ~RadioButton() override; + RadioButton(const std::string &caption, + const std::string &group, + bool marked = false); /** - * Draws the radiobutton, not the caption. + * Implementation of the draw method. */ - void drawBox(gcn::Graphics* graphics) override; + void draw(gcn::Graphics *graphics) override; /** - * Implementation of the draw methods. - * Thus, avoiding the rhomb around the radio button. + * Overridden because box is drawn in RadioButton::draw. */ - void draw(gcn::Graphics* graphics) override; + void drawBox(gcn::Graphics *graphics) override {} /** * Called when the mouse enteres the widget area. */ - void mouseEntered(gcn::MouseEvent& event) override; + void mouseEntered(gcn::MouseEvent &event) override; /** * Called when the mouse leaves the widget area. */ - void mouseExited(gcn::MouseEvent& event) override; + void mouseExited(gcn::MouseEvent &event) override; private: - static int instances; - static float mAlpha; bool mHasMouse = false; - static Image *radioNormal; - static Image *radioChecked; - static Image *radioDisabled; - static Image *radioDisabledChecked; - static Image *radioNormalHi; - static Image *radioCheckedHi; }; - -#endif // RADIOBUTTON_H diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp index dd29a977..0c5a7fb5 100644 --- a/src/gui/widgets/resizegrip.cpp +++ b/src/gui/widgets/resizegrip.cpp @@ -21,48 +21,22 @@ #include "gui/widgets/resizegrip.h" -#include "configuration.h" #include "graphics.h" -#include "resources/image.h" +#include "gui/gui.h" #include "resources/theme.h" #include <guichan/graphics.hpp> -Image *ResizeGrip::gripImage = nullptr; -int ResizeGrip::mInstances = 0; -float ResizeGrip::mAlpha = 1.0; - -ResizeGrip::ResizeGrip(const std::string &image) -{ - if (mInstances == 0) - { - // Load the grip image - gripImage = Theme::getImageFromTheme(image); - gripImage->setAlpha(mAlpha); - } - - mInstances++; - - setWidth(gripImage->getWidth() + 2); - setHeight(gripImage->getHeight() + 2); -} - -ResizeGrip::~ResizeGrip() +ResizeGrip::ResizeGrip() { - mInstances--; - - if (mInstances == 0) - gripImage->decRef(); + auto &skin = gui->getTheme()->getSkin(SkinType::ResizeGrip); + setSize(skin.width, skin.height); } void ResizeGrip::draw(gcn::Graphics *graphics) { - if (config.guiAlpha != mAlpha) - { - mAlpha = config.guiAlpha; - gripImage->setAlpha(mAlpha); - } - - static_cast<Graphics*>(graphics)->drawImage(gripImage, 0, 0); + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), + SkinType::ResizeGrip, + WidgetState(this)); } diff --git a/src/gui/widgets/resizegrip.h b/src/gui/widgets/resizegrip.h index d2f8ca4d..9b4e0611 100644 --- a/src/gui/widgets/resizegrip.h +++ b/src/gui/widgets/resizegrip.h @@ -19,13 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef RESIZEGRIP_H -#define RESIZEGRIP_H +#pragma once #include <guichan/widget.hpp> -class Image; - /** * Resize grip. The resize grip is part of a resizable Window. It relies on the * fact that uncaught mouse events are automatically routed to the parent @@ -36,19 +33,10 @@ class Image; class ResizeGrip : public gcn::Widget { public: - ResizeGrip(const std::string &image = "resize.png"); - - ~ResizeGrip() override; + ResizeGrip(); /** * Draws the resize grip. */ void draw(gcn::Graphics *graphics) override; - - private: - static Image *gripImage; /**< Resize grip image */ - static int mInstances; /**< Number of resize grip instances */ - static float mAlpha; }; - -#endif diff --git a/src/gui/widgets/scrollarea.cpp b/src/gui/widgets/scrollarea.cpp index 225a231d..1dec34be 100644 --- a/src/gui/widgets/scrollarea.cpp +++ b/src/gui/widgets/scrollarea.cpp @@ -21,24 +21,14 @@ #include "gui/widgets/scrollarea.h" -#include "configuration.h" #include "graphics.h" -#include "resources/image.h" -#include "resources/theme.h" +#include "gui/gui.h" -#include "utils/dtor.h" - -int ScrollArea::instances = 0; -float ScrollArea::mAlpha = 1.0; -ImageRect ScrollArea::background; -ImageRect ScrollArea::vMarker; -ImageRect ScrollArea::vMarkerHi; -Image *ScrollArea::buttons[4][2]; +#include <guichan/exception.hpp> ScrollArea::ScrollArea() { - addWidgetListener(this); init(); } @@ -51,24 +41,11 @@ ScrollArea::ScrollArea(gcn::Widget *widget): ScrollArea::~ScrollArea() { delete getContent(); +} - instances--; - - if (instances == 0) - { - std::for_each(background.grid, background.grid + 9, dtor<Image*>()); - std::for_each(vMarker.grid, vMarker.grid + 9, dtor<Image*>()); - std::for_each(vMarkerHi.grid, vMarkerHi.grid + 9, dtor<Image*>()); - - buttons[UP][0]->decRef(); - buttons[UP][1]->decRef(); - buttons[DOWN][0]->decRef(); - buttons[DOWN][1]->decRef(); - buttons[LEFT][0]->decRef(); - buttons[LEFT][1]->decRef(); - buttons[RIGHT][0]->decRef(); - buttons[RIGHT][1]->decRef(); - } +void ScrollArea::setShowButtons(bool showButtons) +{ + mShowButtons = showButtons; } void ScrollArea::init() @@ -76,83 +53,27 @@ void ScrollArea::init() // Draw background by default setOpaque(true); - setUpButtonScrollAmount(2); - setDownButtonScrollAmount(2); - setLeftButtonScrollAmount(2); - setRightButtonScrollAmount(2); - - if (instances == 0) - { - // Load the background skin - Image *textbox = Theme::getImageFromTheme("deepbox.png"); - const int bggridx[4] = {0, 3, 28, 31}; - const int bggridy[4] = {0, 3, 28, 31}; - int a = 0; - - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - background.grid[a] = textbox->getSubImage( - bggridx[x], bggridy[y], - bggridx[x + 1] - bggridx[x] + 1, - bggridy[y + 1] - bggridy[y] + 1); - a++; - } - } - background.setAlpha(config.guiAlpha); + auto theme = gui->getTheme(); - textbox->decRef(); + int scrollBarWidth = theme->getSkin(SkinType::ScrollAreaVBar).width; + if (scrollBarWidth > 0) + setScrollbarWidth(scrollBarWidth); - // Load vertical scrollbar skin - Image *vscroll = Theme::getImageFromTheme("vscroll_grey.png"); - Image *vscrollHi = Theme::getImageFromTheme("vscroll_highlight.png"); + auto &scrollAreaSkin = theme->getSkin(SkinType::ScrollArea); + setShowButtons(scrollAreaSkin.showButtons); - int vsgridx[4] = {0, 4, 7, 11}; - int vsgridy[4] = {0, 4, 15, 19}; - a = 0; - - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - vMarker.grid[a] = vscroll->getSubImage( - vsgridx[x], vsgridy[y], - vsgridx[x + 1] - vsgridx[x], - vsgridy[y + 1] - vsgridy[y]); - vMarkerHi.grid[a] = vscrollHi->getSubImage( - vsgridx[x], vsgridy[y], - vsgridx[x + 1] - vsgridx[x], - vsgridy[y + 1] - vsgridy[y]); - a++; - } - } + if (auto content = getContent()) + content->setFrameSize(scrollAreaSkin.padding); - vMarker.setAlpha(config.guiAlpha); - vMarkerHi.setAlpha(config.guiAlpha); - - vscroll->decRef(); - vscrollHi->decRef(); - - buttons[UP][0] = - Theme::getImageFromTheme("vscroll_up_default.png"); - buttons[DOWN][0] = - Theme::getImageFromTheme("vscroll_down_default.png"); - buttons[LEFT][0] = - Theme::getImageFromTheme("hscroll_left_default.png"); - buttons[RIGHT][0] = - Theme::getImageFromTheme("hscroll_right_default.png"); - buttons[UP][1] = - Theme::getImageFromTheme("vscroll_up_pressed.png"); - buttons[DOWN][1] = - Theme::getImageFromTheme("vscroll_down_pressed.png"); - buttons[LEFT][1] = - Theme::getImageFromTheme("hscroll_left_pressed.png"); - buttons[RIGHT][1] = - Theme::getImageFromTheme("hscroll_right_pressed.png"); - } + // The base color is only used when rendering a square in the corner where + // the scrollbars meet. We disable rendering of this square by setting the + // base color to transparent. + setBaseColor(gcn::Color(0, 0, 0, 0)); - instances++; + setUpButtonScrollAmount(5); + setDownButtonScrollAmount(5); + setLeftButtonScrollAmount(5); + setRightButtonScrollAmount(5); } void ScrollArea::logic() @@ -201,159 +122,237 @@ void ScrollArea::logic() } } -void ScrollArea::updateAlpha() +void ScrollArea::draw(gcn::Graphics *graphics) { - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (alpha != mAlpha) - { - mAlpha = alpha; + if (getFrameSize() == 0) + drawFrame(graphics); - background.setAlpha(mAlpha); - vMarker.setAlpha(mAlpha); - vMarkerHi.setAlpha(mAlpha); - } + gcn::ScrollArea::draw(graphics); } -void ScrollArea::draw(gcn::Graphics *graphics) +void ScrollArea::drawFrame(gcn::Graphics *graphics) { - if (mVBarVisible) - { - drawUpButton(graphics); - drawDownButton(graphics); - drawVBar(graphics); - drawVMarker(graphics); - } - - if (mHBarVisible) - { - drawLeftButton(graphics); - drawRightButton(graphics); - drawHBar(graphics); - drawHMarker(graphics); - } + if (!mOpaque) + return; - if (mHBarVisible && mVBarVisible) - { - graphics->setColor(getBaseColor()); - graphics->fillRectangle(gcn::Rectangle(getWidth() - mScrollbarWidth, - getHeight() - mScrollbarWidth, - mScrollbarWidth, - mScrollbarWidth)); - } + const int bs = getFrameSize(); - updateAlpha(); + WidgetState state(this); + state.width += bs * 2; + state.height += + bs * 2; - drawChildren(graphics); + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollArea, state); } -void ScrollArea::drawFrame(gcn::Graphics *graphics) +void ScrollArea::drawChildren(gcn::Graphics *graphics) { - if (mOpaque) - { - const int bs = getFrameSize(); - const int w = getWidth() + bs * 2; - const int h = getHeight() + bs * 2; + auto g = static_cast<Graphics*>(graphics); + g->pushClipRect(getChildrenArea()); - static_cast<Graphics*>(graphics)-> - drawImageRect(0, 0, w, h, background); - } + gcn::ScrollArea::drawChildren(graphics); + + g->popClipRect(); } void ScrollArea::setOpaque(bool opaque) { mOpaque = opaque; - setFrameSize(mOpaque ? 2 : 0); + + auto &skin = gui->getTheme()->getSkin(SkinType::ScrollArea); + setFrameSize(mOpaque ? skin.frameSize : 0); } -void ScrollArea::drawButton(gcn::Graphics *graphics, BUTTON_DIR dir) +void ScrollArea::drawBackground(gcn::Graphics *graphics) { - int state = 0; - gcn::Rectangle dim; + // background is drawn as part of the frame instead +} - switch (dir) - { - case UP: - state = mUpButtonPressed ? 1 : 0; - dim = getUpButtonDimension(); - break; - case DOWN: - state = mDownButtonPressed ? 1 : 0; - dim = getDownButtonDimension(); - break; - case LEFT: - state = mLeftButtonPressed ? 1 : 0; - dim = getLeftButtonDimension(); - break; - case RIGHT: - state = mRightButtonPressed ? 1 : 0; - dim = getRightButtonDimension(); - break; - } +static void drawButton(gcn::Graphics *graphics, + SkinType skinType, + bool pressed, + const gcn::Rectangle &dim) +{ + WidgetState state(dim); + if (pressed) + state.flags |= STATE_SELECTED; - static_cast<Graphics*>(graphics)-> - drawImage(buttons[dir][state], dim.x, dim.y); + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), skinType, state); } void ScrollArea::drawUpButton(gcn::Graphics *graphics) { - drawButton(graphics, UP); + if (!mShowButtons) + return; + + drawButton(graphics, SkinType::ButtonUp, mUpButtonPressed, getUpButtonDimension()); } void ScrollArea::drawDownButton(gcn::Graphics *graphics) { - drawButton(graphics, DOWN); + if (!mShowButtons) + return; + + drawButton(graphics, SkinType::ButtonDown, mDownButtonPressed, getDownButtonDimension()); } void ScrollArea::drawLeftButton(gcn::Graphics *graphics) { - drawButton(graphics, LEFT); + if (!mShowButtons) + return; + + drawButton(graphics, SkinType::ButtonLeft, mLeftButtonPressed, getLeftButtonDimension()); } void ScrollArea::drawRightButton(gcn::Graphics *graphics) { - drawButton(graphics, RIGHT); + if (!mShowButtons) + return; + + drawButton(graphics, SkinType::ButtonRight, mRightButtonPressed, getRightButtonDimension()); } void ScrollArea::drawVBar(gcn::Graphics *graphics) { - const gcn::Rectangle dim = getVerticalBarDimension(); - graphics->setColor(gcn::Color(0, 0, 0, 32)); - graphics->fillRectangle(dim); - graphics->setColor(gcn::Color(255, 255, 255)); + WidgetState state(getVerticalBarDimension()); + if (mHasMouse && (mX > (getWidth() - getScrollbarWidth()))) + state.flags |= STATE_HOVERED; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaVBar, state); } void ScrollArea::drawHBar(gcn::Graphics *graphics) { - const gcn::Rectangle dim = getHorizontalBarDimension(); - graphics->setColor(gcn::Color(0, 0, 0, 32)); - graphics->fillRectangle(dim); - graphics->setColor(gcn::Color(255, 255, 255)); + WidgetState state(getHorizontalBarDimension()); + if (mHasMouse && (mY > (getHeight() - getScrollbarWidth()))) + state.flags |= STATE_HOVERED; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaHBar, state); } void ScrollArea::drawVMarker(gcn::Graphics *graphics) { - gcn::Rectangle dim = getVerticalMarkerDimension(); + WidgetState state(getVerticalMarkerDimension()); + if (state.height == 0) + return; - if ((mHasMouse) && (mX > (getWidth() - getScrollbarWidth()))) - static_cast<Graphics*>(graphics)-> - drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarkerHi); - else - static_cast<Graphics*>(graphics)-> - drawImageRect(dim.x, dim.y, dim.width, dim.height,vMarker); + if (mHasMouse && (mX > (getWidth() - getScrollbarWidth()))) + state.flags |= STATE_HOVERED; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaVMarker, state); } void ScrollArea::drawHMarker(gcn::Graphics *graphics) { - gcn::Rectangle dim = getHorizontalMarkerDimension(); + WidgetState state(getHorizontalMarkerDimension()); + if (state.width == 0) + return; + + if (mHasMouse && (mY > (getHeight() - getScrollbarWidth()))) + state.flags |= STATE_HOVERED; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaHMarker, state); +} + +/** + * Code copied from gcn::ScrollArea::checkPolicies to make sure it takes the + * frame size of the content into account. + */ +void ScrollArea::checkPolicies() +{ + int w = getWidth(); + int h = getHeight(); - if ((mHasMouse) && (mY > (getHeight() - getScrollbarWidth()))) - static_cast<Graphics*>(graphics)-> - drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarkerHi); - else - static_cast<Graphics*>(graphics)-> - drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarker); + mHBarVisible = false; + mVBarVisible = false; + + if (!getContent()) + { + mHBarVisible = (mHPolicy == SHOW_ALWAYS); + mVBarVisible = (mVPolicy == SHOW_ALWAYS); + return; + } + + const int contentFrameSize = getContent()->getFrameSize(); + w -= 2 * contentFrameSize; + h -= 2 * contentFrameSize; + + if (mHPolicy == SHOW_AUTO && + mVPolicy == SHOW_AUTO) + { + if (getContent()->getWidth() <= w + && getContent()->getHeight() <= h) + { + mHBarVisible = false; + mVBarVisible = false; + } + + if (getContent()->getWidth() > w) + { + mHBarVisible = true; + } + + if ((getContent()->getHeight() > h) + || (mHBarVisible && getContent()->getHeight() > h - mScrollbarWidth)) + { + mVBarVisible = true; + } + + if (mVBarVisible && getContent()->getWidth() > w - mScrollbarWidth) + { + mHBarVisible = true; + } + + return; + } + + switch (mHPolicy) + { + case SHOW_NEVER: + mHBarVisible = false; + break; + + case SHOW_ALWAYS: + mHBarVisible = true; + break; + + case SHOW_AUTO: + if (mVPolicy == SHOW_NEVER) + { + mHBarVisible = getContent()->getWidth() > w; + } + else // (mVPolicy == SHOW_ALWAYS) + { + mHBarVisible = getContent()->getWidth() > w - mScrollbarWidth; + } + break; + + default: + throw GCN_EXCEPTION("Horizontal scroll policy invalid."); + } + + switch (mVPolicy) + { + case SHOW_NEVER: + mVBarVisible = false; + break; + + case SHOW_ALWAYS: + mVBarVisible = true; + break; + + case SHOW_AUTO: + if (mHPolicy == SHOW_NEVER) + { + mVBarVisible = getContent()->getHeight() > h; + } + else // (mHPolicy == SHOW_ALWAYS) + { + mVBarVisible = getContent()->getHeight() > h - mScrollbarWidth; + } + break; + default: + throw GCN_EXCEPTION("Vertical scroll policy invalid."); + } } void ScrollArea::mouseMoved(gcn::MouseEvent& event) @@ -372,8 +371,299 @@ void ScrollArea::mouseExited(gcn::MouseEvent& event) mHasMouse = false; } -void ScrollArea::widgetResized(const gcn::Event &event) +/** + * Code copied from gcn::ScrollArea::mousePressed to make it call our custom + * getVerticalMarkerDimension and getHorizontalMarkerDimension functions. + */ +void ScrollArea::mousePressed(gcn::MouseEvent &mouseEvent) { - getContent()->setSize(getWidth() - 2 * getFrameSize(), - getHeight() - 2 * getFrameSize()); + int x = mouseEvent.getX(); + int y = mouseEvent.getY(); + + if (getUpButtonDimension().isPointInRect(x, y)) + { + setVerticalScrollAmount(getVerticalScrollAmount() + - mUpButtonScrollAmount); + mUpButtonPressed = true; + } + else if (getDownButtonDimension().isPointInRect(x, y)) + { + setVerticalScrollAmount(getVerticalScrollAmount() + + mDownButtonScrollAmount); + mDownButtonPressed = true; + } + else if (getLeftButtonDimension().isPointInRect(x, y)) + { + setHorizontalScrollAmount(getHorizontalScrollAmount() + - mLeftButtonScrollAmount); + mLeftButtonPressed = true; + } + else if (getRightButtonDimension().isPointInRect(x, y)) + { + setHorizontalScrollAmount(getHorizontalScrollAmount() + + mRightButtonScrollAmount); + mRightButtonPressed = true; + } + else if (getVerticalMarkerDimension().isPointInRect(x, y)) + { + mIsHorizontalMarkerDragged = false; + mIsVerticalMarkerDragged = true; + + mVerticalMarkerDragOffset = y - getVerticalMarkerDimension().y; + } + else if (getVerticalBarDimension().isPointInRect(x,y)) + { + if (y < getVerticalMarkerDimension().y) + { + setVerticalScrollAmount(getVerticalScrollAmount() + - (int)(getChildrenArea().height * 0.95)); + } + else + { + setVerticalScrollAmount(getVerticalScrollAmount() + + (int)(getChildrenArea().height * 0.95)); + } + } + else if (getHorizontalMarkerDimension().isPointInRect(x, y)) + { + mIsHorizontalMarkerDragged = true; + mIsVerticalMarkerDragged = false; + + mHorizontalMarkerDragOffset = x - getHorizontalMarkerDimension().x; + } + else if (getHorizontalBarDimension().isPointInRect(x,y)) + { + if (x < getHorizontalMarkerDimension().x) + { + setHorizontalScrollAmount(getHorizontalScrollAmount() + - (int)(getChildrenArea().width * 0.95)); + } + else + { + setHorizontalScrollAmount(getHorizontalScrollAmount() + + (int)(getChildrenArea().width * 0.95)); + } + } +} + +/** + * Code copied from gcn::ScrollArea::mouseDragged to make it call our custom + * getVerticalMarkerDimension and getHorizontalMarkerDimension functions. + */ +void ScrollArea::mouseDragged(gcn::MouseEvent &mouseEvent) +{ + if (mIsVerticalMarkerDragged) + { + int pos = mouseEvent.getY() - getVerticalBarDimension().y - mVerticalMarkerDragOffset; + int length = getVerticalMarkerDimension().height; + + gcn::Rectangle barDim = getVerticalBarDimension(); + + if ((barDim.height - length) > 0) + { + setVerticalScrollAmount((getVerticalMaxScroll() * pos) + / (barDim.height - length)); + } + else + { + setVerticalScrollAmount(0); + } + } + + if (mIsHorizontalMarkerDragged) + { + int pos = mouseEvent.getX() - getHorizontalBarDimension().x - mHorizontalMarkerDragOffset; + int length = getHorizontalMarkerDimension().width; + + gcn::Rectangle barDim = getHorizontalBarDimension(); + + if ((barDim.width - length) > 0) + { + setHorizontalScrollAmount((getHorizontalMaxScroll() * pos) + / (barDim.width - length)); + } + else + { + setHorizontalScrollAmount(0); + } + } + + mouseEvent.consume(); +} + +gcn::Rectangle ScrollArea::getUpButtonDimension() +{ + if (!mVBarVisible || !mShowButtons) + return gcn::Rectangle(); + + return gcn::Rectangle(getWidth() - mScrollbarWidth, 0, mScrollbarWidth, mScrollbarWidth); +} + +gcn::Rectangle ScrollArea::getDownButtonDimension() +{ + if (!mVBarVisible || !mShowButtons) + return gcn::Rectangle(); + + gcn::Rectangle dim(getWidth() - mScrollbarWidth, + getHeight() - mScrollbarWidth, + mScrollbarWidth, + mScrollbarWidth); + + if (mHBarVisible) + dim.y -= mScrollbarWidth; + + return dim; +} + +gcn::Rectangle ScrollArea::getLeftButtonDimension() +{ + if (!mHBarVisible || !mShowButtons) + return gcn::Rectangle(); + + return gcn::Rectangle(0, getHeight() - mScrollbarWidth, mScrollbarWidth, mScrollbarWidth); +} + +gcn::Rectangle ScrollArea::getRightButtonDimension() +{ + if (!mHBarVisible || !mShowButtons) + return gcn::Rectangle(); + + gcn::Rectangle dim(getWidth() - mScrollbarWidth, + getHeight() - mScrollbarWidth, + mScrollbarWidth, + mScrollbarWidth); + + if (mVBarVisible) + dim.x -= mScrollbarWidth; + + return dim; +} + +gcn::Rectangle ScrollArea::getVerticalBarDimension() +{ + if (!mVBarVisible) + return gcn::Rectangle(); + + gcn::Rectangle dim(getWidth() - mScrollbarWidth, + getUpButtonDimension().height, + mScrollbarWidth, + getHeight() + - getUpButtonDimension().height + - getDownButtonDimension().height); + + if (mHBarVisible) + dim.height -= mScrollbarWidth; + + if (dim.height < 0) + dim.height = 0; + + return dim; +} + +gcn::Rectangle ScrollArea::getHorizontalBarDimension() +{ + if (!mHBarVisible) + return gcn::Rectangle(); + + gcn::Rectangle dim(getLeftButtonDimension().width, + getHeight() - mScrollbarWidth, + getWidth() + - getLeftButtonDimension().width + - getRightButtonDimension().width, + mScrollbarWidth); + + if (mVBarVisible) + dim.width -= mScrollbarWidth; + + if (dim.width < 0) + dim.width = 0; + + return dim; +} + +static void getMarkerValues(int barSize, + int maxScroll, int scrollAmount, + int contentHeight, int viewHeight, + int fixedMarkerSize, int minMarkerSize, + int &markerSize, int &markerPos) +{ + if (fixedMarkerSize == 0) + { + if (contentHeight != 0 && contentHeight > viewHeight) + markerSize = std::max((barSize * viewHeight) / contentHeight, minMarkerSize); + else + markerSize = barSize; + } + else + { + if (contentHeight > viewHeight) + markerSize = fixedMarkerSize; + else + markerSize = 0; + } + + // Hide the marker when it doesn't fit + if (markerSize > barSize) + markerSize = 0; + + if (maxScroll != 0) + markerPos = ((barSize - markerSize) * scrollAmount + maxScroll / 2) / maxScroll; + else + markerPos = 0; +} + +gcn::Rectangle ScrollArea::getVerticalMarkerDimension() +{ + if (!mVBarVisible) + return gcn::Rectangle(); + + auto &markerSkin = gui->getTheme()->getSkin(SkinType::ScrollAreaVMarker); + const gcn::Rectangle barDim = getVerticalBarDimension(); + + int contentHeight = 0; + if (auto content = getContent()) + contentHeight = content->getHeight() + content->getFrameSize() * 2; + + int length; + int pos; + + getMarkerValues(barDim.height, + getVerticalMaxScroll(), + getVerticalScrollAmount(), + contentHeight, + getChildrenArea().height, + markerSkin.height, + mScrollbarWidth, + length, + pos); + + return gcn::Rectangle(barDim.x, barDim.y + pos, mScrollbarWidth, length); +} + +gcn::Rectangle ScrollArea::getHorizontalMarkerDimension() +{ + if (!mHBarVisible) + return gcn::Rectangle(); + + auto &markerSkin = gui->getTheme()->getSkin(SkinType::ScrollAreaHMarker); + const gcn::Rectangle barDim = getHorizontalBarDimension(); + + int contentWidth = 0; + if (auto content = getContent()) + contentWidth = content->getWidth() + content->getFrameSize() * 2; + + int length; + int pos; + + getMarkerValues(barDim.width, + getHorizontalMaxScroll(), + getHorizontalScrollAmount(), + contentWidth, + getChildrenArea().width, + markerSkin.width, + mScrollbarWidth, + length, + pos); + + return gcn::Rectangle(barDim.x + pos, barDim.y, length, mScrollbarWidth); } diff --git a/src/gui/widgets/scrollarea.h b/src/gui/widgets/scrollarea.h index 2fae2d4b..0ba10578 100644 --- a/src/gui/widgets/scrollarea.h +++ b/src/gui/widgets/scrollarea.h @@ -19,14 +19,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SCROLLAREA_H -#define SCROLLAREA_H +#pragma once #include <guichan/widgets/scrollarea.hpp> -#include <guichan/widgetlistener.hpp> - -class Image; -class ImageRect; /** * A scroll area. @@ -35,9 +30,11 @@ class ImageRect; * content. However, it won't delete a previously set content widget when * setContent is called! * + * Also overrides several functions to support fixed-size scroll bar markers. + * * \ingroup GUI */ -class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener +class ScrollArea : public gcn::ScrollArea { public: /** @@ -59,18 +56,18 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener ~ScrollArea() override; /** - * Logic function optionally adapts width or height of contents. This - * depends on the scrollbar settings. + * Sets whether the scroll bar buttons are shown. */ - void logic() override; + void setShowButtons(bool showButtons); /** - * Update the alpha value to the graphic components. + * Logic function optionally adapts width or height of contents. This + * depends on the scrollbar settings. */ - static void updateAlpha(); + void logic() override; /** - * Draws the scroll area. + * Overridden to draw the frame if its size is 0. */ void draw(gcn::Graphics *graphics) override; @@ -80,6 +77,11 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener void drawFrame(gcn::Graphics *graphics) override; /** + * Applies clipping to the contents. + */ + void drawChildren(gcn::Graphics *graphics) override; + + /** * Sets whether the widget should draw its background or not. */ void setOpaque(bool opaque); @@ -92,34 +94,28 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener /** * Called when the mouse moves in the widget area. */ - void mouseMoved(gcn::MouseEvent& event) override; + void mouseMoved(gcn::MouseEvent &event) override; /** * Called when the mouse enteres the widget area. */ - void mouseEntered(gcn::MouseEvent& event) override; + void mouseEntered(gcn::MouseEvent &event) override; /** * Called when the mouse leaves the widget area. */ - void mouseExited(gcn::MouseEvent& event) override; + void mouseExited(gcn::MouseEvent &event) override; - void widgetResized(const gcn::Event &event) override; + void mousePressed(gcn::MouseEvent &mouseEvent) override; + void mouseDragged(gcn::MouseEvent &mouseEvent) override; protected: - enum BUTTON_DIR { - UP, - DOWN, - LEFT, - RIGHT - }; - /** * Initializes the scroll area. */ void init(); - void drawButton(gcn::Graphics *graphics, BUTTON_DIR dir); + void drawBackground(gcn::Graphics *graphics) override; void drawUpButton(gcn::Graphics *graphics) override; void drawDownButton(gcn::Graphics *graphics) override; void drawLeftButton(gcn::Graphics *graphics) override; @@ -129,17 +125,31 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener void drawVMarker(gcn::Graphics *graphics) override; void drawHMarker(gcn::Graphics *graphics) override; - static int instances; - static float mAlpha; - static ImageRect background; - static ImageRect vMarker; - static ImageRect vMarkerHi; - static Image *buttons[4][2]; + void checkPolicies() override; + + /** + * Shadowing these functions from gcn::ScrollArea with versions that + * support hiding the buttons. We need to make sure we always use these + * versions. + */ + gcn::Rectangle getUpButtonDimension(); + gcn::Rectangle getDownButtonDimension(); + gcn::Rectangle getLeftButtonDimension(); + gcn::Rectangle getRightButtonDimension(); + gcn::Rectangle getVerticalBarDimension(); + gcn::Rectangle getHorizontalBarDimension(); + + /** + * Shadowing these functions from gcn::ScrollArea with versions that + * supports fixed-size scroll bar markers. We need to make sure we + * always use these versions. + */ + gcn::Rectangle getVerticalMarkerDimension(); + gcn::Rectangle getHorizontalMarkerDimension(); int mX = 0; int mY = 0; bool mHasMouse = false; bool mOpaque = true; + bool mShowButtons = true; }; - -#endif diff --git a/src/gui/widgets/setuptab.h b/src/gui/widgets/setuptab.h index 0cc35a98..78cef5b2 100644 --- a/src/gui/widgets/setuptab.h +++ b/src/gui/widgets/setuptab.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_SETUPTAB_H -#define GUI_SETUPTAB_H +#pragma once #include "gui/widgets/container.h" @@ -58,5 +57,3 @@ protected: private: std::string mName; }; - -#endif diff --git a/src/gui/widgets/shopitems.h b/src/gui/widgets/shopitems.h index e213f67c..1b6e1727 100644 --- a/src/gui/widgets/shopitems.h +++ b/src/gui/widgets/shopitems.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SHOP_H -#define SHOP_H +#pragma once #include <guichan/listmodel.hpp> @@ -111,5 +110,3 @@ class ShopItems : public gcn::ListModel /** Look for duplicate entries on addition. */ bool mMergeDuplicates; }; - -#endif // SHOP_H diff --git a/src/gui/widgets/shoplistbox.cpp b/src/gui/widgets/shoplistbox.cpp index 31c733a6..e2313c85 100644 --- a/src/gui/widgets/shoplistbox.cpp +++ b/src/gui/widgets/shoplistbox.cpp @@ -71,14 +71,13 @@ void ShopListBox::draw(gcn::Graphics *gcnGraphics) return; const int alpha = (int)(config.guiAlpha * 255.0f); - const gcn::Color &highlightColor = - Theme::getThemeColor(Theme::HIGHLIGHT, alpha); - const gcn::Color &backgroundColor = - Theme::getThemeColor(Theme::BACKGROUND, alpha); - const gcn::Color &warningColor = - Theme::getThemeColor(Theme::SHOP_WARNING, alpha); - const gcn::Color &textColor = - Theme::getThemeColor(Theme::TEXT); + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + auto backgroundColor = Theme::getThemeColor(Theme::BACKGROUND); + auto warningColor = Theme::getThemeColor(Theme::SHOP_WARNING); + auto textColor = Theme::getThemeColor(Theme::TEXT); + highlightColor.a = alpha; + backgroundColor.a = alpha; + warningColor.a = alpha; auto *graphics = static_cast<Graphics*>(gcnGraphics); @@ -168,8 +167,7 @@ void ShopListBox::mouseMoved(gcn::MouseEvent &event) } else { - Item *item = mShopItems->at(index); - if (item) + if (Item *item = mShopItems->at(index)) { mItemPopup->setItem(item->getInfo()); mItemPopup->position(viewport->getMouseX(), viewport->getMouseY()); diff --git a/src/gui/widgets/shoplistbox.h b/src/gui/widgets/shoplistbox.h index 4dbd756b..f6a1b12a 100644 --- a/src/gui/widgets/shoplistbox.h +++ b/src/gui/widgets/shoplistbox.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SHOPLISTBOX_H -#define SHOPLISTBOX_H +#pragma once #include "gui/widgets/listbox.h" @@ -97,5 +96,3 @@ class ShopListBox : public ListBox bool mPriceCheck; }; - -#endif // SHOPLISTBOX_H diff --git a/src/gui/widgets/shortcutcontainer.cpp b/src/gui/widgets/shortcutcontainer.cpp index 5925752e..9271b1f4 100644 --- a/src/gui/widgets/shortcutcontainer.cpp +++ b/src/gui/widgets/shortcutcontainer.cpp @@ -21,10 +21,18 @@ #include "gui/widgets/shortcutcontainer.h" -float ShortcutContainer::mAlpha = 1.0; +#include "gui/gui.h" + +#include "resources/theme.h" ShortcutContainer::ShortcutContainer() { + addMouseListener(this); + addWidgetListener(this); + + auto &skin = gui->getTheme()->getSkin(SkinType::ShortcutBox); + mBoxWidth = skin.width; + mBoxHeight = skin.height; } void ShortcutContainer::widgetResized(const gcn::Event &event) diff --git a/src/gui/widgets/shortcutcontainer.h b/src/gui/widgets/shortcutcontainer.h index cab20f27..35a88d7f 100644 --- a/src/gui/widgets/shortcutcontainer.h +++ b/src/gui/widgets/shortcutcontainer.h @@ -19,15 +19,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SHORTCUTCONTAINER_H -#define SHORTCUTCONTAINER_H +#pragma once #include <guichan/mouselistener.hpp> #include <guichan/widget.hpp> #include <guichan/widgetlistener.hpp> -class Image; - /** * A generic shortcut container. * @@ -45,20 +42,18 @@ class ShortcutContainer : public gcn::Widget, */ void draw(gcn::Graphics *graphics) override = 0; + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics *graphics) override {} + /** * Invoked when a widget changes its size. This is used to determine * the new height of the container. */ void widgetResized(const gcn::Event &event) override; - int getMaxItems() const - { return mMaxItems; } - - int getBoxWidth() const - { return mBoxWidth; } - - int getBoxHeight() const - { return mBoxHeight; } + int getMaxItems() const { return mMaxItems; } + int getBoxWidth() const { return mBoxWidth; } + int getBoxHeight() const { return mBoxHeight; } protected: /** @@ -70,10 +65,6 @@ class ShortcutContainer : public gcn::Widget, */ int getIndexFromGrid(int pointX, int pointY) const; - Image *mBackgroundImg; - - static float mAlpha; - int mMaxItems = 0; int mBoxWidth = 0; int mBoxHeight = 0; @@ -82,5 +73,3 @@ class ShortcutContainer : public gcn::Widget, int mGridWidth = 1; int mGridHeight = 1; }; - -#endif diff --git a/src/gui/widgets/slider.cpp b/src/gui/widgets/slider.cpp index a7ba37e8..bad10c15 100644 --- a/src/gui/widgets/slider.cpp +++ b/src/gui/widgets/slider.cpp @@ -21,19 +21,11 @@ #include "gui/widgets/slider.h" -#include "configuration.h" #include "graphics.h" -#include "resources/image.h" +#include "gui/gui.h" #include "resources/theme.h" -Image *Slider::hStart, *Slider::hMid, *Slider::hEnd, *Slider::hGrip; -Image *Slider::vStart, *Slider::vMid, *Slider::vEnd, *Slider::vGrip; -Image *Slider::hStartHi, *Slider::hMidHi, *Slider::hEndHi, *Slider::hGripHi; -Image *Slider::vStartHi, *Slider::vMidHi, *Slider::vEndHi, *Slider::vGripHi; -float Slider::mAlpha = 1.0; -int Slider::mInstances = 0; - Slider::Slider(double scaleEnd): gcn::Slider(scaleEnd) { @@ -46,154 +38,30 @@ Slider::Slider(double scaleStart, double scaleEnd): init(); } -Slider::~Slider() -{ - mInstances--; - - if (mInstances == 0) - { - delete hStart; - delete hMid; - delete hEnd; - delete hGrip; - delete vStart; - delete vMid; - delete vEnd; - delete vGrip; - delete hStartHi; - delete hMidHi; - delete hEndHi; - delete hGripHi; - delete vStartHi; - delete vMidHi; - delete vEndHi; - delete vGripHi; - } -} - void Slider::init() { - int x, y, w, h,o1,o2; - setFrameSize(0); - - // Load resources - if (mInstances == 0) - { - Image *slider = Theme::getImageFromTheme("slider.png"); - Image *sliderHi = Theme::getImageFromTheme("slider_hilight.png"); - - x = 0; y = 0; - w = 15; h = 6; - o1 = 4; o2 = 11; - hStart = slider->getSubImage(x, y, o1 - x, h); - hMid = slider->getSubImage(o1, y, o2 - o1, h); - hEnd = slider->getSubImage(o2, y, w - o2 + x, h); - hStartHi = sliderHi->getSubImage(x, y, o1 - x, h); - hMidHi = sliderHi->getSubImage(o1, y, o2 - o1, h); - hEndHi = sliderHi->getSubImage(o2, y, w - o2 + x, h); - - x = 6; y = 8; - w = 9; h = 10; - hGrip = slider->getSubImage(x, y, w, h); - hGripHi = sliderHi->getSubImage(x, y, w, h); - - x = 0; y = 6; - w = 6; h = 21; - o1 = 10; o2 = 18; - vStart = slider->getSubImage(x, y, w, o1 - y); - vMid = slider->getSubImage(x, o1, w, o2 - o1); - vEnd = slider->getSubImage(x, o2, w, h - o2 + y); - vStartHi = sliderHi->getSubImage(x, y, w, o1 - y); - vMidHi = sliderHi->getSubImage(x, o1, w, o2 - o1); - vEndHi = sliderHi->getSubImage(x, o2, w, h - o2 + y); - - x = 6; y = 8; - w = 9; h = 10; - vGrip = slider->getSubImage(x, y, w, h); - vGripHi = sliderHi->getSubImage(x, y, w, h); - - slider->decRef(); - sliderHi->decRef(); - } - - mInstances++; - - setMarkerLength(hGrip->getWidth()); -} - -void Slider::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (alpha != mAlpha) - { - mAlpha = alpha; - hStart->setAlpha(mAlpha); - hMid->setAlpha(mAlpha); - hEnd->setAlpha(mAlpha); - hGrip->setAlpha(mAlpha); - hStartHi->setAlpha(mAlpha); - hMidHi->setAlpha(mAlpha); - hEndHi->setAlpha(mAlpha); - hGripHi->setAlpha(mAlpha); - - vStart->setAlpha(mAlpha); - vMid->setAlpha(mAlpha); - vEnd->setAlpha(mAlpha); - vGrip->setAlpha(mAlpha); - vStartHi->setAlpha(mAlpha); - vMidHi->setAlpha(mAlpha); - vEndHi->setAlpha(mAlpha); - vGripHi->setAlpha(mAlpha); - } - + auto theme = gui->getTheme(); + auto &sliderSkin = theme->getSkin(SkinType::Slider); + auto &sliderHandleSkin = theme->getSkin(SkinType::SliderHandle); + setFrameSize(sliderSkin.frameSize); + setMarkerLength(sliderHandleSkin.getMinWidth()); + + setWidth(100); + setHeight(sliderSkin.getMinHeight() + 2 * sliderSkin.padding); } void Slider::draw(gcn::Graphics *graphics) { - int w = getWidth(); - int h = getHeight(); - int x = 0; - int y = mHasMouse?(h - hStartHi->getHeight()) / 2:(h - hStart->getHeight()) / 2; - - updateAlpha(); - - if (!mHasMouse) - { - static_cast<Graphics*>(graphics)->drawImage(hStart, x, y); - - w -= hStart->getWidth() + hEnd->getWidth(); - x += hStart->getWidth(); - - static_cast<Graphics*>(graphics)-> - drawImagePattern(hMid, x, y, w, hMid->getHeight()); + WidgetState state(this); + if (mHasMouse) + state.flags |= STATE_HOVERED; - x += w; - static_cast<Graphics*>(graphics)->drawImage(hEnd, x, y); - } - else - { - static_cast<Graphics*>(graphics)->drawImage(hStartHi, x, y); + auto theme = gui->getTheme(); + theme->drawSkin(static_cast<Graphics*>(graphics), SkinType::Slider, state); - w -= hStartHi->getWidth() + hEndHi->getWidth(); - x += hStartHi->getWidth(); - - static_cast<Graphics*>(graphics)-> - drawImagePattern(hMidHi, x, y, w, hMidHi->getHeight()); - - x += w; - static_cast<Graphics*>(graphics)->drawImage(hEndHi, x, y); - } - - drawMarker(graphics); -} - -void Slider::drawMarker(gcn::Graphics *graphics) -{ - static_cast<Graphics*>(graphics)-> - drawImage(mHasMouse?hGripHi:hGrip, getMarkerPosition(), - (getHeight() - (mHasMouse?hGripHi:hGrip)->getHeight()) / 2); + WidgetState handleState(state); + handleState.x += getMarkerPosition(); + theme->drawSkin(static_cast<Graphics*>(graphics), SkinType::SliderHandle, handleState); } void Slider::mouseEntered(gcn::MouseEvent& event) @@ -205,4 +73,3 @@ void Slider::mouseExited(gcn::MouseEvent& event) { mHasMouse = false; } - diff --git a/src/gui/widgets/slider.h b/src/gui/widgets/slider.h index 3896cb52..52b3b3af 100644 --- a/src/gui/widgets/slider.h +++ b/src/gui/widgets/slider.h @@ -19,13 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SLIDER_H -#define SLIDER_H +#pragma once #include <guichan/widgets/slider.hpp> -class Image; - /** * Slider widget. Same as the Guichan slider but with custom look. * @@ -47,22 +44,16 @@ class Slider : public gcn::Slider */ Slider(double scaleStart, double scaleEnd); - ~Slider() override; - - /** - * Update the alpha value to the graphic components. - */ - static void updateAlpha(); - /** * Draws the slider. */ void draw(gcn::Graphics *graphics) override; - /** - * Draws the marker. - */ - void drawMarker(gcn::Graphics *graphics) override; + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics *graphics) override {} + + // Marker is drawn in Slider::draw + void drawMarker(gcn::Graphics *graphics) override {} /** * Called when the mouse enteres the widget area. @@ -80,13 +71,5 @@ class Slider : public gcn::Slider */ void init(); - static Image *hStart, *hMid, *hEnd, *hGrip; - static Image *vStart, *vMid, *vEnd, *vGrip; - static Image *hStartHi, *hMidHi, *hEndHi, *hGripHi; - static Image *vStartHi, *vMidHi, *vEndHi, *vGripHi; bool mHasMouse = false; - static float mAlpha; - static int mInstances; }; - -#endif diff --git a/src/gui/widgets/spacer.h b/src/gui/widgets/spacer.h index f6a210dc..2fda6e8c 100644 --- a/src/gui/widgets/spacer.h +++ b/src/gui/widgets/spacer.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SPACER_H -#define SPACER_H +#pragma once #include "guichan/graphics.hpp" #include "guichan/widget.hpp" @@ -48,5 +47,3 @@ class Spacer : public gcn::Widget */ void draw(gcn::Graphics *g) override {} }; - -#endif // SPACER_H diff --git a/src/gui/widgets/tab.cpp b/src/gui/widgets/tab.cpp index 64f49eac..b2779c4f 100644 --- a/src/gui/widgets/tab.cpp +++ b/src/gui/widgets/tab.cpp @@ -21,149 +21,90 @@ #include "gui/widgets/tab.h" -#include "configuration.h" #include "graphics.h" +#include "gui/gui.h" +#include "gui/widgets/label.h" #include "gui/widgets/tabbedarea.h" -#include "resources/image.h" #include "resources/theme.h" -#include "utils/dtor.h" - #include <guichan/widgets/label.hpp> -int Tab::mInstances = 0; -float Tab::mAlpha = 1.0; - -enum { - TAB_STANDARD, // 0 - TAB_HIGHLIGHTED, // 1 - TAB_SELECTED, // 2 - TAB_UNUSED, // 3 - TAB_COUNT // 4 - Must be last. -}; - -struct TabData -{ - char const *file; - int gridX[4]; - int gridY[4]; -}; - -static TabData const data[TAB_COUNT] = { - { "tab.png", {0, 9, 16, 25}, {0, 13, 19, 20} }, - { "tab_hilight.png", {0, 9, 16, 25}, {0, 13, 19, 20} }, - { "tabselected.png", {0, 9, 16, 25}, {0, 4, 12, 20} }, - { "tab.png", {0, 9, 16, 25}, {0, 13, 19, 20} } -}; - -ImageRect Tab::tabImg[TAB_COUNT]; - -Tab::Tab() : - mTabColor(&Theme::getThemeColor(Theme::TAB)) -{ - init(); -} - -Tab::~Tab() -{ - mInstances--; - - if (mInstances == 0) - { - for (auto &imgRect : tabImg) - { - std::for_each(imgRect.grid, imgRect.grid + 9, dtor<Image*>()); - } - } -} - -void Tab::init() +Tab::Tab() { setFocusable(false); - setFrameSize(0); - mFlash = false; - if (mInstances == 0) - { - // Load the skin - Image *tab[TAB_COUNT]; - - for (int mode = 0; mode < TAB_COUNT; mode++) - { - tab[mode] = Theme::getImageFromTheme(data[mode].file); - int a = 0; - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - tabImg[mode].grid[a] = tab[mode]->getSubImage( - data[mode].gridX[x], data[mode].gridY[y], - data[mode].gridX[x + 1] - data[mode].gridX[x] + 1, - data[mode].gridY[y + 1] - data[mode].gridY[y] + 1); - a++; - } - } - tabImg[mode].setAlpha(mAlpha); - tab[mode]->decRef(); - } - } - mInstances++; + // Replace the label with customized version + delete mLabel; + mLabel = new Label(); + add(mLabel); + + auto &skin = gui->getTheme()->getSkin(SkinType::Tab); + setFrameSize(skin.frameSize); + mPadding = skin.padding; + mLabel->setPosition(mPadding, mPadding); } -void Tab::updateAlpha() +void Tab::setCaption(const std::string &caption) { - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); + mLabel->setCaption(caption); + mLabel->adjustSize(); - // TODO We don't need to do this for every tab on every draw - // Maybe use a config listener to do it as the value changes. - if (alpha != mAlpha) - { - mAlpha = alpha; + setSize(mLabel->getWidth() + mPadding * 2, + mLabel->getHeight() + mPadding * 2); - for (auto &t : tabImg) - { - t.setAlpha(mAlpha); - } - } + if (mTabbedArea) + static_cast<TabbedArea*>(mTabbedArea)->adjustTabPositions(); } void Tab::draw(gcn::Graphics *graphics) { - int mode = TAB_STANDARD; + if (getFrameSize() == 0) + drawFrame(graphics); - // check which type of tab to draw - if (mTabbedArea) + // if tab is selected, it doesnt need to highlight activity + if (mTabbedArea && mTabbedArea->isTabSelected(this)) + mFlash = false; + + uint8_t flags = 0; + if (mHasMouse) + flags |= STATE_HOVERED; + if (mTabbedArea && mTabbedArea->isTabSelected(this)) + flags |= STATE_SELECTED; + + auto &skin = gui->getTheme()->getSkin(SkinType::Tab); + if (auto state = skin.getState(flags)) { - mLabel->setForegroundColor(*mTabColor); - if (mTabbedArea->isTabSelected(this)) - { - mode = TAB_SELECTED; - // if tab is selected, it doesnt need to highlight activity - mFlash = false; - } - else if (mHasMouse) - { - mode = TAB_HIGHLIGHTED; - } + gcn::Color foregroundColor = state->textFormat.color; if (mFlash) - { - mLabel->setForegroundColor(Theme::getThemeColor(Theme::TAB_FLASH)); - } + foregroundColor = Theme::getThemeColor(Theme::TAB_FLASH); + else if (mTabColor) + foregroundColor = *mTabColor; + + auto label = static_cast<Label*>(mLabel); + label->setForegroundColor(foregroundColor); + label->setOutlineColor(state->textFormat.outlineColor); + label->setShadowColor(state->textFormat.shadowColor); } - updateAlpha(); - - // draw tab - static_cast<Graphics*>(graphics)-> - drawImageRect(0, 0, getWidth(), getHeight(), tabImg[mode]); - // draw label drawChildren(graphics); } +void Tab::drawFrame(gcn::Graphics *graphics) +{ + WidgetState state(this); + state.width += getFrameSize() * 2; + state.height += getFrameSize() * 2; + if (mHasMouse) + state.flags |= STATE_HOVERED; + if (mTabbedArea && mTabbedArea->isTabSelected(this)) + state.flags |= STATE_SELECTED; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::Tab, state); +} + void Tab::setTabColor(const gcn::Color *color) { mTabColor = color; diff --git a/src/gui/widgets/tab.h b/src/gui/widgets/tab.h index 86650257..534abaff 100644 --- a/src/gui/widgets/tab.h +++ b/src/gui/widgets/tab.h @@ -19,12 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TAB_H -#define TAB_H +#pragma once #include <guichan/widgets/tab.hpp> -class ImageRect; class TabbedArea; /** @@ -35,19 +33,25 @@ class Tab : public gcn::Tab { public: Tab(); - ~Tab() override; /** - * Update the alpha value to the graphic components. + * Sets the caption of the tab. Shadowing gcn::Tab::setCaption, which + * shouldn't be used because it calls gcn::Tab::adjustSize, which does + * not take into account the padding. */ - static void updateAlpha(); + void setCaption(const std::string& caption); /** - * Draw the tabbed area. + * Draw the tab. */ void draw(gcn::Graphics *graphics) override; /** + * Draw the tab frame. + */ + void drawFrame(gcn::Graphics *graphics) override; + + /** * Set the normal color fo the tab's text. */ void setTabColor(const gcn::Color *color); @@ -62,15 +66,7 @@ class Tab : public gcn::Tab virtual void setCurrent() {} private: - /** Load images if no other instances exist yet */ - void init(); - - static ImageRect tabImg[4]; /**< Tab state graphics */ - static int mInstances; /**< Number of tab instances */ - static float mAlpha; - - const gcn::Color *mTabColor; - bool mFlash; + const gcn::Color *mTabColor = nullptr; + bool mFlash = false; + int mPadding = 8; }; - -#endif diff --git a/src/gui/widgets/tabbedarea.cpp b/src/gui/widgets/tabbedarea.cpp index af0c11cb..3609791a 100644 --- a/src/gui/widgets/tabbedarea.cpp +++ b/src/gui/widgets/tabbedarea.cpp @@ -21,6 +21,8 @@ #include "gui/widgets/tabbedarea.h" +#include "graphics.h" + #include "gui/widgets/tab.h" #include <guichan/widgets/container.hpp> @@ -30,13 +32,13 @@ TabbedArea::TabbedArea() mWidgetContainer->setOpaque(false); addWidgetListener(this); - mArrowButton[0] = new Button(std::string(), "shift_left", this); - mArrowButton[1] = new Button(std::string(), "shift_right", this); + mArrowButton[0] = std::make_unique<Button>(std::string(), "shift_left", this); + mArrowButton[1] = std::make_unique<Button>(std::string(), "shift_right", this); mArrowButton[0]->setButtonIcon("tab_arrows_left.png"); mArrowButton[1]->setButtonIcon("tab_arrows_right.png"); - add(mArrowButton[0]); - add(mArrowButton[1]); + add(mArrowButton[0].get()); + add(mArrowButton[1].get()); widgetResized(nullptr); } @@ -61,7 +63,12 @@ void TabbedArea::draw(gcn::Graphics *graphics) if (mTabs.empty()) return; + auto g = static_cast<Graphics*>(graphics); + g->pushClipRect(getChildrenArea()); + drawChildren(graphics); + + g->popClipRect(); } gcn::Widget *TabbedArea::getWidget(const std::string &name) const @@ -105,7 +112,7 @@ void TabbedArea::addTab(const std::string &caption, gcn::Widget *widget) addTab(tab, widget); } -void TabbedArea::removeTab(Tab *tab) +void TabbedArea::removeTab(gcn::Tab *tab) { if (tab == mSelectedTab) { @@ -206,9 +213,8 @@ void TabbedArea::updateTabsWidth() { mTabsWidth = 0; for (const auto &[tab, _] : mTabs) - { mTabsWidth += tab->getWidth(); - } + updateVisibleTabsWidth(); } @@ -216,9 +222,7 @@ void TabbedArea::updateVisibleTabsWidth() { mVisibleTabsWidth = 0; for (unsigned int i = mTabScrollIndex; i < mTabs.size(); ++i) - { mVisibleTabsWidth += mTabs[i].first->getWidth(); - } } void TabbedArea::adjustTabPositions() @@ -265,7 +269,7 @@ void TabbedArea::action(const gcn::ActionEvent& actionEvent) { if (actionEvent.getId() == "shift_left") { - if (mTabScrollIndex) + if (mTabScrollIndex > 0) --mTabScrollIndex; } else if (actionEvent.getId() == "shift_right") @@ -282,33 +286,18 @@ void TabbedArea::action(const gcn::ActionEvent& actionEvent) void TabbedArea::updateArrowEnableState() { updateTabsWidth(); - if (mTabsWidth > getWidth() - 2) - { - mArrowButton[0]->setVisible(true); - mArrowButton[1]->setVisible(true); - } - else - { - mArrowButton[0]->setVisible(false); - mArrowButton[1]->setVisible(false); + + const bool arrowButtonsVisible = mTabsWidth > getWidth() - 2; + mArrowButton[0]->setVisible(arrowButtonsVisible); + mArrowButton[1]->setVisible(arrowButtonsVisible); + + if (!arrowButtonsVisible) mTabScrollIndex = 0; - } - // Left arrow consistency check - if (!mTabScrollIndex) - mArrowButton[0]->setEnabled(false); - else - mArrowButton[0]->setEnabled(true); + mArrowButton[0]->setEnabled(mTabScrollIndex > 0); // Right arrow consistency check - if (mVisibleTabsWidth < getWidth() - 2 - - mArrowButton[0]->getWidth() - - mArrowButton[1]->getWidth()) - { - mArrowButton[1]->setEnabled(false); - } - else - { - mArrowButton[1]->setEnabled(true); - } + const int availableWidth = getWidth() - 2 - mArrowButton[0]->getWidth() + - mArrowButton[1]->getWidth(); + mArrowButton[1]->setEnabled(mVisibleTabsWidth >= availableWidth); } diff --git a/src/gui/widgets/tabbedarea.h b/src/gui/widgets/tabbedarea.h index 8e6dcb5f..5d0ccfcc 100644 --- a/src/gui/widgets/tabbedarea.h +++ b/src/gui/widgets/tabbedarea.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TABBEDAREA_H -#define TABBEDAREA_H +#pragma once #include <guichan/widget.hpp> #include <guichan/widgetlistener.hpp> @@ -29,6 +28,7 @@ #include "gui/widgets/button.h" +#include <memory> #include <string> class Tab; @@ -36,7 +36,7 @@ class Tab; /** * A tabbed area, the same as the guichan tabbed area in 0.8, but extended */ -class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener +class TabbedArea final : public gcn::TabbedArea, public gcn::WidgetListener { public: TabbedArea(); @@ -86,12 +86,12 @@ class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener void addTab(const std::string &caption, gcn::Widget *widget) override; /** - * Overload the remove tab function as it's broken in guichan 0.8. + * Override the remove tab function as it's broken in guichan 0.8. */ - void removeTab(Tab *tab); + void removeTab(gcn::Tab *tab) override; /** - * Overload the logic function since it's broken in guichan 0.8. + * Override the logic function since it's broken in guichan 0.8. */ void logic() override; @@ -115,7 +115,7 @@ class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener private: /** The tab arrows */ - Button *mArrowButton[2]; + std::unique_ptr<Button> mArrowButton[2]; /** Check whether the arrow should be clickable */ void updateArrowEnableState(); @@ -151,5 +151,3 @@ class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener */ unsigned mTabScrollIndex = 0; }; - -#endif diff --git a/src/gui/widgets/table.cpp b/src/gui/widgets/table.cpp index 905bb166..909617a0 100644 --- a/src/gui/widgets/table.cpp +++ b/src/gui/widgets/table.cpp @@ -21,8 +21,7 @@ #include "gui/widgets/table.h" -#include "configuration.h" - +#include "gui/gui.h" #include "gui/sdlinput.h" #include "resources/theme.h" @@ -33,13 +32,10 @@ #include <guichan/graphics.hpp> #include <guichan/key.hpp> -float GuiTable::mAlpha = 1.0; - class GuiTableActionListener : public gcn::ActionListener { public: - GuiTableActionListener(GuiTable *_table, gcn::Widget *_widget, int _row, int _column); - + GuiTableActionListener(GuiTable *table, gcn::Widget *widget, int row, int column); ~GuiTableActionListener() override; void action(const gcn::ActionEvent& actionEvent) override; @@ -81,18 +77,12 @@ void GuiTableActionListener::action(const gcn::ActionEvent& actionEvent) } -GuiTable::GuiTable(TableModel *initial_model, gcn::Color background, +GuiTable::GuiTable(TableModel *initialModel, gcn::Color background, bool opacity) : - mLinewiseMode(false), - mWrappingEnabled(false), mOpaque(opacity), - mBackgroundColor(background), - mModel(nullptr), - mSelectedRow(0), - mSelectedColumn(0), - mTopWidget(nullptr) + mBackgroundColor(background) { - setModel(initial_model); + setModel(initialModel); setFocusable(true); addMouseListener(this); @@ -269,16 +259,19 @@ void GuiTable::draw(gcn::Graphics* graphics) if (!mModel) return; - if (config.guiAlpha != mAlpha) - mAlpha = config.guiAlpha; + const auto guiAlpha = gui->getTheme()->getGuiAlpha(); if (mOpaque) { - graphics->setColor(Theme::getThemeColor(Theme::BACKGROUND, - (int)(mAlpha * 255.0f))); + auto backgroundColor = Theme::getThemeColor(Theme::BACKGROUND); + backgroundColor.a = guiAlpha; + graphics->setColor(backgroundColor); graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); } + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + highlightColor.a = guiAlpha; + // First, determine how many rows we need to draw, and where we should start. int first_row = -(getY() / getRowHeight()); @@ -320,8 +313,7 @@ void GuiTable::draw(gcn::Graphics* graphics) widget->setDimension(bounds); - graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, - (int)(mAlpha * 255.0f))); + graphics->setColor(highlightColor); if (mLinewiseMode && r == mSelectedRow && c == 0) { @@ -368,7 +360,7 @@ void GuiTable::moveToBottom(gcn::Widget *widget) mTopWidget = nullptr; } -gcn::Rectangle GuiTable::getChildrenArea() const +gcn::Rectangle GuiTable::getChildrenArea() { return gcn::Rectangle(0, 0, getWidth(), getHeight()); } @@ -487,7 +479,7 @@ void GuiTable::modelUpdated(bool completed) } } -gcn::Widget *GuiTable::getWidgetAt(int x, int y) const +gcn::Widget *GuiTable::getWidgetAt(int x, int y) { int row = getRowForY(y); int column = getColumnForX(x); @@ -497,14 +489,12 @@ gcn::Widget *GuiTable::getWidgetAt(int x, int y) const if (row > -1 && column > -1) { - gcn::Widget *w = mModel->getElementAt(row, column); - if (w && w->isFocusable()) - return w; - else - return nullptr; // Grab the event locally + if (gcn::Widget *w = mModel->getElementAt(row, column)) + if (w->isFocusable()) + return w; } - else - return nullptr; + + return nullptr; // Grab the event locally } int GuiTable::getRowForY(int y) const @@ -516,8 +506,8 @@ int GuiTable::getRowForY(int y) const if (row < 0 || row >= mModel->getRows()) return -1; - else - return row; + + return row; } int GuiTable::getColumnForX(int x) const @@ -534,24 +524,23 @@ int GuiTable::getColumnForX(int x) const if (column < 0 || column >= mModel->getColumns()) return -1; - else - return column; + + return column; } void GuiTable::_setFocusHandler(gcn::FocusHandler* focusHandler) { gcn::Widget::_setFocusHandler(focusHandler); - if (mModel) + if (!mModel) + return; + + for (int r = 0; r < mModel->getRows(); ++r) { - for (int r = 0; r < mModel->getRows(); ++r) + for (int c = 0; c < mModel->getColumns(); ++c) { - for (int c = 0; c < mModel->getColumns(); ++c) - { - gcn::Widget *w = mModel->getElementAt(r, c); - if (w) - w->_setFocusHandler(focusHandler); - } + if (gcn::Widget *w = mModel->getElementAt(r, c)) + w->_setFocusHandler(focusHandler); } } } diff --git a/src/gui/widgets/table.h b/src/gui/widgets/table.h index a9202022..88350928 100644 --- a/src/gui/widgets/table.h +++ b/src/gui/widgets/table.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TABLE_H -#define TABLE_H +#pragma once #include "tablemodel.h" @@ -41,16 +40,16 @@ class GuiTableActionListener; * * \ingroup GUI */ -class GuiTable : public gcn::Widget, - public gcn::MouseListener, - public gcn::KeyListener, - public TableModelListener +class GuiTable final : public gcn::Widget, + public gcn::MouseListener, + public gcn::KeyListener, + public TableModelListener { // so that the action listener can call distributeActionEvent friend class GuiTableActionListener; public: - GuiTable(TableModel * initial_model = nullptr, gcn::Color background = 0xffffff, + GuiTable(TableModel *initialModel = nullptr, gcn::Color background = 0xffffff, bool opacity = true); ~GuiTable() override; @@ -85,7 +84,7 @@ public: void setWrappingEnabled(bool wrappingEnabled) {mWrappingEnabled = wrappingEnabled;} - gcn::Rectangle getChildrenArea() const; + gcn::Rectangle getChildrenArea() override; /** * Toggle whether to use linewise selection mode, in which the table selects @@ -103,7 +102,10 @@ public: // Inherited from Widget void draw(gcn::Graphics* graphics) override; - virtual gcn::Widget *getWidgetAt(int x, int y) const; + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics* graphics) override {} + + gcn::Widget *getWidgetAt(int x, int y) override; void moveToTop(gcn::Widget *child) override; @@ -120,7 +122,7 @@ public: * * @param opaque True if the table should be opaque, false otherwise. */ - virtual void setOpaque(bool opaque) {mOpaque = opaque;} + void setOpaque(bool opaque) {mOpaque = opaque;} /** * Checks if the table is opaque, that is if the table area displays its @@ -128,7 +130,7 @@ public: * * @return True if the table is opaque, false otherwise. */ - virtual bool isOpaque() const {return mOpaque;} + bool isOpaque() const {return mOpaque;} // Inherited from MouseListener void mousePressed(gcn::MouseEvent& mouseEvent) override; @@ -144,42 +146,35 @@ public: protected: /** Frees all action listeners on inner widgets. */ - virtual void uninstallActionListeners(); + void uninstallActionListeners(); /** Installs all action listeners on inner widgets. */ - virtual void installActionListeners(); + void installActionListeners(); - virtual int getRowHeight() const; - virtual int getColumnWidth(int i) const; + int getRowHeight() const; + int getColumnWidth(int i) const; private: int getRowForY(int y) const; // -1 on error int getColumnForX(int x) const; // -1 on error void recomputeDimensions(); - bool mLinewiseMode; - bool mWrappingEnabled; - bool mOpaque; - static float mAlpha; + bool mLinewiseMode = false; + bool mWrappingEnabled = false; + bool mOpaque; /** * Holds the background color of the table. */ gcn::Color mBackgroundColor; - TableModel *mModel; + TableModel *mModel = nullptr; - int mSelectedRow; - int mSelectedColumn; - - /** Number of frames to skip upwards when drawing the selected widget. */ - int mPopFramesNr; + int mSelectedRow = 0; + int mSelectedColumn = 0; /** If someone moves a fresh widget to the top, we must display it. */ - gcn::Widget *mTopWidget; + gcn::Widget *mTopWidget = nullptr; /** Vector for compactness; used as a list in practice. */ std::vector<GuiTableActionListener *> mActionListeners; }; - - -#endif // TABLE_H diff --git a/src/gui/widgets/tablemodel.h b/src/gui/widgets/tablemodel.h index d4274e39..2ba36556 100644 --- a/src/gui/widgets/tablemodel.h +++ b/src/gui/widgets/tablemodel.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TABLE_MODEL_H -#define TABLE_MODEL_H +#pragma once #include <guichanfwd.h> @@ -143,5 +142,3 @@ protected: std::vector<gcn::Widget *> mTableModel; std::vector<int> mWidths; }; - -#endif // TABLE_MODEL_H diff --git a/src/gui/widgets/textbox.cpp b/src/gui/widgets/textbox.cpp index 419fa16e..6cc514fe 100644 --- a/src/gui/widgets/textbox.cpp +++ b/src/gui/widgets/textbox.cpp @@ -21,15 +21,20 @@ #include "gui/widgets/textbox.h" +#include "gui/gui.h" #include "resources/theme.h" +#include "textrenderer.h" #include <guichan/font.hpp> #include <sstream> -TextBox::TextBox() : - mTextColor(&Theme::getThemeColor(Theme::TEXT)) +TextBox::TextBox() { + auto &palette = gui->getTheme()->getPalette(0); + mTextColor = &palette.getColor(Theme::TEXT); + mOutlineColor = palette.getOutlineColor(Theme::TEXT); + setOpaque(false); setFrameSize(0); mMinWidth = getWidth(); @@ -147,3 +152,42 @@ void TextBox::setTextWrapped(const std::string &text, int minDimension) gcn::TextBox::setText(wrappedStream.str()); } + +/** + * Overridden so we can customize the color and outline of the text. + */ +void TextBox::draw(gcn::Graphics *graphics) +{ + unsigned int i; + + if (mOpaque) + { + graphics->setColor(getBackgroundColor()); + graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); + } + + if (isFocused() && isEditable()) + { + drawCaret(graphics, + getFont()->getWidth(mTextRows[mCaretRow].substr(0, mCaretColumn)), + mCaretRow * getFont()->getHeight()); + } + + graphics->setColor(*mTextColor); + graphics->setFont(getFont()); + + for (i = 0; i < mTextRows.size(); i++) + { + // Move the text one pixel so we can have a caret before a letter. + TextRenderer::renderText(graphics, + mTextRows[i], + 1, + i * getFont()->getHeight(), + gcn::Graphics::LEFT, + *mTextColor, + getFont(), + mOutlineColor.has_value(), + false, + mOutlineColor); + } +} diff --git a/src/gui/widgets/textbox.h b/src/gui/widgets/textbox.h index bcf09ee2..49a5a2ad 100644 --- a/src/gui/widgets/textbox.h +++ b/src/gui/widgets/textbox.h @@ -19,11 +19,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TEXTBOX_H -#define TEXTBOX_H +#pragma once #include <guichan/widgets/textbox.hpp> +#include <optional> + /** * A text box, meant to be used inside a scroll area. Same as the Guichan text * box except this one doesn't have a background or border, instead completely @@ -39,6 +40,9 @@ class TextBox : public gcn::TextBox void setTextColor(const gcn::Color *color) { mTextColor = color; } + void setOutlineColor(const std::optional<gcn::Color> &color) + { mOutlineColor = color; } + /** * Sets the text after wrapping it to the current width of the widget. */ @@ -52,15 +56,10 @@ class TextBox : public gcn::TextBox /** * Draws the text. */ - void draw(gcn::Graphics *graphics) override - { - setForegroundColor(*mTextColor); - gcn::TextBox::draw(graphics); - } + void draw(gcn::Graphics *graphics) override; private: int mMinWidth; const gcn::Color *mTextColor; + std::optional<gcn::Color> mOutlineColor; }; - -#endif diff --git a/src/gui/widgets/textfield.cpp b/src/gui/widgets/textfield.cpp index dd0adecd..6c866477 100644 --- a/src/gui/widgets/textfield.cpp +++ b/src/gui/widgets/textfield.cpp @@ -21,107 +21,63 @@ #include "gui/widgets/textfield.h" -#include "configuration.h" #include "graphics.h" +#include "gui/gui.h" #include "gui/sdlinput.h" -#include "resources/image.h" #include "resources/theme.h" #include "utils/copynpaste.h" -#include "utils/dtor.h" #include "utils/stringutils.h" #include <guichan/font.hpp> #include <SDL.h> -#undef DELETE //Win32 compatibility hack - -int TextField::instances = 0; -float TextField::mAlpha = 1.0; -ImageRect TextField::skin; - -TextField::TextField(const std::string &text, bool loseFocusOnTab): - gcn::TextField(text) -{ - setFrameSize(2); - - mLoseFocusOnTab = loseFocusOnTab; - - if (instances == 0) - { - // Load the skin - Image *textbox = Theme::getImageFromTheme("deepbox.png"); - int gridx[4] = {0, 3, 28, 31}; - int gridy[4] = {0, 3, 28, 31}; - int a = 0; - - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - skin.grid[a] = textbox->getSubImage( - gridx[x], gridy[y], - gridx[x + 1] - gridx[x] + 1, - gridy[y + 1] - gridy[y] + 1); - a++; - } - } - skin.setAlpha(config.guiAlpha); - - textbox->decRef(); - } - - instances++; -} - -TextField::~TextField() +TextField::TextField(const std::string &text, bool loseFocusOnTab) + : gcn::TextField(text) + , mLoseFocusOnTab(loseFocusOnTab) { - instances--; - - if (instances == 0) - std::for_each(skin.grid, skin.grid + 9, dtor<Image*>()); -} + auto &skin = gui->getTheme()->getSkin(SkinType::TextField); + setFrameSize(skin.frameSize); + mPadding = skin.padding; -void TextField::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (alpha != mAlpha) - { - mAlpha = alpha; - skin.setAlpha(mAlpha); - } + setWidth(getFont()->getWidth(mText) + 2 * mPadding); + setHeight(getFont()->getHeight() + 2 * mPadding); + fixScroll(); } void TextField::draw(gcn::Graphics *graphics) { - updateAlpha(); + if (getFrameSize() == 0) + drawFrame(graphics); + + auto g = static_cast<Graphics *>(graphics); + g->pushClipRect(gcn::Rectangle(0, 0, getWidth(), getHeight())); if (isFocused()) { drawCaret(graphics, - getFont()->getWidth(mText.substr(0, mCaretPosition)) - - mXScroll); + getFont()->getWidth(mText.substr(0, mCaretPosition)) - mXScroll); } graphics->setColor(Theme::getThemeColor(Theme::TEXT)); graphics->setFont(getFont()); - graphics->drawText(mText, 1 - mXScroll, 1); + graphics->drawText(mText, mPadding - mXScroll, mPadding); + + g->popClipRect(); } void TextField::drawFrame(gcn::Graphics *graphics) { - //updateAlpha(); -> Not useful... + const int bs = getFrameSize(); - int bs = getFrameSize(); - int w = getWidth() + bs * 2; - int h = getHeight() + bs * 2; + WidgetState state(this); + state.width += bs * 2; + state.height += bs * 2; - static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, skin); + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::TextField, state); } void TextField::setNumeric(bool numeric) @@ -156,6 +112,12 @@ int TextField::getValue() const return value; } +void TextField::drawCaret(gcn::Graphics *graphics, int x) +{ + graphics->setColor(Theme::getThemeColor(Theme::CARET)); + graphics->drawLine(mPadding + x, mPadding, mPadding + x, getHeight() - mPadding); +} + void TextField::keyPressed(gcn::KeyEvent &keyEvent) { switch (keyEvent.getKey().getValue()) diff --git a/src/gui/widgets/textfield.h b/src/gui/widgets/textfield.h index 9235f4b8..b84dd723 100644 --- a/src/gui/widgets/textfield.h +++ b/src/gui/widgets/textfield.h @@ -19,16 +19,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TEXTFIELD_H -#define TEXTFIELD_H +#pragma once #include <guichan/widgets/textfield.hpp> #include <vector> class TextInput; -class ImageRect; -class TextField; struct TextHistory { @@ -79,7 +76,6 @@ class TextField : public gcn::TextField */ TextField(const std::string &text = std::string(), bool loseFocusOnTab = true); - ~TextField() override; /** * Draws the text field. @@ -87,11 +83,6 @@ class TextField : public gcn::TextField void draw(gcn::Graphics *graphics) override; /** - * Update the alpha value to the graphic components. - */ - static void updateAlpha(); - - /** * Draws the background and border. */ void drawFrame(gcn::Graphics *graphics) override; @@ -139,42 +130,41 @@ class TextField : public gcn::TextField * Sets the TextField's source of autocomplete. Passing null will * disable autocomplete. */ - void setAutoComplete(AutoCompleteLister *lister) - { mAutoComplete = lister; } + void setAutoComplete(AutoCompleteLister *lister) + { mAutoComplete = lister; } - /** + /** * Returns the TextField's source of autocomplete. */ - AutoCompleteLister *getAutoComplete() const - { return mAutoComplete; } + AutoCompleteLister *getAutoComplete() const + { return mAutoComplete; } - /** + /** * Sets the TextField's source of input history. */ - void setHistory(TextHistory *history) - { mHistory = history; } + void setHistory(TextHistory *history) + { mHistory = history; } - /** + /** * Returns the TextField's source of input history. */ - TextHistory *getHistory() const - { return mHistory; } + TextHistory *getHistory() const + { return mHistory; } + + protected: + void drawCaret(gcn::Graphics *graphics, int x) override; private: void autoComplete(); void handlePaste(); - static int instances; - static float mAlpha; - static ImageRect skin; bool mNumeric = false; - int mMinimum; - int mMaximum; + int mMinimum = 0; + int mMaximum = 0; bool mLoseFocusOnTab; + int mPadding = 1; AutoCompleteLister *mAutoComplete = nullptr; TextHistory *mHistory = nullptr; /**< Text history. */ }; - -#endif diff --git a/src/gui/widgets/textpreview.cpp b/src/gui/widgets/textpreview.cpp index f9e85052..2f80bd23 100644 --- a/src/gui/widgets/textpreview.cpp +++ b/src/gui/widgets/textpreview.cpp @@ -29,49 +29,19 @@ #include <typeinfo> -float TextPreview::mAlpha = 1.0; - -TextPreview::TextPreview(const std::string &text): - mText(text) +TextPreview::TextPreview(const std::string &text) + : mText(text) { mFont = gui->getFont(); mTextColor = &Theme::getThemeColor(Theme::TEXT); - mBGColor = &Theme::getThemeColor(Theme::BACKGROUND); } void TextPreview::draw(gcn::Graphics* graphics) { - if (config.guiAlpha != mAlpha) - mAlpha = config.guiAlpha; - - int alpha = (int) (mAlpha * 255.0f); - - if (!mTextAlpha) - alpha = 255; - - if (mOpaque) - { - graphics->setColor(gcn::Color((int) mBGColor->r, - (int) mBGColor->g, - (int) mBGColor->b, - (int)(mAlpha * 255.0f))); - graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); - } - - if (mTextBGColor && typeid(*mFont) == typeid(TrueTypeFont)) - { - auto *font = static_cast<TrueTypeFont*>(mFont); - int x = font->getWidth(mText) + 1 + 2 * ((mOutline || mShadow) ? 1 :0); - int y = font->getHeight() + 1 + 2 * ((mOutline || mShadow) ? 1 : 0); - graphics->setColor(gcn::Color((int) mTextBGColor->r, - (int) mTextBGColor->g, - (int) mTextBGColor->b, - (int)(mAlpha * 255.0f))); - graphics->fillRectangle(gcn::Rectangle(1, 1, x, y)); - } - TextRenderer::renderText(graphics, mText, 2, 2, gcn::Graphics::LEFT, - gcn::Color(mTextColor->r, mTextColor->g, - mTextColor->b, alpha), + gcn::Color(mTextColor->r, + mTextColor->g, + mTextColor->b, + 255), mFont, mOutline, mShadow); } diff --git a/src/gui/widgets/textpreview.h b/src/gui/widgets/textpreview.h index 7e88248f..8246a200 100644 --- a/src/gui/widgets/textpreview.h +++ b/src/gui/widgets/textpreview.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TEXTPREVIEW_H -#define TEXTPREVIEW_H +#pragma once #include <guichan/color.hpp> #include <guichan/font.hpp> @@ -45,37 +44,6 @@ class TextPreview : public gcn::Widget } /** - * Sets the text to use the set alpha value. - * - * @param alpha whether to use alpha values for the text or not - */ - void useTextAlpha(bool alpha) - { - mTextAlpha = alpha; - } - - /** - * Sets the color the text background is drawn in. This is only the - * rectangle directly behind the text, not to full widget. - * - * @param color the color to set - */ - void setTextBGColor(const gcn::Color *color) - { - mTextBGColor = color; - } - - /** - * Sets the background color of the widget. - * - * @param color the color to set - */ - void setBGColor(const gcn::Color *color) - { - mBGColor = color; - } - - /** * Sets the font to render the text in. * * @param font the font to use. @@ -112,31 +80,10 @@ class TextPreview : public gcn::Widget */ void draw(gcn::Graphics *graphics) override; - /** - * Set opacity for this widget (whether or not to show the background - * color) - * - * @param opaque Whether the widget should be opaque or not - */ - void setOpaque(bool opaque) { mOpaque = opaque; } - - /** - * Gets opacity for this widget (whether or not the background color - * is shown below the widget) - */ - bool isOpaque() const { return mOpaque; } - private: gcn::Font *mFont; std::string mText; const gcn::Color *mTextColor; - const gcn::Color *mBGColor; - const gcn::Color *mTextBGColor = nullptr; - static float mAlpha; - bool mTextAlpha = false; - bool mOpaque = false; bool mShadow = false; bool mOutline = false; }; - -#endif diff --git a/src/gui/widgets/vertcontainer.h b/src/gui/widgets/vertcontainer.h index b66957d3..a684453f 100644 --- a/src/gui/widgets/vertcontainer.h +++ b/src/gui/widgets/vertcontainer.h @@ -18,8 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_VERTCONTAINER_H -#define GUI_VERTCONTAINER_H +#pragma once #include "gui/widgets/container.h" @@ -42,5 +41,3 @@ class VertContainer : public Container, public gcn::WidgetListener int mSpacing; int mCount = 0; }; - -#endif diff --git a/src/gui/widgets/whispertab.cpp b/src/gui/widgets/whispertab.cpp index 636f48dd..28181971 100644 --- a/src/gui/widgets/whispertab.cpp +++ b/src/gui/widgets/whispertab.cpp @@ -36,7 +36,7 @@ WhisperTab::WhisperTab(const std::string &nick) : ChatTab(nick), mNick(nick) { - setTabColor(&Theme::getThemeColor(Theme::WHISPER)); + setTabColor(&Theme::getThemeColor(Theme::WHISPER_TAB)); } WhisperTab::~WhisperTab() diff --git a/src/gui/widgets/whispertab.h b/src/gui/widgets/whispertab.h index 0f01bacc..1a0a4a0f 100644 --- a/src/gui/widgets/whispertab.h +++ b/src/gui/widgets/whispertab.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef WHISPERTAB_H -#define WHISPERTAB_H +#pragma once #include "chattab.h" @@ -63,5 +62,3 @@ class WhisperTab : public ChatTab private: std::string mNick; }; - -#endif // CHANNELTAB_H diff --git a/src/gui/widgets/window.cpp b/src/gui/widgets/window.cpp index 8bf9d081..e57918b7 100644 --- a/src/gui/widgets/window.cpp +++ b/src/gui/widgets/window.cpp @@ -23,6 +23,7 @@ #include "configuration.h" #include "log.h" +#include "textrenderer.h" #include "gui/gui.h" #include "gui/viewport.h" @@ -31,9 +32,6 @@ #include "gui/widgets/resizegrip.h" #include "gui/widgets/windowcontainer.h" -#include "resources/image.h" -#include "resources/theme.h" - #include <guichan/exception.hpp> #include <guichan/focushandler.hpp> @@ -43,13 +41,17 @@ int Window::instances = 0; int Window::mouseResize = 0; -Window::Window(const std::string &caption, bool modal, Window *parent, - const std::string &skin): - gcn::Window(caption), - mParent(parent), - mModal(modal), - mMaxWinWidth(graphics->getWidth()), - mMaxWinHeight(graphics->getHeight()) +Window::Window(const std::string &caption, bool modal, Window *parent) + : Window(SkinType::Window, caption, modal, parent) +{} + +Window::Window(SkinType skinType, const std::string &caption, bool modal, Window *parent) + : gcn::Window(caption) + , mParent(parent) + , mModal(modal) + , mSkinType(skinType) + , mMaxWinWidth(graphics->getWidth()) + , mMaxWinHeight(graphics->getHeight()) { logger->log("Window::Window(\"%s\")", caption.c_str()); @@ -58,19 +60,17 @@ Window::Window(const std::string &caption, bool modal, Window *parent, instances++; - setFrameSize(0); - setPadding(3); - setTitleBarHeight(20); - - // Loads the skin - mSkin = Theme::instance()->load(skin); + auto &skin = getSkin(); + setFrameSize(skin.frameSize); + setPadding(skin.padding); + setTitleBarHeight(skin.titleBarHeight); // Add this window to the window container windowContainer->add(this); if (mModal) { - gui->setCursorType(Cursor::POINTER); + gui->setCursorType(Cursor::Pointer); requestModalFocus(); } @@ -94,8 +94,6 @@ Window::~Window() removeWidgetListener(this); instances--; - - mSkin->instances--; } void Window::setWindowContainer(WindowContainer *wc) @@ -105,44 +103,57 @@ void Window::setWindowContainer(WindowContainer *wc) void Window::draw(gcn::Graphics *graphics) { - auto *g = static_cast<Graphics*>(graphics); + if (getFrameSize() == 0) + drawFrame(graphics); - g->drawImageRect(0, 0, getWidth(), getHeight(), mSkin->getBorder()); - - // Draw title - if (mShowTitle) - { - g->setColor(Theme::getThemeColor(Theme::TEXT)); - g->setFont(getFont()); - g->drawText(getCaption(), 7, 5, gcn::Graphics::LEFT); - } + auto g = static_cast<Graphics*>(graphics); - // Draw Close Button if (mCloseButton) { - g->drawImage(mSkin->getCloseImage(), - getWidth() - mSkin->getCloseImage()->getWidth() - getPadding(), - getPadding()); + WidgetState state(getCloseButtonRect(), mCloseButtonHovered ? STATE_HOVERED : 0); + gui->getTheme()->drawSkin(g, SkinType::ButtonClose, state); } - // Draw Sticky Button if (mStickyButton) { - Image *button = mSkin->getStickyImage(mSticky); - int x = getWidth() - button->getWidth() - getPadding(); - if (mCloseButton) - x -= mSkin->getCloseImage()->getWidth(); - - g->drawImage(button, x, getPadding()); + WidgetState state(getStickyButtonRect(), mSticky ? STATE_SELECTED : 0); + gui->getTheme()->drawSkin(g, SkinType::ButtonSticky, state); } drawChildren(graphics); } +void Window::drawFrame(gcn::Graphics *graphics) +{ + auto g = static_cast<Graphics*>(graphics); + + WidgetState widgetState(this); + widgetState.width += getFrameSize() * 2; + widgetState.height += getFrameSize() * 2; + + auto &skin = getSkin(); + skin.draw(g, widgetState); + + if (mShowTitle) + { + if (auto skinState = skin.getState(widgetState.flags)) + { + auto &textFormat = skinState->textFormat; + TextRenderer::renderText(g, + getCaption(), + getFrameSize() + skin.titleOffsetX, + getFrameSize() + skin.titleOffsetY, + gcn::Graphics::LEFT, + textFormat.bold ? boldFont : getFont(), + textFormat); + } + } +} + void Window::setContentSize(int width, int height) { - width = width + 2 * getPadding(); - height = height + getPadding() + getTitleBarHeight(); + width += 2 * getPadding(); + height += getPadding() + getTitleBarHeight(); if (getMinWidth() > width) width = getMinWidth(); @@ -156,6 +167,16 @@ void Window::setContentSize(int width, int height) setSize(width, height); } +void Window::setMinimumContentSize(int width, int height) +{ + const int padding = getPadding(); + const int titleBarHeight = getTitleBarHeight(); + auto &skin = getSkin(); + + setMinWidth(std::max(skin.getMinWidth(), width + 2 * padding)); + setMinHeight(std::max(skin.getMinHeight(), height + padding + titleBarHeight)); +} + void Window::setLocationRelativeTo(gcn::Widget *widget) { int wx; @@ -218,13 +239,12 @@ void Window::setLocationRelativeTo(ImageRect::ImagePosition position, void Window::setMinWidth(int width) { - mMinWinWidth = width > mSkin->getMinWidth() ? width : mSkin->getMinWidth(); + mMinWinWidth = std::max(getSkin().getMinWidth(), width); } void Window::setMinHeight(int height) { - mMinWinHeight = height > mSkin->getMinHeight() ? - height : mSkin->getMinHeight(); + mMinWinHeight = std::max(getSkin().getMinHeight(), height); } void Window::setMaxWidth(int width) @@ -276,9 +296,7 @@ void Window::widgetResized(const gcn::Event &event) void Window::widgetHidden(const gcn::Event &event) { if (gui) - { - gui->setCursorType(Cursor::POINTER); - } + gui->setCursorType(Cursor::Pointer); WidgetListIterator it; @@ -341,38 +359,13 @@ void Window::mousePressed(gcn::MouseEvent &event) const int x = event.getX(); const int y = event.getY(); - // Handle close button - if (mCloseButton) - { - gcn::Rectangle closeButtonRect( - getWidth() - mSkin->getCloseImage()->getWidth() - getPadding(), - getPadding(), - mSkin->getCloseImage()->getWidth(), - mSkin->getCloseImage()->getHeight()); - - if (closeButtonRect.isPointInRect(x, y)) - { - close(); - } - } + if (mCloseButton && getCloseButtonRect().isPointInRect(x, y)) + close(); - // Handle sticky button - if (mStickyButton) - { - Image *button = mSkin->getStickyImage(mSticky); - int rx = getWidth() - button->getWidth() - getPadding(); - if (mCloseButton) - rx -= mSkin->getCloseImage()->getWidth(); - gcn::Rectangle stickyButtonRect(rx, getPadding(), - button->getWidth(), button->getHeight()); - - if (stickyButtonRect.isPointInRect(x, y)) - { - setSticky(!isSticky()); - } - } + if (mStickyButton && getStickyButtonRect().isPointInRect(x, y)) + setSticky(!isSticky()); - // Handle window resizing + // Update resizing state and disable moving if we're resizing the window mouseResize = getResizeHandles(event); if (mouseResize) mMoved = false; @@ -384,6 +377,11 @@ void Window::close() setVisible(false); } +const Skin &Window::getSkin() const +{ + return gui->getTheme()->getSkin(mSkinType); +} + void Window::mouseReleased(gcn::MouseEvent &event) { mouseResize = 0; @@ -394,11 +392,15 @@ void Window::mouseReleased(gcn::MouseEvent &event) void Window::mouseExited(gcn::MouseEvent &event) { if (mGrip && !mouseResize) - gui->setCursorType(Cursor::POINTER); + gui->setCursorType(Cursor::Pointer); + + mCloseButtonHovered = false; } void Window::mouseMoved(gcn::MouseEvent &event) { + mCloseButtonHovered = false; + // Make sure BeingPopup is hidden (Viewport does not receive mouseExited) if (viewport) viewport->hideBeingPopup(); @@ -408,30 +410,36 @@ void Window::mouseMoved(gcn::MouseEvent &event) if (event.isConsumed()) return; - int resizeHandles = getResizeHandles(event); + mCloseButtonHovered = getCloseButtonRect().isPointInRect(event.getX(), event.getY()); + Cursor cursor = Cursor::Pointer; // Changes the custom mouse cursor based on its current position. - switch (resizeHandles) + if (!mCloseButtonHovered) { + switch (getResizeHandles(event)) + { case BOTTOM | RIGHT: case TOP | LEFT: - gui->setCursorType(Cursor::RESIZE_DOWN_RIGHT); + cursor = Cursor::ResizeDownRight; break; case BOTTOM | LEFT: case TOP | RIGHT: - gui->setCursorType(Cursor::RESIZE_DOWN_LEFT); + cursor = Cursor::ResizeDownLeft; break; case BOTTOM: case TOP: - gui->setCursorType(Cursor::RESIZE_DOWN); + cursor = Cursor::ResizeDown; break; case RIGHT: case LEFT: - gui->setCursorType(Cursor::RESIZE_ACROSS); + cursor = Cursor::ResizeAcross; break; default: - gui->setCursorType(Cursor::POINTER); + break; + } } + + gui->setCursorType(cursor); } void Window::mouseDragged(gcn::MouseEvent &event) @@ -675,6 +683,13 @@ int Window::getResizeHandles(gcn::MouseEvent &event) if (inPadding && event.getSource() == this) { + /** + * The width of the resize border. Is independent of the actual window + * border width, and determines mostly the size of the corner area + * where two borders are moved at the same time. + */ + const int resizeBorderWidth = std::max(mGrip->getWidth(), 10); + resizeHandles |= (x >= getWidth() - resizeBorderWidth) ? RIGHT : (x < resizeBorderWidth) ? LEFT : 0; resizeHandles |= (y >= getHeight() - resizeBorderWidth) ? BOTTOM : @@ -692,10 +707,55 @@ int Window::getResizeHandles(gcn::MouseEvent &event) return resizeHandles; } +gcn::Rectangle Window::getCloseButtonRect() const +{ + if (!mCloseButton) + return {}; + + auto theme = gui->getTheme(); + + auto &closeSkin = theme->getSkin(SkinType::ButtonClose); + const int closeWidth = closeSkin.getMinWidth(); + const int closeHeight = closeSkin.getMinHeight(); + + return { + getWidth() - closeWidth - closeSkin.padding, + closeSkin.padding, + closeWidth, + closeHeight + }; +} + +gcn::Rectangle Window::getStickyButtonRect() const +{ + if (!mStickyButton) + return {}; + + auto theme = gui->getTheme(); + + auto &closeSkin = theme->getSkin(SkinType::ButtonClose); + const int closeWidth = closeSkin.getMinWidth(); + + auto &stickySkin = theme->getSkin(SkinType::ButtonSticky); + const int stickyWidth = stickySkin.getMinWidth(); + const int stickyHeight = stickySkin.getMinHeight(); + + int stickyX = getWidth() - stickyWidth - stickySkin.padding - stickySkin.spacing; + if (mCloseButton) + stickyX -= closeWidth + closeSkin.padding; + + return { + stickyX, + stickySkin.padding, + stickyWidth, + stickyHeight + }; +} + int Window::getGuiAlpha() { float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); + gui->getTheme()->getMinimumOpacity()); return (int) (alpha * 255.0f); } diff --git a/src/gui/widgets/window.h b/src/gui/widgets/window.h index bf6f363c..6331a715 100644 --- a/src/gui/widgets/window.h +++ b/src/gui/widgets/window.h @@ -19,14 +19,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef WINDOW_H -#define WINDOW_H +#pragma once #include "graphics.h" #include "guichanfwd.h" -#include <guichan/widgetlistener.hpp> +#include "resources/theme.h" +#include <guichan/widgetlistener.hpp> #include <guichan/widgets/window.hpp> class ContainerPlacer; @@ -54,10 +54,18 @@ class Window : public gcn::Window, gcn::WidgetListener * @param parent The parent window. This is the window standing above * this one in the window hiearchy. When reordering, * a window will never go below its parent window. - * @param skin The location where the window's skin XML can be found. */ - Window(const std::string &caption = "Window", bool modal = false, - Window *parent = nullptr, const std::string &skin = "window.xml"); + Window(const std::string &caption = "Window", + bool modal = false, + Window *parent = nullptr); + + /** + * Constructor that allows customizing the SkinType used by the window. + */ + Window(SkinType skinType, + const std::string &caption = "Window", + bool modal = false, + Window *parent = nullptr); /** * Destructor. Deletes all the added widgets. @@ -70,16 +78,26 @@ class Window : public gcn::Window, gcn::WidgetListener static void setWindowContainer(WindowContainer *windowContainer); /** - * Draws the window. + * Draws the window contents. */ void draw(gcn::Graphics *graphics) override; /** + * Draws the window frame. + */ + void drawFrame(gcn::Graphics *graphics) override; + + /** * Sets the size of this window. */ void setContentSize(int width, int height); /** + * Sets the minimum size of the window content. + */ + void setMinimumContentSize(int width, int height); + + /** * Sets the location relative to the given widget. */ void setLocationRelativeTo(gcn::Widget *widget); @@ -345,6 +363,11 @@ class Window : public gcn::Window, gcn::WidgetListener virtual void close(); /** + * Returns the Skin used by this window. + */ + const Skin &getSkin() const; + + /** * Gets the alpha value used by the window, in a Guichan usable format. */ static int getGuiAlpha(); @@ -377,6 +400,9 @@ class Window : public gcn::Window, gcn::WidgetListener */ int getResizeHandles(gcn::MouseEvent &event); + gcn::Rectangle getCloseButtonRect() const; + gcn::Rectangle getStickyButtonRect() const; + ResizeGrip *mGrip = nullptr; /**< Resize grip */ Window *mParent; /**< The parent window */ Layout *mLayout = nullptr; /**< Layout handler */ @@ -384,10 +410,12 @@ class Window : public gcn::Window, gcn::WidgetListener bool mShowTitle = true; /**< Window has a title bar */ bool mModal; /**< Window is modal */ bool mCloseButton = false; /**< Window has a close button */ + bool mCloseButtonHovered = false; bool mDefaultVisible = false; /**< Window's default visibility */ bool mSaveVisible = false; /**< Window will save visibility */ bool mStickyButton = false; /**< Window has a sticky button */ bool mSticky = false; /**< Window resists hiding*/ + SkinType mSkinType; /**< The skin type used when drawing the window. */ int mMinWinWidth = 100; /**< Minimum window width */ int mMinWinHeight = 40; /**< Minimum window height */ int mMaxWinWidth; /**< Maximum window width */ @@ -398,15 +426,4 @@ class Window : public gcn::Window, gcn::WidgetListener int mDefaultHeight; /**< Default window height */ static int instances; /**< Number of Window instances */ - - Skin *mSkin; /**< Skin in use by this window */ - - /** - * The width of the resize border. Is independent of the actual window - * border width, and determines mostly the size of the corner area - * where two borders are moved at the same time. - */ - static const int resizeBorderWidth = 10; }; - -#endif diff --git a/src/gui/widgets/windowcontainer.cpp b/src/gui/widgets/windowcontainer.cpp index 36f0998f..4e350a9e 100644 --- a/src/gui/widgets/windowcontainer.cpp +++ b/src/gui/widgets/windowcontainer.cpp @@ -24,16 +24,15 @@ #include "gui/gui.h" #include "gui/widgets/window.h" -#include "utils/dtor.h" - #include <guichan/focushandler.hpp> WindowContainer *windowContainer = nullptr; void WindowContainer::logic() { - delete_all(mDeathList); - mDeathList.clear(); + for (auto widget : mScheduledDeletions) + delete widget; + mScheduledDeletions.clear(); gcn::Container::logic(); } @@ -48,7 +47,7 @@ void WindowContainer::draw(gcn::Graphics *graphics) void WindowContainer::scheduleDelete(gcn::Widget *widget) { - mDeathList.push_back(widget); + mScheduledDeletions.insert(widget); } void WindowContainer::adjustAfterResize(int oldScreenWidth, diff --git a/src/gui/widgets/windowcontainer.h b/src/gui/widgets/windowcontainer.h index 861839c9..ff03a903 100644 --- a/src/gui/widgets/windowcontainer.h +++ b/src/gui/widgets/windowcontainer.h @@ -19,11 +19,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef WINDOWCONTAINER_H -#define WINDOWCONTAINER_H +#pragma once #include "gui/widgets/container.h" +#include <set> + /** * A window container. This container adds functionality for more convenient * widget (windows in particular) destruction. @@ -65,11 +66,9 @@ class WindowContainer : public Container bool widgetIsVisible(gcn::Widget *widget); /** - * List of widgets that are scheduled to be deleted. + * Set of widgets that are scheduled to be deleted. */ - std::list<gcn::Widget *> mDeathList; + std::set<gcn::Widget *> mScheduledDeletions; }; extern WindowContainer *windowContainer; - -#endif diff --git a/src/gui/windowmenu.cpp b/src/gui/windowmenu.cpp index c6e52ee7..0b2d126f 100644 --- a/src/gui/windowmenu.cpp +++ b/src/gui/windowmenu.cpp @@ -25,7 +25,7 @@ #include "gui/emotepopup.h" #include "gui/skilldialog.h" -#include "gui/specialswindow.h" +#include "gui/abilitieswindow.h" #include "gui/widgets/button.h" #include "gui/widgets/window.h" @@ -61,8 +61,8 @@ WindowMenu::WindowMenu() addButton(N_("Skills"), x, h, "button-icon-skills.png", KeyboardConfig::KEY_WINDOW_SKILL); - if (specialsWindow->hasSpecials()) - addButton(N_("Specials"), x, h, "button-icon-specials.png"); + if (abilitiesWindow->hasAbilities()) + addButton(N_("Abilities"), x, h, "button-icon-abilities.png"); addButton(N_("Social"), x, h, "button-icon-social.png", KeyboardConfig::KEY_WINDOW_SOCIAL); @@ -122,9 +122,9 @@ void WindowMenu::action(const gcn::ActionEvent &event) { window = skillDialog; } - else if (event.getId() == "Specials") + else if (event.getId() == "Abilities") { - window = specialsWindow; + window = abilitiesWindow; } else if (event.getId() == "Social") { diff --git a/src/gui/windowmenu.h b/src/gui/windowmenu.h index 0eabc247..b80b62b0 100644 --- a/src/gui/windowmenu.h +++ b/src/gui/windowmenu.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef WINDOWMENU_H -#define WINDOWMENU_H +#pragma once #include "keyboardconfig.h" @@ -62,5 +61,3 @@ class WindowMenu : public Container, EmotePopup *mEmotePopup = nullptr; }; - -#endif diff --git a/src/gui/worldselectdialog.h b/src/gui/worldselectdialog.h index a1150cb8..702a5616 100644 --- a/src/gui/worldselectdialog.h +++ b/src/gui/worldselectdialog.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef WORLD_SELECT_DIALOG_H -#define WORLD_SELECT_DIALOG_H +#pragma once #include "gui/widgets/window.h" @@ -62,5 +61,3 @@ class WorldSelectDialog : public Window, public gcn::ActionListener, gcn::Button *mChangeLoginButton; gcn::Button *mChooseWorld; }; - -#endif // WORLD_SELECT_DIALOG_H |