summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/abilitieswindow.cpp6
-rw-r--r--src/gui/buydialog.cpp2
-rw-r--r--src/gui/buyselldialog.cpp11
-rw-r--r--src/gui/changeemaildialog.cpp4
-rw-r--r--src/gui/changepassworddialog.cpp4
-rw-r--r--src/gui/charcreatedialog.cpp4
-rw-r--r--src/gui/charselectdialog.cpp2
-rw-r--r--src/gui/chatwindow.cpp14
-rw-r--r--src/gui/confirmdialog.cpp50
-rw-r--r--src/gui/confirmdialog.h5
-rw-r--r--src/gui/connectiondialog.cpp2
-rw-r--r--src/gui/customserverdialog.cpp2
-rw-r--r--src/gui/emotepopup.cpp50
-rw-r--r--src/gui/emotepopup.h5
-rw-r--r--src/gui/equipmentwindow.cpp108
-rw-r--r--src/gui/equipmentwindow.h20
-rw-r--r--src/gui/gui.cpp56
-rw-r--r--src/gui/gui.h12
-rw-r--r--src/gui/helpwindow.cpp4
-rw-r--r--src/gui/inventorywindow.cpp2
-rw-r--r--src/gui/itemamountwindow.cpp7
-rw-r--r--src/gui/itempopup.cpp44
-rw-r--r--src/gui/itempopup.h3
-rw-r--r--src/gui/minimap.cpp2
-rw-r--r--src/gui/npcdialog.cpp40
-rw-r--r--src/gui/okdialog.cpp37
-rw-r--r--src/gui/okdialog.h5
-rw-r--r--src/gui/palette.cpp51
-rw-r--r--src/gui/palette.h65
-rw-r--r--src/gui/popupmenu.cpp2
-rw-r--r--src/gui/questswindow.cpp290
-rw-r--r--src/gui/questswindow.h71
-rw-r--r--src/gui/recorder.cpp2
-rw-r--r--src/gui/register.cpp2
-rw-r--r--src/gui/selldialog.cpp2
-rw-r--r--src/gui/serverdialog.cpp42
-rw-r--r--src/gui/setup_audio.cpp2
-rw-r--r--src/gui/setup_colors.cpp37
-rw-r--r--src/gui/setup_interface.cpp92
-rw-r--r--src/gui/setup_interface.h11
-rw-r--r--src/gui/setup_video.cpp2
-rw-r--r--src/gui/shortcutwindow.cpp10
-rw-r--r--src/gui/skilldialog.cpp14
-rw-r--r--src/gui/socialwindow.cpp2
-rw-r--r--src/gui/tradewindow.cpp26
-rw-r--r--src/gui/tradewindow.h5
-rw-r--r--src/gui/truetypefont.cpp151
-rw-r--r--src/gui/truetypefont.h25
-rw-r--r--src/gui/unregisterdialog.cpp4
-rw-r--r--src/gui/updaterwindow.cpp20
-rw-r--r--src/gui/viewport.cpp2
-rw-r--r--src/gui/widgets/avatarlistbox.cpp64
-rw-r--r--src/gui/widgets/avatarlistbox.h8
-rw-r--r--src/gui/widgets/browserbox.cpp158
-rw-r--r--src/gui/widgets/browserbox.h27
-rw-r--r--src/gui/widgets/button.cpp20
-rw-r--r--src/gui/widgets/chattab.cpp2
-rw-r--r--src/gui/widgets/chattab.h1
-rw-r--r--src/gui/widgets/checkbox.cpp18
-rw-r--r--src/gui/widgets/container.h2
-rw-r--r--src/gui/widgets/desktop.cpp18
-rw-r--r--src/gui/widgets/dropdown.cpp51
-rw-r--r--src/gui/widgets/dropdown.h4
-rw-r--r--src/gui/widgets/itemcontainer.cpp98
-rw-r--r--src/gui/widgets/itemcontainer.h3
-rw-r--r--src/gui/widgets/itemlinkhandler.cpp27
-rw-r--r--src/gui/widgets/itemlinkhandler.h6
-rw-r--r--src/gui/widgets/itemshortcutcontainer.cpp53
-rw-r--r--src/gui/widgets/itemshortcutcontainer.h2
-rw-r--r--src/gui/widgets/label.cpp26
-rw-r--r--src/gui/widgets/listbox.cpp14
-rw-r--r--src/gui/widgets/popup.cpp55
-rw-r--r--src/gui/widgets/popup.h11
-rw-r--r--src/gui/widgets/progressbar.cpp7
-rw-r--r--src/gui/widgets/radiobutton.cpp20
-rw-r--r--src/gui/widgets/resizegrip.cpp2
-rw-r--r--src/gui/widgets/scrollarea.cpp451
-rw-r--r--src/gui/widgets/scrollarea.h47
-rw-r--r--src/gui/widgets/shoplistbox.cpp24
-rw-r--r--src/gui/widgets/shortcutcontainer.cpp4
-rw-r--r--src/gui/widgets/tab.cpp15
-rw-r--r--src/gui/widgets/tabbedarea.cpp2
-rw-r--r--src/gui/widgets/tabbedarea.h6
-rw-r--r--src/gui/widgets/table.cpp25
-rw-r--r--src/gui/widgets/table.h34
-rw-r--r--src/gui/widgets/textbox.cpp50
-rw-r--r--src/gui/widgets/textbox.h12
-rw-r--r--src/gui/widgets/textfield.cpp2
-rw-r--r--src/gui/widgets/textpreview.cpp55
-rw-r--r--src/gui/widgets/textpreview.h50
-rw-r--r--src/gui/widgets/whispertab.cpp2
-rw-r--r--src/gui/widgets/window.cpp145
-rw-r--r--src/gui/widgets/window.h41
-rw-r--r--src/gui/windowmenu.cpp49
94 files changed, 2026 insertions, 1116 deletions
diff --git a/src/gui/abilitieswindow.cpp b/src/gui/abilitieswindow.cpp
index 700fa7ff..d8122bf3 100644
--- a/src/gui/abilitieswindow.cpp
+++ b/src/gui/abilitieswindow.cpp
@@ -145,13 +145,13 @@ void AbilitiesWindow::draw(gcn::Graphics *graphics)
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 &[id, ability] : abilityData)
{
- logger->log("Updating ability GUI for %d", id);
+ Log::info("Updating ability GUI for %d", id);
AbilityInfo *info = AbilityDB::get(id);
if (info)
@@ -166,7 +166,7 @@ void AbilitiesWindow::rebuild(const std::map<int, Ability> &abilityData)
}
else
{
- logger->log("Warning: No info available of ability %d", id);
+ Log::warn("No info available of ability %d", id);
}
}
}
diff --git a/src/gui/buydialog.cpp b/src/gui/buydialog.cpp
index 135c2119..fb316722 100644
--- a/src/gui/buydialog.cpp
+++ b/src/gui/buydialog.cpp
@@ -54,7 +54,7 @@ BuyDialog::BuyDialog(int npcId):
setCloseButton(true);
setMinWidth(260);
setMinHeight(230);
- setDefaultSize(260, 230, ImageRect::CENTER);
+ setDefaultSize(260, 230, WindowAlignment::Center);
mShopItems = new ShopItems;
diff --git a/src/gui/buyselldialog.cpp b/src/gui/buyselldialog.cpp
index be49ba58..c555cf01 100644
--- a/src/gui/buyselldialog.cpp
+++ b/src/gui/buyselldialog.cpp
@@ -23,8 +23,6 @@
#include "playerinfo.h"
-#include "gui/setup.h"
-
#include "gui/widgets/button.h"
#include "net/net.h"
@@ -109,11 +107,6 @@ void BuySellDialog::action(const gcn::ActionEvent &event)
void BuySellDialog::closeAll()
{
- auto it = instances.begin();
- auto it_end = instances.end();
-
- for (; it != it_end; it++)
- {
- (*it)->close();
- }
+ for (auto &instance : instances)
+ instance->close();
}
diff --git a/src/gui/changeemaildialog.cpp b/src/gui/changeemaildialog.cpp
index ce83087b..e10a0d18 100644
--- a/src/gui/changeemaildialog.cpp
+++ b/src/gui/changeemaildialog.cpp
@@ -112,8 +112,8 @@ void ChangeEmailDialog::action(const gcn::ActionEvent &event)
const std::string username = mLoginData->username.c_str();
const std::string newFirstEmail = mFirstEmailField->getText();
const std::string newSecondEmail = mSecondEmailField->getText();
- logger->log("ChangeEmailDialog::Email change, Username is %s",
- username.c_str());
+ Log::info("ChangeEmailDialog::Email change, Username is %s",
+ username.c_str());
std::stringstream errorMessage;
int error = 0;
diff --git a/src/gui/changepassworddialog.cpp b/src/gui/changepassworddialog.cpp
index 437a8c90..91d7721d 100644
--- a/src/gui/changepassworddialog.cpp
+++ b/src/gui/changepassworddialog.cpp
@@ -94,8 +94,8 @@ void ChangePasswordDialog::action(const gcn::ActionEvent &event)
const std::string oldPassword = mOldPassField->getText();
const std::string newFirstPass = mFirstPassField->getText();
const std::string newSecondPass = mSecondPassField->getText();
- logger->log("ChangePasswordDialog::Password change, Username is %s",
- username.c_str());
+ Log::info("ChangePasswordDialog::Password change, Username is %s",
+ username.c_str());
std::stringstream errorMessage;
int error = 0;
diff --git a/src/gui/charcreatedialog.cpp b/src/gui/charcreatedialog.cpp
index cff7d822..6b46d2ac 100644
--- a/src/gui/charcreatedialog.cpp
+++ b/src/gui/charcreatedialog.cpp
@@ -191,8 +191,10 @@ void CharCreateDialog::action(const gcn::ActionEvent &event)
0 : mHairStylesIds.at(mHairStyleId);
int hairColor = mHairColorsIds.empty() ?
0 : mHairColorsIds.at(mHairColorId);
+
+ Gender gender = mFemale->isSelected() ? Gender::Female : Gender::Male;
Net::getCharHandler()->newCharacter(getName(), characterSlot,
- mFemale->isSelected(),
+ gender,
hairStyle, hairColor,
mAttributes);
}
diff --git a/src/gui/charselectdialog.cpp b/src/gui/charselectdialog.cpp
index 2485be69..1ed353dd 100644
--- a/src/gui/charselectdialog.cpp
+++ b/src/gui/charselectdialog.cpp
@@ -271,7 +271,7 @@ void CharSelectDialog::setCharacters(const Net::Characters &characters)
if (characterSlot >= (int)mCharacterEntries.size())
{
- logger->log("Warning: slot out of range: %d", character->slot);
+ Log::warn("Slot out of range: %d", character->slot);
continue;
}
diff --git a/src/gui/chatwindow.cpp b/src/gui/chatwindow.cpp
index 8a34c961..d19231ab 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"
@@ -82,7 +83,7 @@ class ChatAutoComplete : public AutoCompleteLister
};
ChatWindow::ChatWindow():
- Window(_("Chat")),
+ Window(SkinType::Popup, _("Chat")),
mItemLinkHandler(new ItemLinkHandler(this)),
mChatInput(new ChatInput),
mAutoComplete(new ChatAutoComplete),
@@ -101,16 +102,20 @@ ChatWindow::ChatWindow():
setResizable(true);
setDefaultVisible(true);
setSaveVisible(true);
- setDefaultSize(600, 123, ImageRect::LOWER_LEFT);
+ setDefaultSize(600, 123, WindowAlignment::BottomLeft);
setMinWidth(150);
setMinHeight(90);
mChatInput->setActionEventId("chatinput");
mChatInput->addActionListener(this);
- getLayout().setPadding(3);
+ // 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);
+
place(0, 0, mChatTabs, 3, 3);
- place(0, 3, mChatInput, 3);
+ place(0, 3, mChatInput, 3).setPadding(mChatInput->getFrameSize());
loadWindowState();
@@ -235,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/confirmdialog.cpp b/src/gui/confirmdialog.cpp
index c0b471c1..360b399b 100644
--- a/src/gui/confirmdialog.cpp
+++ b/src/gui/confirmdialog.cpp
@@ -22,49 +22,34 @@
#include "gui/confirmdialog.h"
#include "gui/widgets/button.h"
+#include "gui/widgets/layout.h"
#include "gui/widgets/textbox.h"
#include "utils/gettext.h"
#include <guichan/font.hpp>
-ConfirmDialog::ConfirmDialog(const std::string &title, const std::string &msg,
- Window *parent):
+ConfirmDialog::ConfirmDialog(const std::string &title,
+ const std::string &msg,
+ Window *parent):
Window(title, true, parent)
{
- mTextBox = new TextBox;
- mTextBox->setEditable(false);
- mTextBox->setOpaque(false);
- mTextBox->setTextWrapped(msg, 260);
+ auto textBox = new TextBox;
+ textBox->setEditable(false);
+ textBox->setOpaque(false);
+ textBox->setTextWrapped(msg, 260);
gcn::Button *yesButton = new Button(_("Yes"), "yes", this);
gcn::Button *noButton = new Button(_("No"), "no", this);
- const int numRows = mTextBox->getNumberOfRows();
- const int inWidth = yesButton->getWidth() + noButton->getWidth() +
- (2 * getPadding());
- const int fontHeight = getFont()->getHeight();
- const int height = numRows * fontHeight;
- int width = getFont()->getWidth(title);
+ auto place = getPlacer(0, 0);
+ place(0, 0, textBox);
- if (width < mTextBox->getMinWidth())
- width = mTextBox->getMinWidth();
- if (width < inWidth)
- width = inWidth;
-
- setContentSize(mTextBox->getMinWidth() + fontHeight, height + fontHeight +
- noButton->getHeight());
- mTextBox->setPosition(getPadding(), getPadding());
-
- // 8 is the padding that GUIChan adds to button widgets
- // (top and bottom combined)
- yesButton->setPosition((width - inWidth) / 2, height + 8);
- noButton->setPosition(yesButton->getX() + inWidth - noButton->getWidth(),
- height + 8);
-
- add(mTextBox);
- add(yesButton);
- add(noButton);
+ place = getPlacer(0, 1);
+ place(0, 0, yesButton);
+ place(1, 0, noButton);
+ place.getCell().setHAlign(Layout::CENTER);
+ reflowLayout();
if (getParent())
{
@@ -79,8 +64,5 @@ void ConfirmDialog::action(const gcn::ActionEvent &event)
{
setActionEventId(event.getId());
distributeActionEvent();
-
- // Can we receive anything else anyway?
- if (event.getId() == "yes" || event.getId() == "no")
- scheduleDelete();
+ scheduleDelete();
}
diff --git a/src/gui/confirmdialog.h b/src/gui/confirmdialog.h
index ba51558e..6098b3f7 100644
--- a/src/gui/confirmdialog.h
+++ b/src/gui/confirmdialog.h
@@ -25,8 +25,6 @@
#include <guichan/actionlistener.hpp>
-class TextBox;
-
/**
* An option dialog.
*
@@ -42,7 +40,4 @@ class ConfirmDialog : public Window, public gcn::ActionListener
* Called when receiving actions from the widgets.
*/
void action(const gcn::ActionEvent &event) override;
-
- private:
- TextBox *mTextBox;
};
diff --git a/src/gui/connectiondialog.cpp b/src/gui/connectiondialog.cpp
index 2ff68a15..e6435269 100644
--- a/src/gui/connectiondialog.cpp
+++ b/src/gui/connectiondialog.cpp
@@ -54,7 +54,7 @@ ConnectionDialog::ConnectionDialog(const std::string &text,
void ConnectionDialog::action(const gcn::ActionEvent &)
{
- logger->log("Cancel pressed");
+ Log::info("Cancel pressed");
Client::setState(mCancelState);
}
diff --git a/src/gui/customserverdialog.cpp b/src/gui/customserverdialog.cpp
index 5524e459..9a589671 100644
--- a/src/gui/customserverdialog.cpp
+++ b/src/gui/customserverdialog.cpp
@@ -154,7 +154,7 @@ void CustomServerDialog::action(const gcn::ActionEvent &event)
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/emotepopup.cpp b/src/gui/emotepopup.cpp
index e759ab25..4df6b995 100644
--- a/src/gui/emotepopup.cpp
+++ b/src/gui/emotepopup.cpp
@@ -22,11 +22,10 @@
#include "gui/emotepopup.h"
-#include "configuration.h"
#include "emoteshortcut.h"
#include "graphics.h"
-#include "log.h"
+#include "gui/gui.h"
#include "resources/emotedb.h"
#include "resources/image.h"
#include "resources/theme.h"
@@ -34,19 +33,10 @@
#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);
@@ -62,30 +52,41 @@ void EmotePopup::draw(gcn::Graphics *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)
- g->drawImage(mSelectionImage, emoteX, emoteY + 4);
+ slotState.flags |= STATE_HOVERED;
+
+ slotSkin.draw(g, slotState);
// Draw emote icon
if (auto image = EmoteDB::getByIndex(i).image)
{
image->setAlpha(1.0f);
- g->drawImage(image, emoteX, emoteY);
+ g->drawImage(image,
+ slotState.x + (slotSkin.width - image->getWidth()) / 2,
+ slotState.y + (slotSkin.height - image->getHeight()) / 2);
}
}
}
@@ -139,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())
@@ -178,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()
@@ -186,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 c95c5723..664eef27 100644
--- a/src/gui/emotepopup.h
+++ b/src/gui/emotepopup.h
@@ -23,7 +23,6 @@
#pragma once
#include "gui/widgets/popup.h"
-#include "resources/resource.h"
#include <guichan/mouselistener.hpp>
@@ -104,7 +103,6 @@ class EmotePopup : public Popup
*/
void distributeValueChangedEvent();
- ResourceRef<Image> mSelectionImage;
int mSelectedEmoteId = -1;
int mHoveredEmoteIndex = -1;
@@ -112,7 +110,4 @@ class EmotePopup : public Popup
int mColumnCount = 1;
std::list<gcn::SelectionListener *> mListeners;
-
- static const int gridWidth;
- static const int gridHeight;
};
diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp
index ff3c6630..8523e7b2 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)
@@ -66,7 +63,7 @@ EquipmentWindow::EquipmentWindow(Equipment *equipment):
setCloseButton(true);
setSaveVisible(true);
setContentSize(175, 290);
- setDefaultSize(getWidth(), getHeight(), ImageRect::CENTER);
+ setDefaultSize(getWidth(), getHeight(), WindowAlignment::Center);
loadWindowState();
mUnequip = new Button(_("Unequip"), "unequip", this);
@@ -77,28 +74,6 @@ EquipmentWindow::EquipmentWindow(Equipment *equipment):
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::getImageFromTheme(backgroundFile);
- }
}
EquipmentWindow::~EquipmentWindow()
@@ -110,58 +85,51 @@ void EquipmentWindow::draw(gcn::Graphics *graphics)
{
Window::draw(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);
+ }
}
}
@@ -180,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;
}
diff --git a/src/gui/equipmentwindow.h b/src/gui/equipmentwindow.h
index f46d1175..944ae99e 100644
--- a/src/gui/equipmentwindow.h
+++ b/src/gui/equipmentwindow.h
@@ -22,14 +22,11 @@
#pragma once
#include "equipment.h"
-#include "resources/image.h"
#include "gui/widgets/window.h"
#include <guichan/actionlistener.hpp>
-#include <vector>
-
class Inventory;
class Item;
class ItemPopup;
@@ -58,29 +55,12 @@ class EquipmentWindow : public Window, public gcn::ActionListener
void mouseExited(gcn::MouseEvent &event) override;
/**
- * Loads the correct amount of displayed equip boxes.
- */
- void loadEquipBoxes();
-
- /**
* Returns the current selected slot or -1 if none.
*/
int getSelected() const
{ return mSelected; }
protected:
- /**
- * Equipment box.
- */
- struct EquipBox
- {
- int posX = 0;
- int posY = 0;
- ResourceRef<Image> backgroundImage;
- };
-
- std::vector<EquipBox> mBoxes; /**< Equipment boxes. */
-
int mSelected = -1; /**< Index of selected item. */
Equipment *mEquipment;
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index d72bd56d..768aeb2b 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -42,6 +42,8 @@
#include <guichan/exception.hpp>
#include <guichan/image.hpp>
+#include <algorithm>
+
#include <SDL_image.h>
// Guichan stuff
@@ -57,10 +59,19 @@ gcn::Font *monoFont = nullptr;
bool Gui::debugDraw;
Gui::Gui(Graphics *graphics, const std::string &themePath)
- : mTheme(new Theme(themePath))
+ : mAvailableThemes(Theme::getAvailableThemes())
, mCustomCursorScale(Client::getVideo().settings().scale())
{
- logger->log("Initializing GUI...");
+ // 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());
+
+ Log::info("Initializing GUI...");
// Set graphics
setGraphics(graphics);
@@ -68,7 +79,7 @@ Gui::Gui(Graphics *graphics, const std::string &themePath)
guiInput = new SDLInput;
setInput(guiInput);
- // Set focus handler
+ // Replace focus handler
delete mFocusHandler;
mFocusHandler = new FocusHandler;
@@ -95,8 +106,8 @@ Gui::Gui(Graphics *graphics, const std::string &themePath)
}
catch (gcn::Exception e)
{
- logger->error(std::string("Unable to load '") + fontFile +
- std::string("': ") + e.getMessage());
+ Log::critical(std::string("Unable to load '") + fontFile +
+ "': " + e.getMessage());
}
// Set bold font
@@ -108,8 +119,8 @@ Gui::Gui(Graphics *graphics, const std::string &themePath)
}
catch (gcn::Exception e)
{
- logger->error(std::string("Unable to load '") + fontFile +
- std::string("': ") + e.getMessage());
+ Log::critical(std::string("Unable to load '") + fontFile +
+ "': " + e.getMessage());
}
// Set mono font
@@ -121,8 +132,8 @@ Gui::Gui(Graphics *graphics, const std::string &themePath)
}
catch (gcn::Exception e)
{
- logger->error(std::string("Unable to load '") + fontFile +
- std::string("': ") + e.getMessage());
+ Log::critical(std::string("Unable to load '") + fontFile +
+ "': " + e.getMessage());
}
loadCustomCursors();
@@ -226,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())
@@ -272,32 +288,20 @@ void Gui::loadCustomCursors()
SDL_Surface *mouseSurface = loadSurface(cursorPath);
if (!mouseSurface)
{
- logger->log("Warning: Unable to load mouse cursor file (%s): %s",
- cursorPath.c_str(), SDL_GetError());
+ Log::warn("Unable to load mouse cursor file (%s): %s",
+ cursorPath.c_str(), SDL_GetError());
return;
}
SDL_SetSurfaceBlendMode(mouseSurface, SDL_BLENDMODE_NONE);
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- const Uint32 rmask = 0xff000000;
- const Uint32 gmask = 0x00ff0000;
- const Uint32 bmask = 0x0000ff00;
- const Uint32 amask = 0x000000ff;
-#else
- const Uint32 rmask = 0x000000ff;
- const Uint32 gmask = 0x0000ff00;
- const Uint32 bmask = 0x00ff0000;
- const Uint32 amask = 0xff000000;
-#endif
-
constexpr int cursorSize = 40;
const int targetCursorSize = cursorSize * mCustomCursorScale;
const int columns = mouseSurface->w / cursorSize;
- SDL_Surface *cursorSurface = SDL_CreateRGBSurface(
+ SDL_Surface *cursorSurface = SDL_CreateRGBSurfaceWithFormat(
0, targetCursorSize, targetCursorSize, 32,
- rmask, gmask, bmask, amask);
+ SDL_PIXELFORMAT_RGBA32);
for (int i = 0; i < static_cast<int>(Cursor::Count); ++i)
{
@@ -313,7 +317,7 @@ void Gui::loadCustomCursors()
17 * mCustomCursorScale);
if (!cursor)
{
- logger->log("Warning: Unable to create cursor: %s", SDL_GetError());
+ Log::warn("Unable to create cursor: %s", SDL_GetError());
}
mCustomMouseCursors.push_back(cursor);
diff --git a/src/gui/gui.h b/src/gui/gui.h
index 450514f5..2584b780 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -24,6 +24,8 @@
#include "eventlistener.h"
#include "guichanfwd.h"
+#include "resources/theme.h"
+
#include "utils/time.h"
#include <guichan/gui.hpp>
@@ -33,7 +35,6 @@
#include <memory>
#include <vector>
-class Theme;
class TextInput;
class Graphics;
class SDLInput;
@@ -123,6 +124,14 @@ 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.
*/
@@ -141,6 +150,7 @@ 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*/
diff --git a/src/gui/helpwindow.cpp b/src/gui/helpwindow.cpp
index e0e21610..7c7c5d4c 100644
--- a/src/gui/helpwindow.cpp
+++ b/src/gui/helpwindow.cpp
@@ -45,7 +45,7 @@ HelpWindow::HelpWindow():
setResizable(true);
setupWindow->registerWindowForReset(this);
- setDefaultSize(500, 400, ImageRect::CENTER);
+ setDefaultSize(500, 400, WindowAlignment::Center);
mBrowserBox = new BrowserBox;
mScrollArea = new ScrollArea(mBrowserBox);
@@ -98,7 +98,7 @@ void HelpWindow::loadFile(const std::string &file)
char *fileContents = (char *) FS::loadFile(fileName, contentsLength);
if (!fileContents)
{
- logger->log("Couldn't load text file: %s", fileName.c_str());
+ Log::info("Couldn't load text file: %s", fileName.c_str());
return;
}
diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp
index ab2e9c86..0125700c 100644
--- a/src/gui/inventorywindow.cpp
+++ b/src/gui/inventorywindow.cpp
@@ -65,7 +65,7 @@ InventoryWindow::InventoryWindow(Inventory *inventory):
setCloseButton(true);
setSaveVisible(true);
- setDefaultSize(387, 307, ImageRect::CENTER);
+ setDefaultSize(387, 307, WindowAlignment::Center);
setMinWidth(316);
setMinHeight(179);
addKeyListener(this);
diff --git a/src/gui/itemamountwindow.cpp b/src/gui/itemamountwindow.cpp
index da5dc073..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,7 +45,7 @@ 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);
@@ -127,7 +128,7 @@ ItemAmountWindow::ItemAmountWindow(Usage usage, Window *parent, Item *item,
place(4, 2, cancelButton);
place(5, 2, okButton);
- reflowLayout(225, 0);
+ reflowLayout();
resetAmount();
diff --git a/src/gui/itempopup.cpp b/src/gui/itempopup.cpp
index 35951331..3e7ba15a 100644
--- a/src/gui/itempopup.cpp
+++ b/src/gui/itempopup.cpp
@@ -43,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;
}
}
@@ -132,7 +132,11 @@ 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());
@@ -173,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 535104cf..c741ed64 100644
--- a/src/gui/itempopup.h
+++ b/src/gui/itempopup.h
@@ -29,6 +29,7 @@
#include <guichan/mouselistener.hpp>
class Icon;
+class Label;
class TextBox;
/**
@@ -65,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;
diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp
index 8924bc26..60bfadfa 100644
--- a/src/gui/minimap.cpp
+++ b/src/gui/minimap.cpp
@@ -41,7 +41,7 @@
#include <guichan/font.hpp>
Minimap::Minimap():
- Window(_("Map"))
+ Window(SkinType::Popup, _("Map"))
{
setWindowName("Minimap");
setDefaultSize(5, 25, 100, 100);
diff --git a/src/gui/npcdialog.cpp b/src/gui/npcdialog.cpp
index e2e7b040..033d01cc 100644
--- a/src/gui/npcdialog.cpp
+++ b/src/gui/npcdialog.cpp
@@ -82,7 +82,7 @@ NpcDialog::NpcDialog(int npcId)
setMinWidth(200);
setMinHeight(150);
- setDefaultSize(260, 200, ImageRect::CENTER);
+ setDefaultSize(260, 200, WindowAlignment::Center);
// Setup output text box
mTextBox = new BrowserBox(BrowserBox::AUTO_WRAP);
@@ -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/okdialog.cpp b/src/gui/okdialog.cpp
index 5a710890..287ee1b8 100644
--- a/src/gui/okdialog.cpp
+++ b/src/gui/okdialog.cpp
@@ -22,6 +22,7 @@
#include "gui/okdialog.h"
#include "gui/widgets/button.h"
+#include "gui/widgets/layout.h"
#include "gui/widgets/textbox.h"
#include "utils/gettext.h"
@@ -32,33 +33,16 @@ OkDialog::OkDialog(const std::string &title, const std::string &msg,
bool modal, Window *parent):
Window(title, modal, parent)
{
- mTextBox = new TextBox;
- mTextBox->setEditable(false);
- mTextBox->setOpaque(false);
- mTextBox->setTextWrapped(msg, 260);
+ auto textBox = new TextBox;
+ textBox->setEditable(false);
+ textBox->setOpaque(false);
+ textBox->setTextWrapped(msg, 260);
gcn::Button *okButton = new Button(_("OK"), "ok", this);
- const int numRows = mTextBox->getNumberOfRows();
- const int fontHeight = getFont()->getHeight();
- const int height = numRows * fontHeight;
- int width = getFont()->getWidth(title);
-
- if (width < mTextBox->getMinWidth())
- width = mTextBox->getMinWidth();
- if (width < okButton->getWidth())
- width = okButton->getWidth();
-
- setContentSize(mTextBox->getMinWidth() + fontHeight, height +
- fontHeight + okButton->getHeight());
- mTextBox->setPosition(getPadding(), getPadding());
-
- // 8 is the padding that GUIChan adds to button widgets
- // (top and bottom combined)
- okButton->setPosition((width - okButton->getWidth()) / 2, height + 8);
-
- add(mTextBox);
- add(okButton);
+ place(0, 0, textBox);
+ place(0, 1, okButton).setHAlign(Layout::CENTER);
+ reflowLayout();
center();
setVisible(true);
@@ -69,8 +53,5 @@ void OkDialog::action(const gcn::ActionEvent &event)
{
setActionEventId(event.getId());
distributeActionEvent();
-
- // Can we receive anything else anyway?
- if (event.getId() == "ok")
- scheduleDelete();
+ scheduleDelete();
}
diff --git a/src/gui/okdialog.h b/src/gui/okdialog.h
index f56f24e2..18bca454 100644
--- a/src/gui/okdialog.h
+++ b/src/gui/okdialog.h
@@ -25,8 +25,6 @@
#include <guichan/actionlistener.hpp>
-class TextBox;
-
/**
* An 'Ok' button dialog.
*
@@ -42,7 +40,4 @@ class OkDialog : public Window, public gcn::ActionListener
* Called when receiving actions from the widgets.
*/
void action(const gcn::ActionEvent &event) override;
-
- private:
- TextBox *mTextBox;
};
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 145a93ac..268b9fc6 100644
--- a/src/gui/palette.h
+++ b/src/gui/palette.h
@@ -27,8 +27,9 @@
#include <guichan/color.hpp>
#include <cstdlib>
-#include <string>
+#include <optional>
#include <set>
+#include <string>
#include <vector>
// Default Gradient Delay
@@ -40,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 {
@@ -51,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
@@ -128,10 +121,6 @@ class Palette
using Palettes = std::set<Palette *>;
static Palettes mInstances;
- Palette(int size);
-
- ~Palette();
-
void advanceGradient(int advance);
struct ColorElem
@@ -140,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;
diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp
index 4bafc074..16b16af9 100644
--- a/src/gui/popupmenu.cpp
+++ b/src/gui/popupmenu.cpp
@@ -318,7 +318,7 @@ void PopupMenu::handleLink(const std::string &link)
// Unknown actions
else if (link != "cancel")
{
- logger->log("PopupMenu: Warning, unknown action '%s'", link.c_str());
+ Log::info("PopupMenu: Warning, unknown action '%s'", link.c_str());
}
setVisible(false);
diff --git a/src/gui/questswindow.cpp b/src/gui/questswindow.cpp
new file mode 100644
index 00000000..6769815e
--- /dev/null
+++ b/src/gui/questswindow.cpp
@@ -0,0 +1,290 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2025 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "questswindow.h"
+
+#include "configuration.h"
+
+#include "gui/setup.h"
+
+#include "gui/widgets/browserbox.h"
+#include "gui/widgets/checkbox.h"
+#include "gui/widgets/itemlinkhandler.h"
+#include "gui/widgets/layout.h"
+#include "gui/widgets/listbox.h"
+#include "gui/widgets/scrollarea.h"
+
+#include "net/net.h"
+#include "net/playerhandler.h"
+
+#include "resources/questdb.h"
+
+#include "utils/gettext.h"
+
+#include <guichan/font.hpp>
+
+#include <algorithm>
+
+class QuestsModel final : public gcn::ListModel
+{
+public:
+ int getNumberOfElements() override
+ { return mQuests.size(); }
+
+ std::string getElementAt(int i) override
+ { return mQuests[i].name(); }
+
+ const std::vector<QuestEntry> &getQuests() const
+ { return mQuests; }
+
+ void setQuests(const std::vector<QuestEntry> &quests)
+ { mQuests = quests; }
+
+private:
+ std::vector<QuestEntry> mQuests;
+};
+
+
+class QuestsListBox final : public ListBox
+{
+public:
+ QuestsListBox(QuestsModel *model)
+ : ListBox(model)
+ {}
+
+ unsigned getRowHeight() const override;
+
+ void draw(gcn::Graphics *graphics) override;
+};
+
+unsigned QuestsListBox::getRowHeight() const
+{
+ auto rowHeight = ListBox::getRowHeight();
+
+ if (auto icon = gui->getTheme()->getIcon("complete"))
+ rowHeight = std::max<unsigned>(rowHeight, icon->getHeight() + 2);
+
+ return rowHeight;
+}
+
+void QuestsListBox::draw(gcn::Graphics *gcnGraphics)
+{
+ if (!mListModel)
+ return;
+
+ auto *graphics = static_cast<Graphics *>(gcnGraphics);
+ auto *model = static_cast<QuestsModel *>(getListModel());
+
+ const int rowHeight = getRowHeight();
+
+ auto theme = gui->getTheme();
+ auto completeIcon = theme->getIcon("complete");
+ auto incompleteIcon = theme->getIcon("incomplete");
+
+ // 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, rowHeight * mSelected,
+ getWidth(), rowHeight));
+ }
+
+ // Draw the list elements
+ graphics->setFont(getFont());
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+
+ const int fontHeight = getFont()->getHeight();
+
+ for (int i = 0, y = 0; i < model->getNumberOfElements();
+ ++i, y += rowHeight)
+ {
+ if (mSelected == i)
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT_TEXT));
+ else
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+
+ auto &quest = model->getQuests()[i];
+ int x = 1;
+
+ if (const Image *icon = quest.completed ? completeIcon : incompleteIcon)
+ {
+ graphics->drawImage(icon, x, y + (rowHeight - icon->getHeight()) / 2);
+ x += icon->getWidth() + 4;
+ }
+
+ graphics->drawText(quest.name(), x, y + (rowHeight - fontHeight) / 2);
+ }
+}
+
+
+QuestsWindow::QuestsWindow()
+ : Window(_("Quests"))
+ , mQuestsModel(std::make_unique<QuestsModel>())
+ , mQuestsListBox(new QuestsListBox(mQuestsModel.get()))
+ , mHideCompletedCheckBox(new CheckBox(_("Hide completed"), config.hideCompletedQuests))
+ , mQuestDetails(new BrowserBox(BrowserBox::AUTO_WRAP))
+ , mLinkHandler(std::make_unique<ItemLinkHandler>())
+{
+ setWindowName("Quests");
+ setupWindow->registerWindowForReset(this);
+ setResizable(true);
+ setCloseButton(true);
+ setSaveVisible(true);
+
+ setDefaultSize(387, 307, WindowAlignment::Center);
+ setMinWidth(316);
+ setMinHeight(179);
+
+ mQuestsListBox->addSelectionListener(this);
+ mHideCompletedCheckBox->setActionEventId("hideCompleted");
+ mHideCompletedCheckBox->addActionListener(this);
+
+ auto questListScrollArea = new ScrollArea(mQuestsListBox);
+ questListScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
+
+ mQuestDetails->setLinkHandler(mLinkHandler.get());
+ mQuestDetailsScrollArea = new ScrollArea(mQuestDetails);
+ mQuestDetailsScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
+
+ auto place = getPlacer(0, 0);
+ place(0, 0, questListScrollArea, 2, 2).setPadding(2);
+ place(2, 0, mQuestDetailsScrollArea, 3, 2).setPadding(2);
+ place = getPlacer(0, 1);
+ place(0, 0, mHideCompletedCheckBox);
+
+ getLayout().setRowHeight(1, 0); // Don't scale up the bottom row
+
+ listen(Event::QuestsChannel);
+
+ refreshQuestList();
+ loadWindowState();
+}
+
+QuestsWindow::~QuestsWindow() = default;
+
+void QuestsWindow::action(const gcn::ActionEvent &event)
+{
+ if (event.getId() == "hideCompleted")
+ {
+ config.hideCompletedQuests = mHideCompletedCheckBox->isSelected();
+ refreshQuestList();
+ }
+}
+
+void QuestsWindow::valueChanged(const gcn::SelectionEvent &event)
+{
+ if (mSelectedQuestIndex != mQuestsListBox->getSelected())
+ updateQuestDetails();
+}
+
+void QuestsWindow::event(Event::Channel channel, const Event &event)
+{
+ if (channel == Event::QuestsChannel)
+ {
+ if (event.getType() == Event::QuestVarsChanged)
+ refreshQuestList();
+ }
+}
+
+void QuestsWindow::refreshQuestList()
+{
+ // Store the currently selected quest state and varId to preserve selection
+ const QuestState *selectedQuestState = nullptr;
+ int selectedVarId = -1;
+ if (mSelectedQuestIndex >= 0 && mSelectedQuestIndex < mQuestsModel->getNumberOfElements())
+ {
+ const auto &selectedQuest = mQuestsModel->getQuests().at(mSelectedQuestIndex);
+ selectedQuestState = selectedQuest.state;
+ selectedVarId = selectedQuest.varId;
+ }
+
+ auto &questVars = Net::getPlayerHandler()->getQuestVars();
+ auto newQuests = QuestDB::getQuestsEntries(questVars, config.hideCompletedQuests);
+
+ // Put completed quests at the top
+ std::stable_sort(newQuests.begin(), newQuests.end(), [](const QuestEntry &a, const QuestEntry &b) {
+ return a.completed > b.completed;
+ });
+
+ mQuestsModel->setQuests(newQuests);
+
+ if (!selectedQuestState)
+ return;
+
+ // Try to find and reselect the same quest, preferring exact state match
+ int newSelectedIndex = -1;
+
+ for (int i = 0; i < static_cast<int>(newQuests.size()); ++i)
+ {
+ if (newQuests[i].state == selectedQuestState)
+ {
+ newSelectedIndex = i;
+ break;
+ }
+ else if (newSelectedIndex == -1 && newQuests[i].varId == selectedVarId)
+ {
+ newSelectedIndex = i;
+ // Don't break here - continue looking for exact state match
+ }
+ }
+
+ if (mSelectedQuestIndex != newSelectedIndex)
+ mQuestsListBox->setSelected(newSelectedIndex);
+ else
+ updateQuestDetails();
+}
+
+void QuestsWindow::updateQuestDetails()
+{
+ mQuestDetails->clearRows();
+
+ mSelectedQuestIndex = mQuestsListBox->getSelected();
+ if (mSelectedQuestIndex < 0 || mSelectedQuestIndex >= mQuestsModel->getNumberOfElements())
+ return;
+
+ const QuestEntry &quest = mQuestsModel->getQuests().at(mSelectedQuestIndex);
+ for (const auto &row : quest.rows())
+ {
+ switch (row.type)
+ {
+ case QuestRowType::Text:
+ mQuestDetails->addRow(row.text);
+ break;
+ case QuestRowType::Name:
+ mQuestDetails->addRow("[" + row.text + "]");
+ break;
+ case QuestRowType::Reward:
+ mQuestDetails->addRow(strprintf(_("Reward: %s"), row.text.c_str()));
+ break;
+ case QuestRowType::Giver:
+ mQuestDetails->addRow(strprintf(_("Quest Giver: %s"), row.text.c_str()));
+ break;
+ case QuestRowType::Coordinates:
+ mQuestDetails->addRow(strprintf(_("Coordinates: %s (%d, %d)"),
+ row.text.c_str(), row.x, row.y));
+ break;
+ case QuestRowType::NPC:
+ mQuestDetails->addRow(strprintf(_("NPC: %s"), row.text.c_str()));
+ break;
+ }
+ }
+}
diff --git a/src/gui/questswindow.h b/src/gui/questswindow.h
new file mode 100644
index 00000000..a479f826
--- /dev/null
+++ b/src/gui/questswindow.h
@@ -0,0 +1,71 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2025 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "eventlistener.h"
+
+#include "gui/widgets/window.h"
+
+#include <guichan/actionlistener.hpp>
+#include <guichan/keylistener.hpp>
+#include <guichan/selectionlistener.hpp>
+
+class BrowserBox;
+class CheckBox;
+class LinkHandler;
+class QuestsListBox;
+class QuestsModel;
+class ScrollArea;
+
+/**
+ * Quests window.
+ *
+ * \ingroup Interface
+ */
+class QuestsWindow final : public Window,
+ public gcn::ActionListener,
+ public gcn::SelectionListener,
+ public EventListener
+{
+public:
+ QuestsWindow();
+ ~QuestsWindow();
+
+ void action(const gcn::ActionEvent &event) override;
+
+ void valueChanged(const gcn::SelectionEvent &event) override;
+
+ void event(Event::Channel channel, const Event &event) override;
+
+private:
+ void refreshQuestList();
+ void updateQuestDetails();
+
+ int mSelectedQuestIndex = -1;
+ std::unique_ptr<QuestsModel> mQuestsModel;
+ QuestsListBox *mQuestsListBox;
+ CheckBox *mHideCompletedCheckBox;
+ BrowserBox *mQuestDetails;
+ ScrollArea *mQuestDetailsScrollArea;
+ std::unique_ptr<LinkHandler> mLinkHandler;
+};
+
+extern QuestsWindow *questsWindow;
diff --git a/src/gui/recorder.cpp b/src/gui/recorder.cpp
index 894e3631..96e458ff 100644
--- a/src/gui/recorder.cpp
+++ b/src/gui/recorder.cpp
@@ -47,7 +47,7 @@ Recorder::Recorder(ChatWindow *chat,
// 123 is the default chat window height. If you change this in Chat, please
// change it here as well
setDefaultSize(button->getWidth() + offsetX, button->getHeight() +
- offsetY, ImageRect::LOWER_LEFT, 0, 123);
+ offsetY, WindowAlignment::BottomLeft, 0, 123);
place(0, 0, button);
diff --git a/src/gui/register.cpp b/src/gui/register.cpp
index 62114c10..d7924021 100644
--- a/src/gui/register.cpp
+++ b/src/gui/register.cpp
@@ -147,7 +147,7 @@ void RegisterDialog::action(const gcn::ActionEvent &event)
else if (event.getId() == "register" && canSubmit())
{
const std::string user = mUserField->getText();
- logger->log("RegisterDialog::register Username is %s", user.c_str());
+ Log::info("RegisterDialog::register Username is %s", user.c_str());
std::string errorMessage;
int error = 0;
diff --git a/src/gui/selldialog.cpp b/src/gui/selldialog.cpp
index 4aeacd6f..4fa12e53 100644
--- a/src/gui/selldialog.cpp
+++ b/src/gui/selldialog.cpp
@@ -56,7 +56,7 @@ SellDialog::SellDialog(int npcId):
setCloseButton(true);
setMinWidth(260);
setMinHeight(230);
- setDefaultSize(260, 230, ImageRect::CENTER);
+ setDefaultSize(260, 230, WindowAlignment::Center);
// Create a ShopItems instance, that is aware of duplicate entries.
mShopItems = new ShopItems(true);
diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp
index d86d751a..e41c0bbe 100644
--- a/src/gui/serverdialog.cpp
+++ b/src/gui/serverdialog.cpp
@@ -95,20 +95,19 @@ public:
auto *model = static_cast<ServersListModel*>(mListModel);
- const int alpha = gui->getTheme()->getGuiAlpha();
-
- graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, alpha));
graphics->setFont(getFont());
const int height = getRowHeight();
- const gcn::Color unsupported =
- Theme::getThemeColor(Theme::SERVER_VERSION_NOT_SUPPORTED,
- alpha);
// 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();
@@ -116,7 +115,10 @@ public:
{
const ServerInfo &info = model->getServer(i);
- graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+ if (mSelected == i)
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT_TEXT));
+ else
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
if (!info.name.empty())
{
@@ -132,7 +134,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);
}
@@ -200,7 +204,7 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir):
setMinWidth(getWidth());
setMinHeight(getHeight());
- setDefaultSize(getWidth(), getHeight(), ImageRect::CENTER);
+ setDefaultSize(getWidth(), getHeight(), WindowAlignment::Center);
setResizable(true);
addKeyListener(this);
@@ -232,7 +236,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
)
{
@@ -360,7 +364,7 @@ void ServerDialog::logic()
case DownloadStatus::Canceled:
case DownloadStatus::Error:
mDownloadDone = true;
- logger->log("Error retrieving server list: %s", mDownload->getError());
+ Log::info("Error retrieving server list: %s", mDownload->getError());
mDownloadText->setCaption(_("Error retrieving server list!"));
break;
@@ -405,15 +409,14 @@ void ServerDialog::loadServers()
if (!rootNode || rootNode.name() != "serverlist")
{
- logger->log("Error loading server list!");
+ Log::info("Error loading server list!");
return;
}
int version = rootNode.getProperty("version", 0);
if (version != 1)
{
- logger->log("Error: unsupported online server list version: %d",
- version);
+ Log::error("Unsupported online server list version: %d", version);
return;
}
@@ -435,12 +438,12 @@ void ServerDialog::loadServer(XML::Node serverNode)
// 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());
+ Log::info("Ignoring server entry with unknown type: %s",
+ type.c_str());
return;
}
@@ -510,13 +513,8 @@ void ServerDialog::loadServer(XML::Node serverNode)
void ServerDialog::loadCustomServers()
{
for (auto &server : config.servers)
- {
if (server.isValid())
- {
- server.save = true;
mServers.push_back(server);
- }
- }
}
void ServerDialog::saveCustomServers(const ServerInfo &currentServer, int index)
diff --git a/src/gui/setup_audio.cpp b/src/gui/setup_audio.cpp
index 43b132d8..0031f9bc 100644
--- a/src/gui/setup_audio.cpp
+++ b/src/gui/setup_audio.cpp
@@ -108,7 +108,7 @@ void Setup_Audio::apply()
catch (const char *err)
{
new OkDialog(_("Sound Engine"), err);
- logger->log("Warning: %s", err);
+ Log::warn("%s", err);
}
}
else
diff --git a/src/gui/setup_colors.cpp b/src/gui/setup_colors.cpp
index c3d6a8c8..52cb4bf7 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);
@@ -180,8 +187,8 @@ void Setup_Colors::action(const gcn::ActionEvent &event)
{
if (event.getId() == "slider_grad")
{
- updateGradType();
updateColor();
+ updateGradType();
return;
}
@@ -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_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 9f5bbf1a..027526a2 100644
--- a/src/gui/setup_interface.h
+++ b/src/gui/setup_interface.h
@@ -29,8 +29,6 @@
#include <guichan/actionlistener.hpp>
#include <guichan/keylistener.hpp>
-class FontSizeChoiceListModel;
-
class Setup_Interface : public SetupTab, public gcn::ActionListener,
public gcn::KeyListener
{
@@ -53,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;
@@ -72,7 +67,7 @@ class Setup_Interface : public SetupTab, public gcn::ActionListener,
gcn::Label *mSpeechLabel;
gcn::Slider *mAlphaSlider;
- int mFontSize;
+ gcn::DropDown *mThemeDropDown;
gcn::DropDown *mFontSizeDropDown;
};
diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp
index b5322525..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"
diff --git a/src/gui/shortcutwindow.cpp b/src/gui/shortcutwindow.cpp
index 82c7678e..c33fcf01 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.
@@ -48,10 +49,13 @@ ShortcutWindow::ShortcutWindow(const std::string &title,
const int border = (getPadding() + content->getFrameSize()) * 2;
setMinWidth(content->getBoxWidth() + border);
setMinHeight(content->getBoxHeight() + border + GRAB_MARGIN);
- setMaxWidth(content->getBoxWidth() * content->getMaxItems() + border);
- setMaxHeight(content->getBoxHeight() * content->getMaxItems() + border + GRAB_MARGIN);
- setDefaultSize(getMinWidth(), getMaxHeight(), ImageRect::LOWER_RIGHT);
+ const int maxContentWidth = content->getBoxWidth() * content->getMaxItems();
+ const int maxContentHeight = content->getBoxHeight() * content->getMaxItems();
+ setMaxWidth(std::max(getMinWidth(), maxContentWidth + border));
+ setMaxHeight(std::max(getMinHeight(), maxContentHeight + border + GRAB_MARGIN));
+
+ setDefaultSize(getMinWidth(), getMaxHeight(), WindowAlignment::BottomRight);
place(0, 0, scrollArea, 5, 5).setPadding(0);
diff --git a/src/gui/skilldialog.cpp b/src/gui/skilldialog.cpp
index c1911ac5..49552421 100644
--- a/src/gui/skilldialog.cpp
+++ b/src/gui/skilldialog.cpp
@@ -134,24 +134,28 @@ public:
auto *model = static_cast<SkillModel *>(mListModel);
auto *graphics = static_cast<Graphics *>(gcnGraphics);
- const int alpha = gui->getTheme()->getGuiAlpha();
-
- graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, alpha));
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()));
}
// Draw the list elements
- graphics->setColor(Theme::getThemeColor(Theme::TEXT));
for (int i = 0, y = 1;
i < model->getNumberOfElements();
++i, y += getRowHeight())
{
+ if (mSelected == i)
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT_TEXT));
+ else
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+
if (SkillInfo *e = model->getSkillAt(i))
e->draw(graphics, y, getWidth());
}
@@ -290,7 +294,7 @@ void SkillDialog::loadSkills()
if (!root || root.name() != "skills")
{
- logger->log("Error loading skills file: %s", SKILLS_FILE);
+ Log::info("Error loading skills file: %s", SKILLS_FILE);
if (Net::getNetworkType() == ServerType::TmwAthena)
{
diff --git a/src/gui/socialwindow.cpp b/src/gui/socialwindow.cpp
index 072d87bc..265fb166 100644
--- a/src/gui/socialwindow.cpp
+++ b/src/gui/socialwindow.cpp
@@ -171,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());
diff --git a/src/gui/tradewindow.cpp b/src/gui/tradewindow.cpp
index db4b6490..6610bd43 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")
@@ -64,7 +60,7 @@ TradeWindow::TradeWindow():
setWindowName("Trade");
setResizable(true);
setCloseButton(true);
- setDefaultSize(386, 180, ImageRect::CENTER);
+ setDefaultSize(386, 180, WindowAlignment::Center);
setMinWidth(386);
setMinHeight(180);
setupWindow->registerWindowForReset(this);
@@ -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 d54ec35e..85b2f97d 100644
--- a/src/gui/tradewindow.h
+++ b/src/gui/tradewindow.h
@@ -81,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.
*/
diff --git a/src/gui/truetypefont.cpp b/src/gui/truetypefont.cpp
index 444641b5..2dd7b817 100644
--- a/src/gui/truetypefont.cpp
+++ b/src/gui/truetypefont.cpp
@@ -45,13 +45,6 @@ static const char *getSafeUtf8String(const std::string &text)
return buf;
}
-bool operator==(SDL_Color lhs, SDL_Color rhs)
-{
- return (lhs.r == rhs.r &&
- lhs.g == rhs.g &&
- lhs.b == rhs.b &&
- lhs.a == rhs.a);
-}
class TextChunk
{
@@ -60,24 +53,49 @@ public:
: text(text)
{}
- void generate(TTF_Font *font)
+ void render(Graphics *graphics,
+ int x, int y,
+ TTF_Font *font,
+ std::unique_ptr<Image> &img,
+ float scale);
+
+ const std::string text;
+ std::unique_ptr<Image> regular;
+ std::unique_ptr<Image> outlined;
+};
+
+void TextChunk::render(Graphics *graphics,
+ int x, int y,
+ TTF_Font *font,
+ std::unique_ptr<Image> &img,
+ float scale)
+{
+ if (!img)
{
// Always render in white, we'll use color modulation when rendering
+ constexpr SDL_Color white = { 255, 255, 255, 255 };
SDL_Surface *surface = TTF_RenderUTF8_Blended(font,
getSafeUtf8String(text),
- SDL_Color { 255, 255, 255, 255 });
+ white);
- if (!surface)
- return;
+ if (surface)
+ {
+ img.reset(Image::load(surface));
+ SDL_FreeSurface(surface);
+ }
+ }
- img.reset(Image::load(surface));
+ if (img)
+ {
+ graphics->drawRescaledImageF(img.get(), 0, 0, x, y,
+ img->getWidth(),
+ img->getHeight(),
+ img->getWidth() / scale,
+ img->getHeight() / scale, true);
- SDL_FreeSurface(surface);
}
+}
- std::unique_ptr<Image> img;
- const std::string text;
-};
std::list<TrueTypeFont*> TrueTypeFont::mFonts;
float TrueTypeFont::mScale = 1.0f;
@@ -85,6 +103,7 @@ float TrueTypeFont::mScale = 1.0f;
TrueTypeFont::TrueTypeFont(const std::string &filename, int size, int style)
: mFilename(filename)
, mPointSize(size)
+ , mStyle(style)
{
if (TTF_Init() == -1)
{
@@ -93,14 +112,18 @@ TrueTypeFont::TrueTypeFont(const std::string &filename, int size, int style)
}
mFont = TTF_OpenFont(filename.c_str(), size * mScale);
+ mFontOutline = TTF_OpenFont(filename.c_str(), size * mScale);
- if (!mFont)
+ if (!mFont || !mFontOutline)
{
throw GCN_EXCEPTION("SDLTrueTypeFont::SDLTrueTypeFont: " +
std::string(TTF_GetError()));
}
TTF_SetFontStyle(mFont, style);
+ TTF_SetFontStyle(mFontOutline, style);
+
+ TTF_SetFontOutline(mFontOutline, static_cast<int>(mScale));
mFonts.push_back(this);
}
@@ -112,6 +135,9 @@ TrueTypeFont::~TrueTypeFont()
if (mFont)
TTF_CloseFont(mFont);
+ if (mFontOutline)
+ TTF_CloseFont(mFontOutline);
+
TTF_Quit();
}
@@ -123,37 +149,41 @@ void TrueTypeFont::drawString(gcn::Graphics *graphics,
return;
auto *g = static_cast<Graphics *>(graphics);
+ TextChunk &chunk = getChunk(text);
- bool found = false;
+ chunk.render(g, x, y, mFont, chunk.regular, mScale);
+}
- for (auto i = mCache.begin(); i != mCache.end(); ++i)
- {
- auto &chunk = *i;
- if (chunk.text == text)
- {
- // Raise priority: move it to front
- mCache.splice(mCache.begin(), mCache, i);
- found = true;
- break;
- }
- }
+void TrueTypeFont::drawString(Graphics *graphics,
+ const std::string &text,
+ int x, int y,
+ const std::optional<gcn::Color> &outlineColor,
+ const std::optional<gcn::Color> &shadowColor)
+{
+ if (text.empty())
+ return;
+
+ auto *g = static_cast<Graphics *>(graphics);
+ auto color = graphics->getColor();
+ TextChunk &chunk = getChunk(text);
- if (!found)
+ if (shadowColor)
{
- if (mCache.size() >= CACHE_SIZE)
- mCache.pop_back();
- mCache.emplace_front(text);
- mCache.front().generate(mFont);
+ g->setColor(*shadowColor);
+ if (outlineColor)
+ chunk.render(g, x, y, mFontOutline, chunk.outlined, mScale);
+ else
+ chunk.render(g, x + 1, y + 1, mFont, chunk.regular, mScale);
}
- if (auto img = mCache.front().img.get())
+ if (outlineColor)
{
- g->drawRescaledImageF(img, 0, 0, x, y,
- img->getWidth(),
- img->getHeight(),
- img->getWidth() / mScale,
- img->getHeight() / mScale, true);
+ g->setColor(*outlineColor);
+ chunk.render(g, x - 1, y - 1, mFontOutline, chunk.outlined, mScale);
}
+
+ g->setColor(color);
+ chunk.render(g, x, y, mFont, chunk.regular, mScale);
}
void TrueTypeFont::updateFontScale(float scale)
@@ -167,9 +197,16 @@ void TrueTypeFont::updateFontScale(float scale)
{
#if SDL_TTF_VERSION_ATLEAST(2, 0, 18)
TTF_SetFontSize(font->mFont, font->mPointSize * mScale);
+ TTF_SetFontSize(font->mFontOutline, font->mPointSize * mScale);
+ TTF_SetFontOutline(font->mFontOutline, mScale);
#else
TTF_CloseFont(font->mFont);
+ TTF_CloseFont(font->mFontOutline);
font->mFont = TTF_OpenFont(font->mFilename.c_str(), font->mPointSize * mScale);
+ font->mFontOutline = TTF_OpenFont(font->mFilename.c_str(), font->mPointSize * mScale);
+ TTF_SetFontStyle(font->mFont, font->mStyle);
+ TTF_SetFontStyle(font->mFontOutline, font->mStyle);
+ TTF_SetFontOutline(font->mFontOutline, mScale);
#endif
font->mCache.clear();
@@ -178,19 +215,11 @@ void TrueTypeFont::updateFontScale(float scale)
int TrueTypeFont::getWidth(const std::string &text) const
{
- for (auto i = mCache.begin(); i != mCache.end(); i++)
- {
- if (i->text == text)
- {
- // Raise priority: move it to front
- // Assumption is that TTF::draw will be called next
- mCache.splice(mCache.begin(), mCache, i);
- if (i->img)
- return std::ceil(i->img->getWidth() / mScale);
- return 0;
- }
- }
+ TextChunk &chunk = getChunk(text);
+ if (auto img = chunk.regular.get())
+ return std::ceil(img->getWidth() / mScale);
+ // If the image wasn't created yet, just calculate the width of the text
int w, h;
TTF_SizeUTF8(mFont, getSafeUtf8String(text), &w, &h);
return std::ceil(w / mScale);
@@ -205,3 +234,21 @@ int TrueTypeFont::getLineHeight() const
{
return std::ceil(TTF_FontLineSkip(mFont) / mScale);
}
+
+TextChunk &TrueTypeFont::getChunk(const std::string &text) const
+{
+ for (auto i = mCache.begin(); i != mCache.end(); i++)
+ {
+ if (i->text == text)
+ {
+ // Raise priority: move it to front
+ mCache.splice(mCache.begin(), mCache, i);
+ return *i;
+ }
+ }
+
+ if (mCache.size() >= CACHE_SIZE)
+ mCache.pop_back();
+
+ return mCache.emplace_front(text);
+}
diff --git a/src/gui/truetypefont.h b/src/gui/truetypefont.h
index a479537d..f88938ff 100644
--- a/src/gui/truetypefont.h
+++ b/src/gui/truetypefont.h
@@ -22,13 +22,16 @@
#pragma once
+#include <guichan/color.hpp>
#include <guichan/font.hpp>
#include <SDL_ttf.h>
#include <list>
+#include <optional>
#include <string>
+class Graphics;
class TextChunk;
/**
@@ -46,11 +49,13 @@ class TrueTypeFont : public gcn::Font
* @param size Font size.
*/
TrueTypeFont(const std::string &filename, int size, int style = 0);
-
~TrueTypeFont() override;
- int getWidth(const std::string &text) const override;
+ const std::string &filename() const { return mFilename; }
+ int pointSize() const { return mPointSize; }
+ int style() const { return mStyle; }
+ int getWidth(const std::string &text) const override;
int getHeight() const override;
/**
@@ -67,13 +72,27 @@ class TrueTypeFont : public gcn::Font
const std::string &text,
int x, int y) override;
+ /**
+ * Extended version of drawString that allows for rendering text with
+ * outline and/or shadow.
+ */
+ void drawString(Graphics *graphics,
+ const std::string &text,
+ int x, int y,
+ const std::optional<gcn::Color> &outlineColor,
+ const std::optional<gcn::Color> &shadowColor);
+
static void updateFontScale(float scale);
private:
+ TextChunk &getChunk(const std::string &text) const;
+
const std::string mFilename;
- TTF_Font *mFont;
+ TTF_Font *mFont = nullptr;
+ TTF_Font *mFontOutline = nullptr;
const int mPointSize;
+ const int mStyle;
// Word surfaces cache
mutable std::list<TextChunk> mCache;
diff --git a/src/gui/unregisterdialog.cpp b/src/gui/unregisterdialog.cpp
index 94f6ef62..2abbebf7 100644
--- a/src/gui/unregisterdialog.cpp
+++ b/src/gui/unregisterdialog.cpp
@@ -98,8 +98,8 @@ void UnRegisterDialog::action(const gcn::ActionEvent &event)
else if (event.getId() == "unregister")
{
const std::string &password = mPasswordField->getText();
- logger->log("UnregisterDialog::unregistered, Username is %s",
- mLoginData->username.c_str());
+ Log::info("UnregisterDialog::unregistered, Username is %s",
+ mLoginData->username.c_str());
std::stringstream errorMessage;
bool error = false;
diff --git a/src/gui/updaterwindow.cpp b/src/gui/updaterwindow.cpp
index 5cfb45cd..c16c3e04 100644
--- a/src/gui/updaterwindow.cpp
+++ b/src/gui/updaterwindow.cpp
@@ -60,7 +60,7 @@ std::vector<UpdateFile> loadXMLFile(const std::string &fileName)
if (!rootNode || rootNode.name() != "updates")
{
- logger->log("Error loading update file: %s", fileName.c_str());
+ Log::info("Error loading update file: %s", fileName.c_str());
return files;
}
@@ -110,7 +110,7 @@ std::vector<UpdateFile> loadTxtFile(const std::string &fileName)
}
else
{
- logger->log("Error loading update file: %s", fileName.c_str());
+ Log::info("Error loading update file: %s", fileName.c_str());
}
fileHandler.close();
@@ -128,7 +128,7 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost,
{
setWindowName("UpdaterWindow");
setResizable(true);
- setDefaultSize(450, 400, ImageRect::CENTER);
+ setDefaultSize(450, 400, WindowAlignment::Center);
setMinWidth(320);
setMinHeight(240);
@@ -258,9 +258,9 @@ void UpdaterWindow::loadUpdates()
mUpdateFiles = loadXMLFile(mUpdatesDir + "/" + xmlUpdateFile);
if (mUpdateFiles.empty())
{
- logger->log("Warning this server does not have a"
- " %s file falling back to %s", xmlUpdateFile,
- txtUpdateFile);
+ Log::warn("This server does not have a"
+ " %s file falling back to %s", xmlUpdateFile,
+ txtUpdateFile);
mUpdateFiles = loadTxtFile(mUpdatesDir + "/" + txtUpdateFile);
}
}
@@ -336,9 +336,9 @@ void UpdaterWindow::downloadCompleted()
mUpdateFiles = loadXMLFile(mUpdatesDir + "/" + xmlUpdateFile);
if (mUpdateFiles.empty())
{
- logger->log("Warning this server does not have a %s"
- " file falling back to %s",
- xmlUpdateFile, txtUpdateFile);
+ Log::warn("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;
@@ -383,7 +383,7 @@ void UpdaterWindow::downloadCompleted()
else
{
fclose(file);
- logger->log("%s already here", thisFile.name.c_str());
+ Log::info("%s already here", thisFile.name.c_str());
}
mUpdateIndex++;
}
diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp
index 9e529063..3d90846e 100644
--- a/src/gui/viewport.cpp
+++ b/src/gui/viewport.cpp
@@ -278,7 +278,7 @@ void Viewport::_followMouse()
mMouseY = static_cast<int>(logicalY);
// If the left button is dragged
- if (mPlayerFollowMouse && button & SDL_BUTTON(1))
+ if (mPlayerFollowMouse && button & SDL_BUTTON_LMASK)
{
// We create a mouse event and send it to mouseDragged.
const Uint8 *keys = SDL_GetKeyboardState(nullptr);
diff --git a/src/gui/widgets/avatarlistbox.cpp b/src/gui/widgets/avatarlistbox.cpp
index f7d6e801..a5109267 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;
-ResourceRef<Image> AvatarListBox::onlineIcon;
-ResourceRef<Image> AvatarListBox::offlineIcon;
-
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 = nullptr;
- offlineIcon = nullptr;
- }
+ 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)
@@ -69,29 +57,43 @@ void AvatarListBox::draw(gcn::Graphics *gcnGraphics)
auto *model = static_cast<AvatarListModel *>(mListModel);
auto *graphics = static_cast<Graphics *>(gcnGraphics);
- const int alpha = gui->getTheme()->getGuiAlpha();
-
- graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, alpha));
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)
{
+ if (mSelected == i)
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT_TEXT));
+ else
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+
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);
@@ -109,7 +111,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 9b0588ac..1f51935f 100644
--- a/src/gui/widgets/avatarlistbox.h
+++ b/src/gui/widgets/avatarlistbox.h
@@ -23,7 +23,6 @@
#include "avatar.h"
#include "gui/widgets/listbox.h"
-#include "resources/resource.h"
#include <string>
@@ -43,7 +42,7 @@ class AvatarListBox : public ListBox
public:
AvatarListBox(AvatarListModel *model);
- ~AvatarListBox() override;
+ unsigned int getRowHeight() const override;
/**
* Draws the list box.
@@ -51,9 +50,4 @@ public:
void draw(gcn::Graphics *gcnGraphics) override;
void mousePressed(gcn::MouseEvent &event) override;
-
-private:
- static int instances;
- static ResourceRef<Image> onlineIcon;
- static ResourceRef<Image> offlineIcon;
};
diff --git a/src/gui/widgets/browserbox.cpp b/src/gui/widgets/browserbox.cpp
index 91366720..bf337a0f 100644
--- a/src/gui/widgets/browserbox.cpp
+++ b/src/gui/widgets/browserbox.cpp
@@ -23,7 +23,6 @@
#include "gui/widgets/browserbox.h"
#include "keyboardconfig.h"
-#include "textrenderer.h"
#include "gui/gui.h"
#include "gui/truetypefont.h"
@@ -73,9 +72,12 @@ static void replaceKeys(std::string &text)
}
}
+
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;
@@ -83,23 +85,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)
@@ -175,7 +193,7 @@ void BrowserBox::addRow(std::string_view row)
replaceKeys(newRow.text);
// Layout the newly added row
- LayoutContext context(getFont());
+ LayoutContext context(getFont(), gui->getTheme()->getPalette(mPalette));
context.y = getHeight();
layoutTextRow(newRow, context);
@@ -209,7 +227,7 @@ void BrowserBox::addRow(std::string_view row)
void BrowserBox::clearRows()
{
mTextRows.clear();
- setSize(0, 0);
+ setSize(mMode == AUTO_SIZE ? 0 : getWidth(), 0);
mHoveredLink.reset();
maybeRelayoutText();
}
@@ -252,19 +270,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,
@@ -272,6 +291,8 @@ void BrowserBox::draw(gcn::Graphics *graphics)
}
}
+ auto g = static_cast<Graphics*>(graphics);
+
for (const auto &row : mTextRows)
{
for (const auto &part : row.parts)
@@ -281,15 +302,15 @@ void BrowserBox::draw(gcn::Graphics *graphics)
if (part.y > yEnd)
return;
- TextRenderer::renderText(graphics,
- part.text,
- part.x,
- part.y,
- Graphics::LEFT,
- part.color,
- part.font,
- mOutline,
- mShadows);
+ g->drawText(part.text,
+ part.x,
+ part.y,
+ Graphics::LEFT,
+ part.color,
+ part.font,
+ part.outlineColor.has_value() || mOutline,
+ mShadows,
+ part.outlineColor);
}
}
}
@@ -299,7 +320,7 @@ 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);
@@ -317,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();
@@ -330,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;
@@ -347,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
@@ -371,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 == '<')
- {
- prevColor = context.selColor;
- context.selColor = col;
- }
- else if (c == 'B')
- {
- context.font = boldFont;
- }
- else if (c == 'b')
+ switch (c)
{
- 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
@@ -481,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()
});
@@ -495,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)
diff --git a/src/gui/widgets/browserbox.h b/src/gui/widgets/browserbox.h
index 7066585d..142bba63 100644
--- a/src/gui/widgets/browserbox.h
+++ b/src/gui/widgets/browserbox.h
@@ -51,6 +51,7 @@ struct LinePart
int x;
int y;
gcn::Color color;
+ std::optional<gcn::Color> outlineColor;
std::string text;
gcn::Font *font;
};
@@ -86,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.
*/
@@ -154,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.
*/
@@ -195,6 +175,7 @@ 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;
diff --git a/src/gui/widgets/button.cpp b/src/gui/widgets/button.cpp
index 31c3a677..604f5dc8 100644
--- a/src/gui/widgets/button.cpp
+++ b/src/gui/widgets/button.cpp
@@ -28,7 +28,6 @@
#include "resources/image.h"
#include "resources/theme.h"
-#include "textrenderer.h"
#include <guichan/exception.hpp>
#include <guichan/font.hpp>
@@ -132,8 +131,9 @@ void Button::draw(gcn::Graphics *graphics)
if (isPressed())
widgetState.flags |= STATE_SELECTED;
+ auto g = static_cast<Graphics *>(graphics);
auto &skin = gui->getTheme()->getSkin(SkinType::Button);
- skin.draw(static_cast<Graphics *>(graphics), widgetState);
+ skin.draw(g, widgetState);
auto skinState = skin.getState(widgetState.flags);
auto font = (skinState && skinState->textFormat.bold) ? boldFont : getFont();
@@ -196,18 +196,16 @@ void Button::draw(gcn::Graphics *graphics)
}
if (btnIconWidth)
- static_cast<Graphics *>(graphics)->drawImage(icon, btnIconX, btnIconY);
+ g->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);
+ g->drawText(getCaption(),
+ textX,
+ textY,
+ getAlignment(),
+ font,
+ skinState->textFormat);
}
}
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 dfc07638..5232392f 100644
--- a/src/gui/widgets/chattab.h
+++ b/src/gui/widgets/chattab.h
@@ -24,7 +24,6 @@
#include "gui/chatwindow.h"
#include "gui/widgets/tab.h"
-#include "gui/widgets/textfield.h"
class BrowserBox;
class Recorder;
diff --git a/src/gui/widgets/checkbox.cpp b/src/gui/widgets/checkbox.cpp
index e6079f2f..4bb0bb72 100644
--- a/src/gui/widgets/checkbox.cpp
+++ b/src/gui/widgets/checkbox.cpp
@@ -21,8 +21,6 @@
#include "gui/widgets/checkbox.h"
-#include "textrenderer.h"
-
#include "gui/gui.h"
#include "resources/theme.h"
@@ -44,19 +42,19 @@ void CheckBox::draw(gcn::Graphics* graphics)
if (isSelected())
widgetState.flags |= STATE_SELECTED;
+ auto g = static_cast<Graphics *>(graphics);
auto &skin = gui->getTheme()->getSkin(SkinType::CheckBox);
- skin.draw(static_cast<Graphics *>(graphics), widgetState);
+ skin.draw(g, widgetState);
if (auto skinState = skin.getState(widgetState.flags))
{
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);
+ g->drawText(getCaption(),
+ skin.getMinWidth() + skin.padding + skin.spacing,
+ skin.padding,
+ Graphics::LEFT,
+ textFormat.bold ? boldFont : getFont(),
+ textFormat);
}
}
diff --git a/src/gui/widgets/container.h b/src/gui/widgets/container.h
index fbdaa1d4..54b7950b 100644
--- a/src/gui/widgets/container.h
+++ b/src/gui/widgets/container.h
@@ -32,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.
diff --git a/src/gui/widgets/desktop.cpp b/src/gui/widgets/desktop.cpp
index e424beec..aae78335 100644
--- a/src/gui/widgets/desktop.cpp
+++ b/src/gui/widgets/desktop.cpp
@@ -69,24 +69,20 @@ void Desktop::draw(gcn::Graphics *graphics)
{
auto *g = static_cast<Graphics *>(graphics);
- if (!mWallpaper || (getWidth() > mWallpaper->getWidth() ||
- getHeight() > mWallpaper->getHeight()))
- {
- // TODO: Color from palette
- g->setColor(gcn::Color(64, 64, 64));
- g->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
- }
-
if (mWallpaper)
{
g->drawRescaledImage(mWallpaper, 0, 0, 0, 0,
mWallpaper->getWidth(), mWallpaper->getHeight(),
getWidth(), getHeight(), false);
}
+ else
+ {
+ gui->getTheme()->drawSkin(g, SkinType::Desktop, WidgetState(this));
+ }
- // Draw a thin border under the application version...
+ // Draw a background beneath the application version for readability...
g->setColor(gcn::Color(255, 255, 255, 128));
- g->fillRectangle(gcn::Rectangle(mVersionLabel->getDimension()));
+ g->fillRectangle(mVersionLabel->getDimension());
Container::draw(graphics);
}
@@ -113,6 +109,6 @@ void Desktop::setBestFittingWallpaper()
}
else
{
- logger->log("Couldn't load %s as wallpaper", wallpaperName.c_str());
+ Log::info("Couldn't load %s as wallpaper", wallpaperName.c_str());
}
}
diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp
index 45c8e53f..28308ce6 100644
--- a/src/gui/widgets/dropdown.cpp
+++ b/src/gui/widgets/dropdown.cpp
@@ -42,6 +42,9 @@ DropDown::DropDown(gcn::ListModel *listModel):
setFrameSize(skin.frameSize);
mPadding = skin.padding;
+ // Make sure to call the right setOpaque function
+ static_cast<ScrollArea*>(mScrollArea)->setOpaque(false);
+
setHeight(getFont()->getHeight() + 2 * mPadding);
}
@@ -57,7 +60,8 @@ void DropDown::draw(gcn::Graphics* graphics)
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;
@@ -72,7 +76,7 @@ void DropDown::draw(gcn::Graphics* graphics)
if (isFocused())
{
- graphics->setColor(*highlightColor);
+ graphics->setColor(highlightColor);
graphics->drawRectangle(
gcn::Rectangle(mPadding, mPadding, getWidth() - h - mPadding * 2, h - 2 * mPadding));
}
@@ -85,10 +89,10 @@ void DropDown::draw(gcn::Graphics* graphics)
// Draw two lines separating the ListBox with selected
// element view.
- graphics->setColor(*highlightColor);
- graphics->drawLine(0, h, getWidth(), h);
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, h, getWidth() - 1, h);
graphics->setColor(shadowColor);
- graphics->drawLine(0, h + 1, getWidth(), h + 1);
+ graphics->drawLine(0, h + 1, getWidth() - 1, h + 1);
}
}
@@ -109,21 +113,22 @@ void DropDown::adjustHeight()
const int listBoxHeight = mListBox->getHeight();
int height = getFont()->getHeight() + 2 * mPadding;
- // The addition/subtraction of 2 compensates for the seperation lines
+ // The addition/subtraction of 4 compensates for the seperation lines
// seperating the selected element view and the scroll area.
+ const int extraHeight = 4;
if (mDroppedDown && getParent())
{
- int availableHeight = getParent()->getChildrenArea().height - getY();
+ int availableHeight = getParent()->getChildrenArea().height - getY() - getFrameSize();
- if (listBoxHeight > availableHeight - height - 2)
+ if (listBoxHeight > availableHeight - height - extraHeight)
{
- mScrollArea->setHeight(availableHeight - height - 2);
+ mScrollArea->setHeight(availableHeight - height - extraHeight);
height = availableHeight;
}
else
{
- height += listBoxHeight + 2;
+ height += listBoxHeight + extraHeight;
mScrollArea->setHeight(listBoxHeight);
}
}
@@ -131,11 +136,26 @@ void DropDown::adjustHeight()
setHeight(height);
mScrollArea->setWidth(getWidth());
- // Resize the ListBox to exactly fit the ScrollArea.
- mListBox->setWidth(mScrollArea->getChildrenArea().width);
+ // Resize the ListBox to exactly fit the ScrollArea, minus the one pixel padding.
+ mListBox->setWidth(mScrollArea->getChildrenArea().width - 2);
mScrollArea->setPosition(0, 0);
}
+// Overridden to add more space for the separator
+gcn::Rectangle DropDown::getChildrenArea()
+{
+ if (mDroppedDown)
+ {
+ // Calculate the children area (with the two pixel border in mind)
+ return gcn::Rectangle(1,
+ mFoldedUpHeight + 3,
+ getWidth() - 2,
+ getHeight() - mFoldedUpHeight - 4);
+ }
+
+ return gcn::Rectangle();
+}
+
void DropDown::drawButton(gcn::Graphics *graphics)
{
WidgetState state(this);
@@ -147,13 +167,12 @@ void DropDown::drawButton(gcn::Graphics *graphics)
if (mPushed)
state.flags |= STATE_HOVERED;
- const auto theme = gui->getTheme();
- const int buttonWidth = theme->getMinWidth(SkinType::DropDownButton);
+ auto &skin = gui->getTheme()->getSkin(SkinType::DropDownButton);
// FIXME: Needs support for setting alignment in the theme.
- state.x = state.width - buttonWidth;
+ state.x = state.width - skin.getMinWidth();
- theme->drawSkin(static_cast<Graphics *>(graphics), SkinType::DropDownButton, state);
+ skin.draw(static_cast<Graphics *>(graphics), state);
}
// -- KeyListener notifications
diff --git a/src/gui/widgets/dropdown.h b/src/gui/widgets/dropdown.h
index a5e2e2f7..d3a6cbbe 100644
--- a/src/gui/widgets/dropdown.h
+++ b/src/gui/widgets/dropdown.h
@@ -49,6 +49,10 @@ class DropDown : public gcn::DropDown
void adjustHeight();
+ // Inherited from BasicContainer
+
+ gcn::Rectangle getChildrenArea() override;
+
// Inherited from FocusListener
void focusLost(const gcn::Event& event) override;
diff --git a/src/gui/widgets/itemcontainer.cpp b/src/gui/widgets/itemcontainer.cpp
index d1d00677..63388213 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);
@@ -112,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)
@@ -153,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->setColor(theme->getColor(Theme::TEXT));
- 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));
}
}
@@ -354,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());
@@ -375,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 0a8ac1e2..c1d611b9 100644
--- a/src/gui/widgets/itemcontainer.h
+++ b/src/gui/widgets/itemcontainer.h
@@ -21,8 +21,6 @@
#pragma once
-#include "resources/resource.h"
-
#include <guichan/keylistener.hpp>
#include <guichan/mouselistener.hpp>
#include <guichan/widget.hpp>
@@ -182,7 +180,6 @@ class ItemContainer : public gcn::Widget,
Inventory *mInventory;
int mGridColumns = 1;
int mGridRows = 1;
- ResourceRef<Image> mSelImg;
int mSelectedIndex = -1;
int mHighlightedIndex = -1;
int mLastUsedSlot = -1;
diff --git a/src/gui/widgets/itemlinkhandler.cpp b/src/gui/widgets/itemlinkhandler.cpp
index f596fc82..d9d6f506 100644
--- a/src/gui/widgets/itemlinkhandler.cpp
+++ b/src/gui/widgets/itemlinkhandler.cpp
@@ -33,6 +33,7 @@
#include "gui/widgets/itemlinkhandler.h"
+#include "client.h"
#include "resources/iteminfo.h"
#include "resources/itemdb.h"
#include "utils/gettext.h"
@@ -42,6 +43,7 @@ ItemLinkHandler::ItemLinkHandler(Window *parent)
: mParent(parent)
{
mItemPopup = std::make_unique<ItemPopup>();
+ mItemPopup->addDeathListener(this);
}
ItemLinkHandler::~ItemLinkHandler() = default;
@@ -55,6 +57,24 @@ static bool isUrl(const std::string &link)
void ItemLinkHandler::handleLink(const std::string &link)
{
+#if SDL_VERSION_ATLEAST(2, 0, 14)
+ // Handle screenshots by constructing full file path
+ if (startsWith(link, "screenshot:"))
+ {
+ std::string filename = link.substr(11); // Remove "screenshot:" prefix
+
+ // Prevent directory traversal attacks or opening malicious files
+ if (filename.find("..") == std::string::npos && endsWith(filename, ".png"))
+ {
+ std::string fileUrl = "file://" + Client::getScreenshotDirectory() + "/" + filename;
+ if (SDL_OpenURL(fileUrl.c_str()) == -1)
+ new OkDialog(_("Open URL Failed"), SDL_GetError(), true, mParent);
+ }
+
+ return;
+ }
+#endif
+
if (isUrl(link))
{
mLink = link;
@@ -97,3 +117,10 @@ void ItemLinkHandler::action(const gcn::ActionEvent &actionEvent)
#endif
}
}
+
+void ItemLinkHandler::death(const gcn::Event &event)
+{
+ // If somebody else killed the PopupUp, make sure we don't also try to delete it
+ if (event.getSource() == mItemPopup.get())
+ mItemPopup.release();
+}
diff --git a/src/gui/widgets/itemlinkhandler.h b/src/gui/widgets/itemlinkhandler.h
index 58202d33..637482bd 100644
--- a/src/gui/widgets/itemlinkhandler.h
+++ b/src/gui/widgets/itemlinkhandler.h
@@ -24,13 +24,14 @@
#include "gui/widgets/linkhandler.h"
#include <guichan/actionlistener.hpp>
+#include <guichan/deathlistener.hpp>
#include <memory>
class ItemPopup;
class Window;
-class ItemLinkHandler : public LinkHandler, gcn::ActionListener
+class ItemLinkHandler : public LinkHandler, gcn::ActionListener, public gcn::DeathListener
{
public:
ItemLinkHandler(Window *parent = nullptr);
@@ -42,6 +43,9 @@ class ItemLinkHandler : public LinkHandler, gcn::ActionListener
// ActionListener interface
void action(const gcn::ActionEvent &actionEvent) override;
+ // DeathListener interface
+ void death(const gcn::Event &event) override;
+
private:
std::unique_ptr<ItemPopup> mItemPopup;
diff --git a/src/gui/widgets/itemshortcutcontainer.cpp b/src/gui/widgets/itemshortcutcontainer.cpp
index 4d0641b1..b47fa29d 100644
--- a/src/gui/widgets/itemshortcutcontainer.cpp
+++ b/src/gui/widgets/itemshortcutcontainer.cpp
@@ -49,6 +49,7 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics)
{
auto *g = static_cast<Graphics*>(graphics);
auto theme = gui->getTheme();
+ auto &skin = theme->getSkin(SkinType::ShortcutBox);
graphics->setFont(getFont());
@@ -57,26 +58,25 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics)
WidgetState state;
state.x = (i % mGridWidth) * mBoxWidth;
state.y = (i / mGridWidth) * mBoxHeight;
- theme->drawSkin(g, SkinType::ShortcutBox, state);
+ 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, state.x + 2, state.y + 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)
@@ -85,11 +85,13 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics)
caption = "Eq.";
image->setAlpha(1.0f);
- g->drawImage(image, state.x, state.y);
+ g->drawImage(image, state.x + skin.padding, state.y + skin.padding);
if (item->isEquipped())
g->setColor(Theme::getThemeColor(Theme::ITEM_EQUIPPED));
- g->drawText(caption, state.x + mBoxWidth / 2,
- state.y + mBoxHeight - 14, gcn::Graphics::CENTER);
+ g->drawText(caption,
+ state.x + mBoxWidth / 2,
+ state.y + mBoxHeight - 14,
+ gcn::Graphics::CENTER);
}
}
}
@@ -97,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);
@@ -202,15 +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;
-
- if (Item *item = PlayerInfo::getInventory()->findItem(itemId))
+ if (Item *item = getItemAt(event.getX(), event.getY()))
{
mItemPopup->setItem(item->getInfo());
mItemPopup->position(viewport->getMouseX(), viewport->getMouseY());
@@ -221,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 63d9e0ef..a01857db 100644
--- a/src/gui/widgets/itemshortcutcontainer.h
+++ b/src/gui/widgets/itemshortcutcontainer.h
@@ -67,6 +67,8 @@ 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;
diff --git a/src/gui/widgets/label.cpp b/src/gui/widgets/label.cpp
index 53a82e14..9c0fd3cd 100644
--- a/src/gui/widgets/label.cpp
+++ b/src/gui/widgets/label.cpp
@@ -21,8 +21,6 @@
#include "gui/widgets/label.h"
-#include "textrenderer.h"
-
#include "resources/theme.h"
#include <guichan/exception.hpp>
@@ -42,7 +40,7 @@ Label::Label(const std::string &caption) :
void Label::draw(gcn::Graphics *graphics)
{
int textX;
- int textY = getHeight() / 2 - getFont()->getHeight() / 2;
+ int textY = (getHeight() - getFont()->getHeight()) / 2;
switch (getAlignment())
{
@@ -59,15 +57,15 @@ void Label::draw(gcn::Graphics *graphics)
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);
+ auto g = static_cast<Graphics *>(graphics);
+ g->drawText(getCaption(),
+ textX,
+ textY,
+ getAlignment(),
+ getForegroundColor(),
+ getFont(),
+ mOutlineColor.has_value(),
+ mShadowColor.has_value(),
+ mOutlineColor,
+ mShadowColor);
}
diff --git a/src/gui/widgets/listbox.cpp b/src/gui/widgets/listbox.cpp
index f1fcfd53..612e785f 100644
--- a/src/gui/widgets/listbox.cpp
+++ b/src/gui/widgets/listbox.cpp
@@ -41,23 +41,29 @@ void ListBox::draw(gcn::Graphics *graphics)
if (!mListModel)
return;
- const int alpha = gui->getTheme()->getGuiAlpha();
-
- graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, alpha));
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));
for (int i = 0, y = 0; i < mListModel->getNumberOfElements();
++i, y += height)
{
+ if (mSelected == i)
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT_TEXT));
+ else
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+
graphics->drawText(mListModel->getElementAt(i), 1, y);
}
}
diff --git a/src/gui/widgets/popup.cpp b/src/gui/widgets/popup.cpp
index b7c70fe5..c4c6f652 100644
--- a/src/gui/widgets/popup.cpp
+++ b/src/gui/widgets/popup.cpp
@@ -22,11 +22,14 @@
#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 <guichan/exception.hpp>
@@ -37,12 +40,12 @@ Popup::Popup(const std::string &name, SkinType skinType)
, mMaxHeight(graphics->getHeight())
, mSkinType(skinType)
{
- logger->log("Popup::Popup(\"%s\")", name.c_str());
+ Log::debug("Popup::Popup(\"%s\")", name.c_str());
if (!windowContainer)
throw GCN_EXCEPTION("Popup::Popup(): no windowContainer set");
- auto &skin = gui->getTheme()->getSkin(skinType);
+ auto &skin = getSkin();
setFrameSize(skin.frameSize);
setPadding(skin.padding);
@@ -55,7 +58,7 @@ Popup::Popup(const std::string &name, SkinType skinType)
Popup::~Popup()
{
- logger->log("Popup::~Popup(\"%s\")", mPopupName.c_str());
+ Log::debug("Popup::~Popup(\"%s\")", mPopupName.c_str());
}
void Popup::setWindowContainer(WindowContainer *wc)
@@ -63,6 +66,41 @@ void Popup::setWindowContainer(WindowContainer *wc)
windowContainer = wc;
}
+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
+{
+ 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));
+ }
+ }
+}
+
void Popup::draw(gcn::Graphics *graphics)
{
if (getFrameSize() == 0)
@@ -76,7 +114,7 @@ void Popup::drawFrame(gcn::Graphics *graphics)
WidgetState state(this);
state.width += getFrameSize() * 2;
state.height += getFrameSize() * 2;
- gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), mSkinType, state);
+ getSkin().draw(static_cast<Graphics *>(graphics), state);
}
gcn::Rectangle Popup::getChildrenArea()
@@ -119,12 +157,12 @@ void Popup::setLocationRelativeTo(gcn::Widget *widget)
void Popup::setMinWidth(int width)
{
- mMinWidth = std::max(gui->getTheme()->getMinWidth(mSkinType), width);
+ mMinWidth = std::max(getSkin().getMinWidth(), width);
}
void Popup::setMinHeight(int height)
{
- mMinHeight = std::max(gui->getTheme()->getMinHeight(mSkinType), height);
+ mMinHeight = std::max(getSkin().getMinHeight(), height);
}
void Popup::setMaxWidth(int width)
@@ -159,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 012b55de..b88cafc9 100644
--- a/src/gui/widgets/popup.h
+++ b/src/gui/widgets/popup.h
@@ -68,6 +68,10 @@ 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.
*/
@@ -156,7 +160,14 @@ 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:
+ void widgetAdded(gcn::Widget *widget) const;
+
std::string mPopupName; /**< Name of the popup */
int mMinWidth = 100; /**< Minimum popup width */
int mMinHeight = 40; /**< Minimum popup height */
diff --git a/src/gui/widgets/progressbar.cpp b/src/gui/widgets/progressbar.cpp
index 5cf1b05a..502ab686 100644
--- a/src/gui/widgets/progressbar.cpp
+++ b/src/gui/widgets/progressbar.cpp
@@ -83,11 +83,16 @@ void ProgressBar::draw(gcn::Graphics *graphics)
rect.x = 0;
rect.y = 0;
+ Theme::ProgressPalette palette = Theme::THEME_PROG_END;
+ if (mProgressPalette >= 0)
+ palette = static_cast<Theme::ProgressPalette>(mProgressPalette);
+
gui->getTheme()->drawProgressBar(static_cast<Graphics *>(graphics),
rect,
mColor,
mProgress,
- mText);
+ mText,
+ palette);
}
void ProgressBar::setProgress(float progress)
diff --git a/src/gui/widgets/radiobutton.cpp b/src/gui/widgets/radiobutton.cpp
index ceba78eb..3474bbd8 100644
--- a/src/gui/widgets/radiobutton.cpp
+++ b/src/gui/widgets/radiobutton.cpp
@@ -21,11 +21,11 @@
#include "gui/widgets/radiobutton.h"
-#include "textrenderer.h"
-
#include "gui/gui.h"
#include "resources/theme.h"
+#include <guichan/font.hpp>
+
RadioButton::RadioButton(const std::string &caption,
const std::string &group,
bool marked)
@@ -44,19 +44,19 @@ void RadioButton::draw(gcn::Graphics* graphics)
if (isSelected())
widgetState.flags |= STATE_SELECTED;
+ auto g = static_cast<Graphics *>(graphics);
auto &skin = gui->getTheme()->getSkin(SkinType::RadioButton);
- skin.draw(static_cast<Graphics *>(graphics), widgetState);
+ skin.draw(g, widgetState);
if (auto skinState = skin.getState(widgetState.flags))
{
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);
+ g->drawText(getCaption(),
+ skin.getMinWidth() + skin.padding + skin.spacing,
+ skin.padding,
+ Graphics::LEFT,
+ textFormat.bold ? boldFont : getFont(),
+ textFormat);
}
}
diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp
index c802a405..0c5a7fb5 100644
--- a/src/gui/widgets/resizegrip.cpp
+++ b/src/gui/widgets/resizegrip.cpp
@@ -31,7 +31,7 @@
ResizeGrip::ResizeGrip()
{
auto &skin = gui->getTheme()->getSkin(SkinType::ResizeGrip);
- setSize(skin.getMinWidth() + skin.padding, skin.getMinHeight() + skin.padding);
+ setSize(skin.width, skin.height);
}
void ResizeGrip::draw(gcn::Graphics *graphics)
diff --git a/src/gui/widgets/scrollarea.cpp b/src/gui/widgets/scrollarea.cpp
index c4d55072..1dec34be 100644
--- a/src/gui/widgets/scrollarea.cpp
+++ b/src/gui/widgets/scrollarea.cpp
@@ -25,6 +25,8 @@
#include "gui/gui.h"
+#include <guichan/exception.hpp>
+
ScrollArea::ScrollArea()
{
init();
@@ -41,6 +43,11 @@ ScrollArea::~ScrollArea()
delete getContent();
}
+void ScrollArea::setShowButtons(bool showButtons)
+{
+ mShowButtons = showButtons;
+}
+
void ScrollArea::init()
{
// Draw background by default
@@ -48,12 +55,15 @@ void ScrollArea::init()
auto theme = gui->getTheme();
- int minWidth = theme->getSkin(SkinType::ScrollAreaVBar).getMinWidth();
- if (minWidth > 0)
- setScrollbarWidth(minWidth);
+ int scrollBarWidth = theme->getSkin(SkinType::ScrollAreaVBar).width;
+ if (scrollBarWidth > 0)
+ setScrollbarWidth(scrollBarWidth);
+
+ auto &scrollAreaSkin = theme->getSkin(SkinType::ScrollArea);
+ setShowButtons(scrollAreaSkin.showButtons);
if (auto content = getContent())
- content->setFrameSize(theme->getSkin(SkinType::ScrollArea).padding);
+ content->setFrameSize(scrollAreaSkin.padding);
// 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
@@ -157,23 +167,47 @@ void ScrollArea::drawBackground(gcn::Graphics *graphics)
// background is drawn as part of the frame instead
}
+static void drawButton(gcn::Graphics *graphics,
+ SkinType skinType,
+ bool pressed,
+ const gcn::Rectangle &dim)
+{
+ WidgetState state(dim);
+ if (pressed)
+ state.flags |= STATE_SELECTED;
+
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), skinType, state);
+}
+
void ScrollArea::drawUpButton(gcn::Graphics *graphics)
{
+ if (!mShowButtons)
+ return;
+
drawButton(graphics, SkinType::ButtonUp, mUpButtonPressed, getUpButtonDimension());
}
void ScrollArea::drawDownButton(gcn::Graphics *graphics)
{
+ if (!mShowButtons)
+ return;
+
drawButton(graphics, SkinType::ButtonDown, mDownButtonPressed, getDownButtonDimension());
}
void ScrollArea::drawLeftButton(gcn::Graphics *graphics)
{
+ if (!mShowButtons)
+ return;
+
drawButton(graphics, SkinType::ButtonLeft, mLeftButtonPressed, getLeftButtonDimension());
}
void ScrollArea::drawRightButton(gcn::Graphics *graphics)
{
+ if (!mShowButtons)
+ return;
+
drawButton(graphics, SkinType::ButtonRight, mRightButtonPressed, getRightButtonDimension());
}
@@ -198,6 +232,9 @@ void ScrollArea::drawHBar(gcn::Graphics *graphics)
void ScrollArea::drawVMarker(gcn::Graphics *graphics)
{
WidgetState state(getVerticalMarkerDimension());
+ if (state.height == 0)
+ return;
+
if (mHasMouse && (mX > (getWidth() - getScrollbarWidth())))
state.flags |= STATE_HOVERED;
@@ -207,22 +244,115 @@ void ScrollArea::drawVMarker(gcn::Graphics *graphics)
void ScrollArea::drawHMarker(gcn::Graphics *graphics)
{
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);
}
-void ScrollArea::drawButton(gcn::Graphics *graphics,
- SkinType skinType,
- bool pressed,
- const gcn::Rectangle &dim)
+/**
+ * Code copied from gcn::ScrollArea::checkPolicies to make sure it takes the
+ * frame size of the content into account.
+ */
+void ScrollArea::checkPolicies()
{
- WidgetState state(dim);
- if (pressed)
- state.flags |= STATE_SELECTED;
+ int w = getWidth();
+ int h = getHeight();
- gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), skinType, state);
+ 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)
@@ -240,3 +370,300 @@ void ScrollArea::mouseExited(gcn::MouseEvent& event)
{
mHasMouse = false;
}
+
+/**
+ * Code copied from gcn::ScrollArea::mousePressed to make it call our custom
+ * getVerticalMarkerDimension and getHorizontalMarkerDimension functions.
+ */
+void ScrollArea::mousePressed(gcn::MouseEvent &mouseEvent)
+{
+ 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 40f1adc1..a0167a32 100644
--- a/src/gui/widgets/scrollarea.h
+++ b/src/gui/widgets/scrollarea.h
@@ -21,8 +21,6 @@
#pragma once
-#include "resources/theme.h"
-
#include <guichan/widgets/scrollarea.hpp>
/**
@@ -32,6 +30,8 @@
* 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
@@ -56,6 +56,11 @@ class ScrollArea : public gcn::ScrollArea
~ScrollArea() override;
/**
+ * Sets whether the scroll bar buttons are shown.
+ */
+ void setShowButtons(bool showButtons);
+
+ /**
* Logic function optionally adapts width or height of contents. This
* depends on the scrollbar settings.
*/
@@ -74,7 +79,7 @@ class ScrollArea : public gcn::ScrollArea
/**
* Applies clipping to the contents.
*/
- void drawChildren(gcn::Graphics* graphics) override;
+ void drawChildren(gcn::Graphics *graphics) override;
/**
* Sets whether the widget should draw its background or not.
@@ -89,17 +94,20 @@ class ScrollArea : public gcn::ScrollArea
/**
* 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 mousePressed(gcn::MouseEvent &mouseEvent) override;
+ void mouseDragged(gcn::MouseEvent &mouseEvent) override;
protected:
/**
@@ -117,13 +125,30 @@ class ScrollArea : public gcn::ScrollArea
void drawVMarker(gcn::Graphics *graphics) override;
void drawHMarker(gcn::Graphics *graphics) override;
- static void drawButton(gcn::Graphics *graphics,
- SkinType skinType,
- bool pressed,
- const gcn::Rectangle &dim);
+ 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;
};
diff --git a/src/gui/widgets/shoplistbox.cpp b/src/gui/widgets/shoplistbox.cpp
index 31c733a6..afcb97b4 100644
--- a/src/gui/widgets/shoplistbox.cpp
+++ b/src/gui/widgets/shoplistbox.cpp
@@ -71,14 +71,14 @@ 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);
+ auto highlightTextColor = Theme::getThemeColor(Theme::HIGHLIGHT_TEXT);
+ highlightColor.a = alpha;
+ backgroundColor.a = alpha;
+ warningColor.a = alpha;
auto *graphics = static_cast<Graphics*>(gcnGraphics);
@@ -103,7 +103,8 @@ void ShopListBox::draw(gcn::Graphics *gcnGraphics)
gcn::Color blend = warningColor;
blend.r = (blend.r + highlightColor.r) / 2;
blend.g = (blend.g + highlightColor.g) / 2;
- blend.b = (blend.g + highlightColor.b) / 2;
+ blend.b = (blend.b + highlightColor.b) / 2;
+ blend.a = alpha;
graphics->setColor(blend);
}
}
@@ -137,7 +138,7 @@ void ShopListBox::draw(gcn::Graphics *gcnGraphics)
}
}
- graphics->setColor(textColor);
+ graphics->setColor(i == mSelected ? highlightTextColor : textColor);
graphics->drawText(mListModel->getElementAt(i),
ITEM_ICON_SIZE + 5,
y + (ITEM_ICON_SIZE - fontHeight) / 2);
@@ -168,8 +169,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/shortcutcontainer.cpp b/src/gui/widgets/shortcutcontainer.cpp
index ccf4b082..9271b1f4 100644
--- a/src/gui/widgets/shortcutcontainer.cpp
+++ b/src/gui/widgets/shortcutcontainer.cpp
@@ -31,8 +31,8 @@ ShortcutContainer::ShortcutContainer()
addWidgetListener(this);
auto &skin = gui->getTheme()->getSkin(SkinType::ShortcutBox);
- mBoxWidth = skin.getMinWidth();
- mBoxHeight = skin.getMinHeight();
+ mBoxWidth = skin.width;
+ mBoxHeight = skin.height;
}
void ShortcutContainer::widgetResized(const gcn::Event &event)
diff --git a/src/gui/widgets/tab.cpp b/src/gui/widgets/tab.cpp
index b2779c4f..0f6ca4e5 100644
--- a/src/gui/widgets/tab.cpp
+++ b/src/gui/widgets/tab.cpp
@@ -73,18 +73,27 @@ void Tab::draw(gcn::Graphics *graphics)
if (mTabbedArea && mTabbedArea->isTabSelected(this))
flags |= STATE_SELECTED;
- auto &skin = gui->getTheme()->getSkin(SkinType::Tab);
+ auto theme = gui->getTheme();
+ auto &palette = theme->getPalette(0);
+ auto &skin = theme->getSkin(SkinType::Tab);
+
if (auto state = skin.getState(flags))
{
gcn::Color foregroundColor = state->textFormat.color;
+ auto outlineColor = state->textFormat.outlineColor;
if (mFlash)
- foregroundColor = Theme::getThemeColor(Theme::TAB_FLASH);
+ {
+ foregroundColor = palette.getColor(Theme::TAB_FLASH);
+ outlineColor = palette.getOutlineColor(Theme::TAB_FLASH);
+ }
else if (mTabColor)
+ {
foregroundColor = *mTabColor;
+ }
auto label = static_cast<Label*>(mLabel);
label->setForegroundColor(foregroundColor);
- label->setOutlineColor(state->textFormat.outlineColor);
+ label->setOutlineColor(outlineColor);
label->setShadowColor(state->textFormat.shadowColor);
}
diff --git a/src/gui/widgets/tabbedarea.cpp b/src/gui/widgets/tabbedarea.cpp
index fb5436e0..3609791a 100644
--- a/src/gui/widgets/tabbedarea.cpp
+++ b/src/gui/widgets/tabbedarea.cpp
@@ -112,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)
{
diff --git a/src/gui/widgets/tabbedarea.h b/src/gui/widgets/tabbedarea.h
index 558b2696..5d0ccfcc 100644
--- a/src/gui/widgets/tabbedarea.h
+++ b/src/gui/widgets/tabbedarea.h
@@ -86,12 +86,12 @@ class TabbedArea final : 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;
diff --git a/src/gui/widgets/table.cpp b/src/gui/widgets/table.cpp
index 7cddc1fa..909617a0 100644
--- a/src/gui/widgets/table.cpp
+++ b/src/gui/widgets/table.cpp
@@ -77,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,10 +263,15 @@ void GuiTable::draw(gcn::Graphics* graphics)
if (mOpaque)
{
- graphics->setColor(Theme::getThemeColor(Theme::BACKGROUND, guiAlpha));
+ 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());
@@ -314,7 +313,7 @@ void GuiTable::draw(gcn::Graphics* graphics)
widget->setDimension(bounds);
- graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, guiAlpha));
+ graphics->setColor(highlightColor);
if (mLinewiseMode && r == mSelectedRow && c == 0)
{
@@ -361,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());
}
@@ -480,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);
diff --git a/src/gui/widgets/table.h b/src/gui/widgets/table.h
index 81071267..88350928 100644
--- a/src/gui/widgets/table.h
+++ b/src/gui/widgets/table.h
@@ -49,7 +49,7 @@ class GuiTable final : public gcn::Widget,
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;
@@ -84,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
@@ -105,7 +105,7 @@ public:
// Overridden to disable drawing of the frame
void drawFrame(gcn::Graphics* graphics) override {}
- virtual gcn::Widget *getWidgetAt(int x, int y) const;
+ gcn::Widget *getWidgetAt(int x, int y) override;
void moveToTop(gcn::Widget *child) override;
@@ -122,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
@@ -130,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;
@@ -146,19 +146,20 @@ 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 mLinewiseMode = false;
+ bool mWrappingEnabled = false;
bool mOpaque;
/**
@@ -166,16 +167,13 @@ private:
*/
gcn::Color mBackgroundColor;
- TableModel *mModel;
-
- int mSelectedRow;
- int mSelectedColumn;
+ TableModel *mModel = nullptr;
- /** 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;
diff --git a/src/gui/widgets/textbox.cpp b/src/gui/widgets/textbox.cpp
index 419fa16e..7fd7d626 100644
--- a/src/gui/widgets/textbox.cpp
+++ b/src/gui/widgets/textbox.cpp
@@ -21,15 +21,19 @@
#include "gui/widgets/textbox.h"
+#include "gui/gui.h"
#include "resources/theme.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();
@@ -91,7 +95,7 @@ void TextBox::setTextWrapped(const std::string &text, int minDimension)
xpos = width;
wrappedStream << word;
}
- else if (xpos != 0 && xpos + getFont()->getWidth(" ") + width <=
+ else if (xpos != 0 && xpos + getFont()->getWidth(" ") + width <=
mMinWidth)
{
xpos += getFont()->getWidth(" ") + width;
@@ -147,3 +151,43 @@ 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());
+
+ auto g = static_cast<Graphics*>(graphics);
+
+ for (i = 0; i < mTextRows.size(); i++)
+ {
+ // Move the text one pixel so we can have a caret before a letter.
+ g->drawText(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 e4596b9a..49a5a2ad 100644
--- a/src/gui/widgets/textbox.h
+++ b/src/gui/widgets/textbox.h
@@ -23,6 +23,8 @@
#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
@@ -38,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.
*/
@@ -51,13 +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;
};
diff --git a/src/gui/widgets/textfield.cpp b/src/gui/widgets/textfield.cpp
index 9f35a5dd..6c866477 100644
--- a/src/gui/widgets/textfield.cpp
+++ b/src/gui/widgets/textfield.cpp
@@ -114,7 +114,7 @@ int TextField::getValue() const
void TextField::drawCaret(gcn::Graphics *graphics, int x)
{
- graphics->setColor(getForegroundColor());
+ graphics->setColor(Theme::getThemeColor(Theme::CARET));
graphics->drawLine(mPadding + x, mPadding, mPadding + x, getHeight() - mPadding);
}
diff --git a/src/gui/widgets/textpreview.cpp b/src/gui/widgets/textpreview.cpp
index f9e85052..aed04853 100644
--- a/src/gui/widgets/textpreview.cpp
+++ b/src/gui/widgets/textpreview.cpp
@@ -21,57 +21,22 @@
#include "gui/widgets/textpreview.h"
-#include "configuration.h"
-#include "textrenderer.h"
-
#include "gui/gui.h"
-#include "gui/truetypefont.h"
-
-#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)
+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),
- mFont, mOutline, mShadow);
+ auto g = static_cast<Graphics*>(graphics);
+ g->drawText(mText, 2, 2, gcn::Graphics::LEFT,
+ 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 da24b61e..8246a200 100644
--- a/src/gui/widgets/textpreview.h
+++ b/src/gui/widgets/textpreview.h
@@ -44,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.
@@ -111,29 +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;
};
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/window.cpp b/src/gui/widgets/window.cpp
index 61ed7896..14d91af1 100644
--- a/src/gui/widgets/window.cpp
+++ b/src/gui/widgets/window.cpp
@@ -23,7 +23,6 @@
#include "configuration.h"
#include "log.h"
-#include "textrenderer.h"
#include "gui/gui.h"
#include "gui/viewport.h"
@@ -32,8 +31,6 @@
#include "gui/widgets/resizegrip.h"
#include "gui/widgets/windowcontainer.h"
-#include "resources/theme.h"
-
#include <guichan/exception.hpp>
#include <guichan/focushandler.hpp>
@@ -44,20 +41,25 @@ int Window::instances = 0;
int Window::mouseResize = 0;
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());
+ Log::debug("Window::Window(\"%s\")", caption.c_str());
if (!windowContainer)
throw GCN_EXCEPTION("Window::Window(): no windowContainer set");
instances++;
- auto &skin = gui->getTheme()->getSkin(SkinType::Window);
+ auto &skin = getSkin();
setFrameSize(skin.frameSize);
setPadding(skin.padding);
setTitleBarHeight(skin.titleBarHeight);
@@ -79,7 +81,7 @@ Window::Window(const std::string &caption, bool modal, Window *parent)
Window::~Window()
{
- logger->log("Window::~Window(\"%s\")", getCaption().c_str());
+ Log::debug("Window::~Window(\"%s\")", getCaption().c_str());
saveWindowState();
@@ -123,13 +125,12 @@ void Window::draw(gcn::Graphics *graphics)
void Window::drawFrame(gcn::Graphics *graphics)
{
auto g = static_cast<Graphics*>(graphics);
- auto theme = gui->getTheme();
WidgetState widgetState(this);
widgetState.width += getFrameSize() * 2;
widgetState.height += getFrameSize() * 2;
- auto &skin = theme->getSkin(SkinType::Window);
+ auto &skin = getSkin();
skin.draw(g, widgetState);
if (mShowTitle)
@@ -137,13 +138,12 @@ void Window::drawFrame(gcn::Graphics *graphics)
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);
+ g->drawText(getCaption(),
+ getFrameSize() + skin.titleOffsetX,
+ getFrameSize() + skin.titleOffsetY,
+ gcn::Graphics::LEFT,
+ textFormat.bold ? boldFont : getFont(),
+ textFormat);
}
}
}
@@ -169,7 +169,7 @@ void Window::setMinimumContentSize(int width, int height)
{
const int padding = getPadding();
const int titleBarHeight = getTitleBarHeight();
- auto &skin = gui->getTheme()->getSkin(SkinType::Window);
+ auto &skin = getSkin();
setMinWidth(std::max(skin.getMinWidth(), width + 2 * padding));
setMinHeight(std::max(skin.getMinHeight(), height + padding + titleBarHeight));
@@ -189,60 +189,14 @@ void Window::setLocationRelativeTo(gcn::Widget *widget)
getY() + (wy + (widget->getHeight() - getHeight()) / 2 - y));
}
-void Window::setLocationRelativeTo(ImageRect::ImagePosition position,
- int offsetX, int offsetY)
-{
- if (position == ImageRect::UPPER_LEFT)
- {
- }
- else if (position == ImageRect::UPPER_CENTER)
- {
- offsetX += (graphics->getWidth() - getWidth()) / 2;
- }
- else if (position == ImageRect::UPPER_RIGHT)
- {
- offsetX += graphics->getWidth() - getWidth();
- }
- else if (position == ImageRect::LEFT)
- {
- offsetY += (graphics->getHeight() - getHeight()) / 2;
- }
- else if (position == ImageRect::CENTER)
- {
- offsetX += (graphics->getWidth() - getWidth()) / 2;
- offsetY += (graphics->getHeight() - getHeight()) / 2;
- }
- else if (position == ImageRect::RIGHT)
- {
- offsetX += graphics->getWidth() - getWidth();
- offsetY += (graphics->getHeight() - getHeight()) / 2;
- }
- else if (position == ImageRect::LOWER_LEFT)
- {
- offsetY += graphics->getHeight() - getHeight();
- }
- else if (position == ImageRect::LOWER_CENTER)
- {
- offsetX += (graphics->getWidth() - getWidth()) / 2;
- offsetY += graphics->getHeight() - getHeight();
- }
- else if (position == ImageRect::LOWER_RIGHT)
- {
- offsetX += graphics->getWidth() - getWidth();
- offsetY += graphics->getHeight() - getHeight();
- }
-
- setPosition(offsetX, offsetY);
-}
-
void Window::setMinWidth(int width)
{
- mMinWinWidth = std::max(gui->getTheme()->getMinWidth(SkinType::Window), width);
+ mMinWinWidth = std::max(getSkin().getMinWidth(), width);
}
void Window::setMinHeight(int height)
{
- mMinWinHeight = std::max(gui->getTheme()->getMinHeight(SkinType::Window), height);
+ mMinWinHeight = std::max(getSkin().getMinHeight(), height);
}
void Window::setMaxWidth(int width)
@@ -375,6 +329,11 @@ void Window::close()
setVisible(false);
}
+const Skin &Window::getSkin() const
+{
+ return gui->getTheme()->getSkin(mSkinType);
+}
+
void Window::mouseReleased(gcn::MouseEvent &event)
{
mouseResize = 0;
@@ -592,41 +551,41 @@ void Window::setDefaultSize()
}
void Window::setDefaultSize(int defaultWidth, int defaultHeight,
- ImageRect::ImagePosition position,
+ WindowAlignment alignment,
int offsetX, int offsetY)
{
int x = 0;
int y = 0;
- switch (position)
+ switch (alignment)
{
- case ImageRect::UPPER_LEFT:
+ case WindowAlignment::TopLeft:
break;
- case ImageRect::UPPER_CENTER:
+ case WindowAlignment::Top:
x = (graphics->getWidth() - defaultWidth) / 2;
break;
- case ImageRect::UPPER_RIGHT:
+ case WindowAlignment::TopRight:
x = graphics->getWidth() - defaultWidth;
break;
- case ImageRect::LEFT:
+ case WindowAlignment::Left:
y = (graphics->getHeight() - defaultHeight) / 2;
break;
- case ImageRect::CENTER:
+ case WindowAlignment::Center:
x = (graphics->getWidth() - defaultWidth) / 2;
y = (graphics->getHeight() - defaultHeight) / 2;
break;
- case ImageRect::RIGHT:
+ case WindowAlignment::Right:
x = graphics->getWidth() - defaultWidth;
y = (graphics->getHeight() - defaultHeight) / 2;
break;
- case ImageRect::LOWER_LEFT:
+ case WindowAlignment::BottomLeft:
y = graphics->getHeight() - defaultHeight;
break;
- case ImageRect::LOWER_CENTER:
+ case WindowAlignment::Bottom:
x = (graphics->getWidth() - defaultWidth) / 2;
y = graphics->getHeight() - defaultHeight;
break;
- case ImageRect::LOWER_RIGHT:
+ case WindowAlignment::BottomRight:
x = graphics->getWidth() - defaultWidth;
y = graphics->getHeight() - defaultHeight;
break;
@@ -707,15 +666,15 @@ gcn::Rectangle Window::getCloseButtonRect() const
auto theme = gui->getTheme();
- auto &closeButtonSkin = theme->getSkin(SkinType::ButtonClose);
- const int closeButtonWidth = closeButtonSkin.getMinWidth();
- const int closeButtonHeight = closeButtonSkin.getMinHeight();
+ auto &closeSkin = theme->getSkin(SkinType::ButtonClose);
+ const int closeWidth = closeSkin.getMinWidth();
+ const int closeHeight = closeSkin.getMinHeight();
return {
- getWidth() - closeButtonWidth - closeButtonSkin.padding,
- closeButtonSkin.padding,
- closeButtonWidth,
- closeButtonHeight
+ getWidth() - closeWidth - closeSkin.padding,
+ closeSkin.padding,
+ closeWidth,
+ closeHeight
};
}
@@ -726,22 +685,22 @@ gcn::Rectangle Window::getStickyButtonRect() const
auto theme = gui->getTheme();
- auto &closeButtonSkin = theme->getSkin(SkinType::ButtonClose);
- const int closeButtonWidth = closeButtonSkin.getMinWidth();
+ auto &closeSkin = theme->getSkin(SkinType::ButtonClose);
+ const int closeWidth = closeSkin.getMinWidth();
- auto &stickyButtonSkin = theme->getSkin(SkinType::ButtonSticky);
- const int stickyButtonWidth = stickyButtonSkin.getMinWidth();
- const int stickyButtonHeight = stickyButtonSkin.getMinHeight();
+ auto &stickySkin = theme->getSkin(SkinType::ButtonSticky);
+ const int stickyWidth = stickySkin.getMinWidth();
+ const int stickyHeight = stickySkin.getMinHeight();
- int stickyButtonX = getWidth() - stickyButtonWidth - stickyButtonSkin.padding;
+ int stickyX = getWidth() - stickyWidth - stickySkin.padding - stickySkin.spacing;
if (mCloseButton)
- stickyButtonX -= closeButtonWidth + closeButtonSkin.padding;
+ stickyX -= closeWidth + closeSkin.padding;
return {
- stickyButtonX,
- stickyButtonSkin.padding,
- stickyButtonWidth,
- stickyButtonHeight
+ stickyX,
+ stickySkin.padding,
+ stickyWidth,
+ stickyHeight
};
}
diff --git a/src/gui/widgets/window.h b/src/gui/widgets/window.h
index bf459a32..2a47b0b9 100644
--- a/src/gui/widgets/window.h
+++ b/src/gui/widgets/window.h
@@ -24,8 +24,9 @@
#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;
@@ -35,6 +36,19 @@ class ResizeGrip;
class Skin;
class WindowContainer;
+enum class WindowAlignment
+{
+ TopLeft,
+ Top,
+ TopRight,
+ Left,
+ Center,
+ Right,
+ BottomLeft,
+ Bottom,
+ BottomRight
+};
+
/**
* A window. This window can be dragged around and has a title bar. Windows are
* invisible by default.
@@ -54,7 +68,16 @@ class Window : public gcn::Window, gcn::WidgetListener
* this one in the window hiearchy. When reordering,
* a window will never go below its parent window.
*/
- Window(const std::string &caption = "Window", bool modal = false,
+ 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);
/**
@@ -93,12 +116,6 @@ class Window : public gcn::Window, gcn::WidgetListener
void setLocationRelativeTo(gcn::Widget *widget);
/**
- * Sets the location relative to the given enumerated position.
- */
- void setLocationRelativeTo(ImageRect::ImagePosition position,
- int offsetX = 0, int offsetY = 0);
-
- /**
* Sets whether or not the window can be resized.
*/
void setResizable(bool resize);
@@ -293,7 +310,7 @@ class Window : public gcn::Window, gcn::WidgetListener
* on a relative enumerated position, rather than a coordinate position.
*/
void setDefaultSize(int defaultWidth, int defaultHeight,
- ImageRect::ImagePosition position,
+ WindowAlignment alignment,
int offsetx = 0, int offsetY = 0);
/**
@@ -353,6 +370,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();
@@ -400,6 +422,7 @@ class Window : public gcn::Window, gcn::WidgetListener
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 */
diff --git a/src/gui/windowmenu.cpp b/src/gui/windowmenu.cpp
index 0b2d126f..2c1b6211 100644
--- a/src/gui/windowmenu.cpp
+++ b/src/gui/windowmenu.cpp
@@ -23,9 +23,10 @@
#include "graphics.h"
+#include "gui/abilitieswindow.h"
#include "gui/emotepopup.h"
+#include "gui/questswindow.h"
#include "gui/skilldialog.h"
-#include "gui/abilitieswindow.h"
#include "gui/widgets/button.h"
#include "gui/widgets/window.h"
@@ -34,6 +35,8 @@
#include "net/net.h"
#include "net/playerhandler.h"
+#include "resources/questdb.h"
+
#include "utils/gettext.h"
#include <string>
@@ -64,6 +67,9 @@ WindowMenu::WindowMenu()
if (abilitiesWindow->hasAbilities())
addButton(N_("Abilities"), x, h, "button-icon-abilities.png");
+ if (QuestDB::hasQuests())
+ addButton(N_("Quests"), x, h, "button-icon-quests.png");
+
addButton(N_("Social"), x, h, "button-icon-social.png",
KeyboardConfig::KEY_WINDOW_SOCIAL);
addButton(N_("Shortcuts"), x, h, "button-icon-shortcut.png",
@@ -122,6 +128,10 @@ void WindowMenu::action(const gcn::ActionEvent &event)
{
window = skillDialog;
}
+ else if (event.getId() == "Quests")
+ {
+ window = questsWindow;
+ }
else if (event.getId() == "Abilities")
{
window = abilitiesWindow;
@@ -166,12 +176,18 @@ static std::string createShortcutCaption(const std::string &text,
KeyboardConfig::KeyAction key)
{
std::string caption = gettext(text.c_str());
+
if (key != KeyboardConfig::KEY_NO_VALUE)
{
- caption += " (";
- caption += SDL_GetKeyName(keyboard.getKeyValue(key));
- caption += ")";
+ auto keyValue = keyboard.getKeyValue(key);
+ if (keyValue > 0)
+ {
+ caption += " (";
+ caption += SDL_GetKeyName(keyValue);
+ caption += ")";
+ }
}
+
return caption;
}
@@ -179,7 +195,7 @@ void WindowMenu::addButton(const std::string &text, int &x, int &h,
const std::string &iconPath,
KeyboardConfig::KeyAction key)
{
- auto *btn = new Button("", text, this);
+ auto *btn = new Button(std::string(), text, this);
if (!iconPath.empty() && btn->setButtonIcon(iconPath))
{
btn->setButtonPopupText(createShortcutCaption(text, key));
@@ -187,7 +203,7 @@ void WindowMenu::addButton(const std::string &text, int &x, int &h,
else
{
btn->setCaption(gettext(text.c_str()));
- btn->setButtonPopupText(createShortcutCaption("", key));
+ btn->setButtonPopupText(createShortcutCaption(std::string(), key));
}
btn->setPosition(x, 0);
@@ -204,40 +220,45 @@ void WindowMenu::updatePopUpCaptions()
if (!button)
continue;
- std::string eventId = button->getActionEventId();
+ const std::string &eventId = button->getActionEventId();
if (eventId == "Status")
{
- button->setButtonPopupText(createShortcutCaption("Status",
+ button->setButtonPopupText(createShortcutCaption(eventId,
KeyboardConfig::KEY_WINDOW_STATUS));
}
else if (eventId == "Equipment")
{
- button->setButtonPopupText(createShortcutCaption("Equipment",
+ button->setButtonPopupText(createShortcutCaption(eventId,
KeyboardConfig::KEY_WINDOW_EQUIPMENT));
}
else if (eventId == "Inventory")
{
- button->setButtonPopupText(createShortcutCaption("Inventory",
+ button->setButtonPopupText(createShortcutCaption(eventId,
KeyboardConfig::KEY_WINDOW_INVENTORY));
}
else if (eventId == "Skills")
{
- button->setButtonPopupText(createShortcutCaption("Skills",
+ button->setButtonPopupText(createShortcutCaption(eventId,
KeyboardConfig::KEY_WINDOW_SKILL));
}
+ else if (eventId == "Quests")
+ {
+ button->setButtonPopupText(
+ createShortcutCaption(eventId, KeyboardConfig::KEY_WINDOW_QUESTS));
+ }
else if (eventId == "Social")
{
- button->setButtonPopupText(createShortcutCaption("Social",
+ button->setButtonPopupText(createShortcutCaption(eventId,
KeyboardConfig::KEY_WINDOW_SOCIAL));
}
else if (eventId == "Shortcuts")
{
- button->setButtonPopupText(createShortcutCaption("Shortcuts",
+ button->setButtonPopupText(createShortcutCaption(eventId,
KeyboardConfig::KEY_WINDOW_SHORTCUT));
}
else if (eventId == "Setup")
{
- button->setButtonPopupText(createShortcutCaption("Setup",
+ button->setButtonPopupText(createShortcutCaption(eventId,
KeyboardConfig::KEY_WINDOW_SETUP));
}
}