diff options
author | Thorbjørn Lindeijer <bjorn@lindeijer.nl> | 2025-04-08 21:40:38 +0200 |
---|---|---|
committer | Thorbjørn Lindeijer <bjorn@lindeijer.nl> | 2025-04-25 14:05:30 +0000 |
commit | 6536aff2d24a48a4e91d31eb35971e4b788ec3b0 (patch) | |
tree | f2263d04695ac1ec18ee60f38233eeea7dc767f1 | |
parent | 1c83a844551eadfb4db80258b9b3b55d97bbfe54 (diff) | |
download | mana-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.xml | 19 | ||||
-rw-r--r-- | data/graphics/gui/theme.xml | 22 | ||||
-rw-r--r-- | src/gui/equipmentwindow.cpp | 53 | ||||
-rw-r--r-- | src/gui/widgets/dropdown.cpp | 7 | ||||
-rw-r--r-- | src/gui/widgets/itemcontainer.cpp | 83 | ||||
-rw-r--r-- | src/gui/widgets/itemcontainer.h | 3 | ||||
-rw-r--r-- | src/gui/widgets/itemshortcutcontainer.cpp | 6 | ||||
-rw-r--r-- | src/gui/widgets/popup.cpp | 6 | ||||
-rw-r--r-- | src/gui/widgets/resizegrip.cpp | 2 | ||||
-rw-r--r-- | src/gui/widgets/shortcutcontainer.cpp | 4 | ||||
-rw-r--r-- | src/gui/widgets/window.cpp | 6 | ||||
-rw-r--r-- | src/resources/theme.cpp | 34 | ||||
-rw-r--r-- | src/resources/theme.h | 8 |
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. */ |