summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThorbjørn Lindeijer <bjorn@lindeijer.nl>2025-04-08 21:40:38 +0200
committerThorbjørn Lindeijer <bjorn@lindeijer.nl>2025-04-25 14:05:30 +0000
commit6536aff2d24a48a4e91d31eb35971e4b788ec3b0 (patch)
treef2263d04695ac1ec18ee60f38233eeea7dc767f1
parent1c83a844551eadfb4db80258b9b3b55d97bbfe54 (diff)
downloadmana-6536aff2d24a48a4e91d31eb35971e4b788ec3b0.tar.gz
mana-6536aff2d24a48a4e91d31eb35971e4b788ec3b0.tar.bz2
mana-6536aff2d24a48a4e91d31eb35971e4b788ec3b0.tar.xz
mana-6536aff2d24a48a4e91d31eb35971e4b788ec3b0.zip
GUI: Allow theme to skin item slots and equipment boxes
* Added EquipmentBox and ItemSlot skin types. * Allowed rectangles drawn by the theme to be not filled. * Added width/height attributes to skin element since we needed a way to specify the size of the item slots and equipment boxes.
-rw-r--r--data/graphics/gui/jewelry/theme.xml19
-rw-r--r--data/graphics/gui/theme.xml22
-rw-r--r--src/gui/equipmentwindow.cpp53
-rw-r--r--src/gui/widgets/dropdown.cpp7
-rw-r--r--src/gui/widgets/itemcontainer.cpp83
-rw-r--r--src/gui/widgets/itemcontainer.h3
-rw-r--r--src/gui/widgets/itemshortcutcontainer.cpp6
-rw-r--r--src/gui/widgets/popup.cpp6
-rw-r--r--src/gui/widgets/resizegrip.cpp2
-rw-r--r--src/gui/widgets/shortcutcontainer.cpp4
-rw-r--r--src/gui/widgets/window.cpp6
-rw-r--r--src/resources/theme.cpp34
-rw-r--r--src/resources/theme.h8
13 files changed, 141 insertions, 112 deletions
diff --git a/data/graphics/gui/jewelry/theme.xml b/data/graphics/gui/jewelry/theme.xml
index c90c59e9..29e421ac 100644
--- a/data/graphics/gui/jewelry/theme.xml
+++ b/data/graphics/gui/jewelry/theme.xml
@@ -129,7 +129,7 @@
</state>
</skin>
- <skin type="ResizeGrip" padding="2">
+ <skin type="ResizeGrip" width="17" height="17">
<state>
<img src="window.png" x="328" y="89" width="15" height="15" />
</state>
@@ -363,9 +363,24 @@
</state>
</skin>
- <skin type="ShortcutBox">
+ <skin type="ShortcutBox" width="36" height="42">
<state>
<img src="window.png" x="58" y="75" width="36" height="42" />
</state>
</skin>
+
+ <skin type="EquipmentBox" padding="3" width="38" height="38">
+ <state selected="true">
+ <img src="window.png" x="175" y="75" width="38" height="38" />
+ </state>
+ <state>
+ <img src="window.png" x="136" y="75" width="38" height="38" />
+ </state>
+ </skin>
+
+ <skin type="ItemSlot" padding="3" width="38" height="54">
+ <state selected="true">
+ <img src="window.png" x="175" y="114" width="38" height="38" />
+ </state>
+ </skin>
</theme>
diff --git a/data/graphics/gui/theme.xml b/data/graphics/gui/theme.xml
index 1b790569..df388380 100644
--- a/data/graphics/gui/theme.xml
+++ b/data/graphics/gui/theme.xml
@@ -57,9 +57,9 @@
</state>
</skin>
- <skin type="ResizeGrip" padding="3">
+ <skin type="ResizeGrip" width="15" height="13">
<state>
- <img src="resize.png" />
+ <img src="resize.png" offsetX="-2" offsetY="-1" />
</state>
</skin>
@@ -278,9 +278,25 @@
</state>
</skin>
- <skin type="ShortcutBox">
+ <skin type="ShortcutBox" width="36" height="42">
<state>
<img src="item_shortcut_bgr.png" />
</state>
</skin>
+
+ <skin type="EquipmentBox" padding="2" width="36" height="36">
+ <state selected="true">
+ <rect color="#c0c0c0" />
+ <rect color="#000000" fill="false" />
+ </state>
+ <state>
+ <rect color="#000000" fill="false" />
+ </state>
+ </skin>
+
+ <skin type="ItemSlot" width="35" height="45">
+ <state selected="true">
+ <img src="selection.png" />
+ </state>
+ </skin>
</theme>
diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp
index ff3c6630..4d99c482 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)
@@ -113,51 +110,43 @@ void EquipmentWindow::draw(gcn::Graphics *graphics)
// Draw equipment boxes
auto *g = static_cast<Graphics*>(graphics);
+ auto &boxSkin = gui->getTheme()->getSkin(SkinType::EquipmentBox);
+
for (size_t i = 0; i < mBoxes.size(); i++)
{
const auto &box = mBoxes[i];
+ WidgetState boxState(gcn::Rectangle(box.posX, box.posY, boxSkin.width, boxSkin.height));
+ if (static_cast<int>(i) == mSelected)
+ boxState.flags |= STATE_SELECTED;
+
+ boxSkin.draw(g, boxState);
+
// 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;
+ + (boxSkin.width - box.backgroundImage->getWidth()) / 2;
int posY = box.posY
- + (BOX_HEIGHT - box.backgroundImage->getHeight()) / 2;
+ + (boxSkin.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);
-
- // Set color to the highlight color
- g->setColor(gcn::Color(color.r, color.g, color.b, getGuiAlpha()));
- g->fillRectangle(tRect);
- }
-
- // Draw black box border
- g->setColor(gcn::Color(0, 0, 0));
- g->drawRectangle(tRect);
-
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,
+ box.posX + boxSkin.padding,
+ box.posY + 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.posX + (boxSkin.width / 2),
box.posY - getFont()->getHeight(),
gcn::Graphics::CENTER);
}
@@ -180,12 +169,12 @@ void EquipmentWindow::action(const gcn::ActionEvent &event)
*/
int EquipmentWindow::getBoxIndex(int x, int y) const
{
+ auto &boxSkin = gui->getTheme()->getSkin(SkinType::EquipmentBox);
+
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);
-
+ const gcn::Rectangle tRect(box.posX, box.posY, boxSkin.width, boxSkin.height);
if (tRect.isPointInRect(x, y))
return i;
}
diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp
index 45c8e53f..cea50b1d 100644
--- a/src/gui/widgets/dropdown.cpp
+++ b/src/gui/widgets/dropdown.cpp
@@ -147,13 +147,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/itemcontainer.cpp b/src/gui/widgets/itemcontainer.cpp
index d1d00677..57e8a973 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->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));
}
}
@@ -375,25 +375,30 @@ 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
{
+ auto &slotSkin = gui->getTheme()->getSkin(SkinType::ItemSlot);
+
if (x < getWidth() && y < getHeight())
- {
- return (y / BOX_HEIGHT) * mGridColumns + (x / BOX_WIDTH);
- }
+ return (y / slotSkin.height) * mGridColumns + (x / slotSkin.width);
+
return Inventory::NO_SLOT_INDEX;
}
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/itemshortcutcontainer.cpp b/src/gui/widgets/itemshortcutcontainer.cpp
index 4d0641b1..b7924a4b 100644
--- a/src/gui/widgets/itemshortcutcontainer.cpp
+++ b/src/gui/widgets/itemshortcutcontainer.cpp
@@ -74,9 +74,7 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics)
if (item)
{
// Draw item icon.
- Image* image = item->getImage();
-
- if (image)
+ if (Image *image = item->getImage())
{
std::string caption;
if (item->getQuantity() > 1)
@@ -97,7 +95,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);
diff --git a/src/gui/widgets/popup.cpp b/src/gui/widgets/popup.cpp
index b7c70fe5..4095c5c3 100644
--- a/src/gui/widgets/popup.cpp
+++ b/src/gui/widgets/popup.cpp
@@ -119,12 +119,14 @@ void Popup::setLocationRelativeTo(gcn::Widget *widget)
void Popup::setMinWidth(int width)
{
- mMinWidth = std::max(gui->getTheme()->getMinWidth(mSkinType), width);
+ auto &skin = gui->getTheme()->getSkin(mSkinType);
+ mMinWidth = std::max(skin.getMinWidth(), width);
}
void Popup::setMinHeight(int height)
{
- mMinHeight = std::max(gui->getTheme()->getMinHeight(mSkinType), height);
+ auto &skin = gui->getTheme()->getSkin(mSkinType);
+ mMinHeight = std::max(skin.getMinHeight(), height);
}
void Popup::setMaxWidth(int width)
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/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/window.cpp b/src/gui/widgets/window.cpp
index 94262f47..ea8bc064 100644
--- a/src/gui/widgets/window.cpp
+++ b/src/gui/widgets/window.cpp
@@ -240,12 +240,14 @@ void Window::setLocationRelativeTo(ImageRect::ImagePosition position,
void Window::setMinWidth(int width)
{
- mMinWinWidth = std::max(gui->getTheme()->getMinWidth(mSkinType), width);
+ auto &skin = gui->getTheme()->getSkin(mSkinType);
+ mMinWinWidth = std::max(skin.getMinWidth(), width);
}
void Window::setMinHeight(int height)
{
- mMinWinHeight = std::max(gui->getTheme()->getMinHeight(mSkinType), height);
+ auto &skin = gui->getTheme()->getSkin(mSkinType);
+ mMinWinHeight = std::max(skin.getMinHeight(), height);
}
void Window::setMaxWidth(int width)
diff --git a/src/resources/theme.cpp b/src/resources/theme.cpp
index ca39bbea..489fd5b3 100644
--- a/src/resources/theme.cpp
+++ b/src/resources/theme.cpp
@@ -132,12 +132,21 @@ void Skin::draw(Graphics *graphics, const WidgetState &state) const
}
else if constexpr (std::is_same_v<T, ColoredRectangle>)
{
+ const auto color = graphics->getColor();
+ // TODO: Take GUI alpha into account
graphics->setColor(data.color);
- graphics->fillRectangle(gcn::Rectangle(state.x + part.offsetX,
- state.y + part.offsetY,
- state.width,
- state.height));
- graphics->setColor(gcn::Color(255, 255, 255));
+
+ const gcn::Rectangle rect(state.x + part.offsetX,
+ state.y + part.offsetY,
+ state.width,
+ state.height);
+
+ if (data.filled)
+ graphics->fillRectangle(rect);
+ else
+ graphics->drawRectangle(rect);
+
+ graphics->setColor(color);
}
}, part.data);
}
@@ -350,16 +359,6 @@ const Skin &Theme::getSkin(SkinType skinType) const
return it != mSkins.end() ? it->second : emptySkin;
}
-int Theme::getMinWidth(SkinType skinType) const
-{
- return getSkin(skinType).getMinWidth();
-}
-
-int Theme::getMinHeight(SkinType skinType) const
-{
- return getSkin(skinType).getMinHeight();
-}
-
void Theme::setMinimumOpacity(float minimumOpacity)
{
if (minimumOpacity > 1.0f)
@@ -460,6 +459,8 @@ static std::optional<SkinType> readSkinType(std::string_view type)
if (type == "SliderHandle") return SkinType::SliderHandle;
if (type == "ResizeGrip") return SkinType::ResizeGrip;
if (type == "ShortcutBox") return SkinType::ShortcutBox;
+ if (type == "EquipmentBox") return SkinType::EquipmentBox;
+ if (type == "ItemSlot") return SkinType::ItemSlot;
return {};
}
@@ -472,6 +473,8 @@ void Theme::readSkinNode(XML::Node node)
auto &skin = mSkins[*skinType];
+ node.attribute("width", skin.width);
+ node.attribute("height", skin.height);
node.attribute("frameSize", skin.frameSize);
node.attribute("padding", skin.padding);
node.attribute("spacing", skin.spacing);
@@ -645,6 +648,7 @@ void Theme::readSkinStateRectNode(XML::Node node, SkinState &state) const
node.attribute("color", rect.color);
node.attribute("alpha", rect.color.a);
+ node.attribute("fill", rect.filled);
}
static int readColorType(const std::string &type)
diff --git a/src/resources/theme.h b/src/resources/theme.h
index 43057a39..5c662cd7 100644
--- a/src/resources/theme.h
+++ b/src/resources/theme.h
@@ -73,6 +73,8 @@ enum class SkinType
SliderHandle,
ResizeGrip,
ShortcutBox,
+ EquipmentBox,
+ ItemSlot,
};
enum StateFlags : uint8_t
@@ -86,6 +88,7 @@ enum StateFlags : uint8_t
struct ColoredRectangle
{
gcn::Color color;
+ bool filled = true;
};
struct SkinPart
@@ -151,6 +154,8 @@ class Skin
*/
void updateAlpha(float alpha);
+ int width = 0;
+ int height = 0;
int frameSize = 0;
int padding = 0;
int spacing = 0;
@@ -252,9 +257,6 @@ class Theme : public Palette, public EventListener
const Skin &getSkin(SkinType skinType) const;
- int getMinWidth(SkinType skinType) const;
- int getMinHeight(SkinType skinType) const;
-
/**
* Get the current GUI alpha value.
*/