diff options
Diffstat (limited to 'src/gui/widgets')
78 files changed, 1820 insertions, 2280 deletions
diff --git a/src/gui/widgets/avatarlistbox.cpp b/src/gui/widgets/avatarlistbox.cpp index ec3327b2..4a806d04 100644 --- a/src/gui/widgets/avatarlistbox.cpp +++ b/src/gui/widgets/avatarlistbox.cpp @@ -32,33 +32,21 @@ #include <guichan/font.hpp> -int AvatarListBox::instances = 0; -Image *AvatarListBox::onlineIcon = nullptr; -Image *AvatarListBox::offlineIcon = nullptr; - AvatarListBox::AvatarListBox(AvatarListModel *model): ListBox(model) { - instances++; - - if (instances == 1) - { - onlineIcon = Theme::getImageFromTheme("circle-green.png"); - offlineIcon = Theme::getImageFromTheme("circle-gray.png"); - } - setWidth(200); } -AvatarListBox::~AvatarListBox() +unsigned int AvatarListBox::getRowHeight() const { - instances--; + auto rowHeight = ListBox::getRowHeight(); - if (instances == 0) - { - onlineIcon->decRef(); - offlineIcon->decRef(); - } + auto theme = gui->getTheme(); + if (auto onlineIcon = theme->getIcon("online")) + rowHeight = std::max<unsigned>(rowHeight, onlineIcon->getHeight() + 2); + + return rowHeight; } void AvatarListBox::draw(gcn::Graphics *gcnGraphics) @@ -66,34 +54,42 @@ void AvatarListBox::draw(gcn::Graphics *gcnGraphics) if (!mListModel) return; - auto* model = static_cast<AvatarListModel*>(mListModel); + auto *model = static_cast<AvatarListModel *>(mListModel); + auto *graphics = static_cast<Graphics *>(gcnGraphics); - updateAlpha(); - - auto *graphics = static_cast<Graphics*>(gcnGraphics); - - graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, - (int) (mAlpha * 255.0f))); graphics->setFont(getFont()); - const int fontHeight = getFont()->getHeight(); + const int rowHeight = getRowHeight(); // Draw filled rectangle around the selected list element if (mSelected >= 0) - graphics->fillRectangle(gcn::Rectangle(0, fontHeight * mSelected, - getWidth(), fontHeight)); + { + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + highlightColor.a = gui->getTheme()->getGuiAlpha(); + graphics->setColor(highlightColor); + graphics->fillRectangle(gcn::Rectangle(0, rowHeight * mSelected, + getWidth(), rowHeight)); + } + + auto theme = gui->getTheme(); + auto onlineIcon = theme->getIcon("online"); + auto offlineIcon = theme->getIcon("offline"); // Draw the list elements graphics->setColor(Theme::getThemeColor(Theme::TEXT)); for (int i = 0, y = 0; i < model->getNumberOfElements(); - ++i, y += fontHeight) + ++i, y += rowHeight) { Avatar *a = model->getAvatarAt(i); + int x = 1; + // Draw online status - Image *icon = a->getOnline() ? onlineIcon : offlineIcon; - if (icon) - graphics->drawImage(icon, 2, y + 1); + if (const Image *icon = a->getOnline() ? onlineIcon : offlineIcon) + { + graphics->drawImage(icon, x, y + (rowHeight - icon->getHeight()) / 2); + x += icon->getWidth() + 4; + } if (a->getDisplayBold()) graphics->setFont(boldFont); @@ -111,7 +107,7 @@ void AvatarListBox::draw(gcn::Graphics *gcnGraphics) } // Draw Name - graphics->drawText(text, 15, y); + graphics->drawText(text, x, y); if (a->getDisplayBold()) graphics->setFont(getFont()); diff --git a/src/gui/widgets/avatarlistbox.h b/src/gui/widgets/avatarlistbox.h index 7ee36d1e..1f51935f 100644 --- a/src/gui/widgets/avatarlistbox.h +++ b/src/gui/widgets/avatarlistbox.h @@ -18,8 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_GUILDLISTBOX_H -#define GUI_GUILDLISTBOX_H +#pragma once #include "avatar.h" @@ -43,7 +42,7 @@ class AvatarListBox : public ListBox public: AvatarListBox(AvatarListModel *model); - ~AvatarListBox() override; + unsigned int getRowHeight() const override; /** * Draws the list box. @@ -51,11 +50,4 @@ public: void draw(gcn::Graphics *gcnGraphics) override; void mousePressed(gcn::MouseEvent &event) override; - -private: - static int instances; - static Image *onlineIcon; - static Image *offlineIcon; }; - -#endif diff --git a/src/gui/widgets/browserbox.cpp b/src/gui/widgets/browserbox.cpp index 9eee9448..4fe85ae5 100644 --- a/src/gui/widgets/browserbox.cpp +++ b/src/gui/widgets/browserbox.cpp @@ -22,6 +22,9 @@ #include "gui/widgets/browserbox.h" +#include "keyboardconfig.h" +#include "textrenderer.h" + #include "gui/gui.h" #include "gui/truetypefont.h" #include "gui/widgets/linkhandler.h" @@ -38,9 +41,44 @@ #include <algorithm> +/** + * Check for key replacements in format "###key;" + */ +static void replaceKeys(std::string &text) +{ + auto keyStart = text.find("###"); + + while (keyStart != std::string::npos) + { + const auto keyEnd = text.find(";", keyStart + 3); + if (keyEnd == std::string::npos) + break; + + std::string_view key(text.data() + keyStart + 3, keyEnd - keyStart - 3); + + // Remove "key" prefix + if (key.size() > 3 && key.substr(0, 3) == "key") + key.remove_prefix(3); + + const auto keyName = keyboard.getKeyName(key); + if (!keyName.empty()) + { + text.replace(keyStart, keyEnd - keyStart + 1, keyName); + keyStart = text.find("###", keyStart + keyName.size()); + } + else + { + keyStart = text.find("###", keyEnd + 1); + } + } +} + + struct LayoutContext { - LayoutContext(gcn::Font *font); + LayoutContext(gcn::Font *font, const Palette &palette); + + LinePart linePart(int x, std::string text); int y = 0; gcn::Font *font; @@ -48,23 +86,39 @@ struct LayoutContext const int minusWidth; const int tildeWidth; int lineHeight; - gcn::Color selColor; const gcn::Color textColor; + const std::optional<gcn::Color> textOutlineColor; + gcn::Color color; + std::optional<gcn::Color> outlineColor; }; -LayoutContext::LayoutContext(gcn::Font *font) +inline LayoutContext::LayoutContext(gcn::Font *font, const Palette &palette) : font(font) , fontHeight(font->getHeight()) , minusWidth(font->getWidth("-")) , tildeWidth(font->getWidth("~")) , lineHeight(fontHeight) - , selColor(Theme::getThemeColor(Theme::TEXT)) - , textColor(Theme::getThemeColor(Theme::TEXT)) + , textColor(palette.getColor(Theme::TEXT)) + , textOutlineColor(palette.getOutlineColor(Theme::TEXT)) + , color(textColor) + , outlineColor(textOutlineColor) { if (auto *trueTypeFont = dynamic_cast<const TrueTypeFont*>(font)) lineHeight = trueTypeFont->getLineHeight(); } +inline LinePart LayoutContext::linePart(int x, std::string text) +{ + return { + x, + y, + color, + outlineColor, + std::move(text), + font + }; +} + BrowserBox::BrowserBox(Mode mode): mMode(mode) @@ -75,28 +129,38 @@ BrowserBox::BrowserBox(Mode mode): BrowserBox::~BrowserBox() = default; -void BrowserBox::addRow(const std::string &row) +void BrowserBox::addRows(std::string_view rows) +{ + std::string_view::size_type start = 0; + std::string_view::size_type end = 0; + while (end != std::string::npos) + { + end = rows.find('\n', start); + addRow(rows.substr(start, end - start)); + start = end + 1; + } +} + +void BrowserBox::addRow(std::string_view row) { TextRow &newRow = mTextRows.emplace_back(); // Use links and user defined colors if (mUseLinksAndUserColors) { - std::string tmp = row; - // Check for links in format "@@link|Caption@@" - auto idx1 = tmp.find("@@"); - while (idx1 != std::string::npos) + auto linkStart = row.find("@@"); + while (linkStart != std::string::npos) { - const auto idx2 = tmp.find("|", idx1); - const auto idx3 = tmp.find("@@", idx2); + const auto linkSep = row.find("|", linkStart); + const auto linkEnd = row.find("@@", linkSep); - if (idx2 == std::string::npos || idx3 == std::string::npos) + if (linkSep == std::string::npos || linkEnd == std::string::npos) break; BrowserLink &link = newRow.links.emplace_back(); - link.link = tmp.substr(idx1 + 2, idx2 - (idx1 + 2)); - link.caption = tmp.substr(idx2 + 1, idx3 - (idx2 + 1)); + link.link = row.substr(linkStart + 2, linkSep - (linkStart + 2)); + link.caption = row.substr(linkSep + 1, linkEnd - (linkSep + 1)); if (link.caption.empty()) { @@ -107,18 +171,18 @@ void BrowserBox::addRow(const std::string &row) link.caption = link.link; } - newRow.text += tmp.substr(0, idx1); + newRow.text += row.substr(0, linkStart); newRow.text += "##<" + link.caption; - tmp.erase(0, idx3 + 2); - if (!tmp.empty()) + row = row.substr(linkEnd + 2); + if (!row.empty()) { newRow.text += "##>"; } - idx1 = tmp.find("@@"); + linkStart = row.find("@@"); } - newRow.text += tmp; + newRow.text += row; } // Don't use links and user defined colors else @@ -126,8 +190,11 @@ void BrowserBox::addRow(const std::string &row) newRow.text = row; } + if (mEnableKeys) + replaceKeys(newRow.text); + // Layout the newly added row - LayoutContext context(getFont()); + LayoutContext context(getFont(), gui->getTheme()->getPalette(mPalette)); context.y = getHeight(); layoutTextRow(newRow, context); @@ -175,14 +242,14 @@ void BrowserBox::mousePressed(gcn::MouseEvent &event) if (mHoveredLink) { mLinkHandler->handleLink(mHoveredLink->link); - gui->setCursorType(Cursor::POINTER); + gui->setCursorType(Cursor::Pointer); } } void BrowserBox::mouseMoved(gcn::MouseEvent &event) { updateHoveredLink(event.getX(), event.getY()); - gui->setCursorType(mHoveredLink ? Cursor::HAND : Cursor::POINTER); + gui->setCursorType(mHoveredLink ? Cursor::Hand : Cursor::Pointer); event.consume(); // Suppress mouse cursor change by parent } @@ -204,19 +271,20 @@ void BrowserBox::draw(gcn::Graphics *graphics) if (mHoveredLink) { + auto &palette = gui->getTheme()->getPalette(mPalette); auto &link = *mHoveredLink; const gcn::Rectangle &rect = link.rect; if (mHighlightMode & BACKGROUND) { - graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT)); + graphics->setColor(palette.getColor(Theme::HIGHLIGHT)); graphics->fillRectangle(rect); } if (mHighlightMode & UNDERLINE) { - graphics->setColor(Theme::getThemeColor(Theme::HYPERLINK)); + graphics->setColor(palette.getColor(Theme::HYPERLINK)); graphics->drawLine(rect.x, rect.y + rect.height, rect.x + rect.width, @@ -233,34 +301,16 @@ void BrowserBox::draw(gcn::Graphics *graphics) if (part.y > yEnd) return; - auto font = part.font; - - // Handle text shadows - if (mShadows) - { - graphics->setColor(Theme::getThemeColor(Theme::SHADOW, - part.color.a / 2)); - - if (mOutline) - font->drawString(graphics, part.text, part.x + 2, part.y + 2); - else - font->drawString(graphics, part.text, part.x + 1, part.y + 1); - } - - if (mOutline) - { - // Text outline - graphics->setColor(Theme::getThemeColor(Theme::OUTLINE, - part.color.a / 4)); - font->drawString(graphics, part.text, part.x + 1, part.y); - font->drawString(graphics, part.text, part.x - 1, part.y); - font->drawString(graphics, part.text, part.x, part.y + 1); - font->drawString(graphics, part.text, part.x, part.y - 1); - } - - // the main text - graphics->setColor(part.color); - font->drawString(graphics, part.text, part.x, part.y); + TextRenderer::renderText(graphics, + part.text, + part.x, + part.y, + Graphics::LEFT, + part.color, + part.font, + part.outlineColor.has_value() || mOutline, + mShadows, + part.outlineColor); } } } @@ -270,13 +320,13 @@ void BrowserBox::draw(gcn::Graphics *graphics) */ void BrowserBox::relayoutText() { - LayoutContext context(getFont()); + LayoutContext context(getFont(), gui->getTheme()->getPalette(mPalette)); for (auto &row : mTextRows) layoutTextRow(row, context); mLastLayoutWidth = getWidth(); - mLayoutTimer.set(100); + mLayoutTimer.set(33); setHeight(context.y); } @@ -288,7 +338,8 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) { // each line starts with normal font in default color context.font = getFont(); - context.selColor = context.textColor; + context.color = context.textColor; + context.outlineColor = context.textOutlineColor; const int startY = context.y; row.parts.clear(); @@ -301,15 +352,7 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) if (startsWith(row.text, "---")) { for (x = 0; x < getWidth(); x += context.minusWidth - 1) - { - row.parts.push_back(LinePart { - x, - context.y, - context.selColor, - "-", - context.font - }); - } + row.parts.push_back(context.linePart(x, "-")); context.y += row.height; @@ -318,7 +361,9 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) return; } - gcn::Color prevColor = context.selColor; + auto &palette = gui->getTheme()->getPalette(mPalette); + auto prevColor = context.color; + auto prevOutlineColor = context.outlineColor; // TODO: Check if we must take texture size limits into account here // TODO: Check if some of the O(n) calls can be removed @@ -342,44 +387,38 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) const char c = row.text.at(start + 2); start += 3; - bool valid; - const gcn::Color &col = Theme::getThemeColor(c, valid); - - if (c == '>') - { - context.selColor = prevColor; - } - else if (c == '<') + switch (c) { - prevColor = context.selColor; - context.selColor = col; - } - else if (c == 'B') - { - context.font = boldFont; - } - else if (c == 'b') - { - context.font = getFont(); - } - else if (valid) - { - context.selColor = col; - } - else switch (c) - { - case '1': context.selColor = RED; break; - case '2': context.selColor = GREEN; break; - case '3': context.selColor = BLUE; break; - case '4': context.selColor = ORANGE; break; - case '5': context.selColor = YELLOW; break; - case '6': context.selColor = PINK; break; - case '7': context.selColor = PURPLE; break; - case '8': context.selColor = GRAY; break; - case '9': context.selColor = BROWN; break; - case '0': - default: - context.selColor = context.textColor; + case '>': + context.color = prevColor; + context.outlineColor = prevOutlineColor; + break; + case '<': + prevColor = context.color; + prevOutlineColor = context.outlineColor; + context.color = palette.getColor(Theme::HYPERLINK); + context.outlineColor = palette.getOutlineColor(Theme::HYPERLINK); + break; + case 'B': + context.font = boldFont; + break; + case 'b': + context.font = getFont(); + break; + default: { + const auto colorId = Theme::getColorIdForChar(c); + if (colorId) + { + context.color = palette.getColor(*colorId); + context.outlineColor = palette.getOutlineColor(*colorId); + } + else + { + context.color = context.textColor; + context.outlineColor = context.textOutlineColor; + } + break; + } } // Update the position of the links @@ -452,7 +491,8 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) row.parts.push_back(LinePart { getWidth() - context.tildeWidth, context.y, - context.selColor, + context.color, + context.outlineColor, "~", getFont() }); @@ -466,14 +506,7 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) wrapped = true; } - row.parts.push_back(LinePart { - x, - context.y, - context.selColor, - std::move(part), - context.font - }); - + row.parts.push_back(context.linePart(x, std::move(part))); row.width = std::max(row.width, x + partWidth); if (mMode == AUTO_WRAP && partWidth == 0) @@ -506,7 +539,7 @@ void BrowserBox::updateHoveredLink(int x, int y) void BrowserBox::maybeRelayoutText() { // Reduce relayouting frequency when there is a lot of text - if (mTextRows.size() > 100) + if (mTextRows.size() > 1000) if (!mLayoutTimer.passed()) return; diff --git a/src/gui/widgets/browserbox.h b/src/gui/widgets/browserbox.h index 7278bb59..142bba63 100644 --- a/src/gui/widgets/browserbox.h +++ b/src/gui/widgets/browserbox.h @@ -20,8 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef BROWSERBOX_H -#define BROWSERBOX_H +#pragma once #include "utils/time.h" @@ -52,6 +51,7 @@ struct LinePart int x; int y; gcn::Color color; + std::optional<gcn::Color> outlineColor; std::string text; gcn::Font *font; }; @@ -87,6 +87,8 @@ class BrowserBox : public gcn::Widget, */ void setLinkHandler(LinkHandler *handler) { mLinkHandler = handler; } + void setPalette(int palette) { mPalette = palette; } + /** * Sets the Highlight mode for links. */ @@ -118,9 +120,19 @@ class BrowserBox : public gcn::Widget, void disableLinksAndUserColors() { mUseLinksAndUserColors = false; } /** + * Enable or disable the replacement of keys. + */ + void setEnableKeys(bool enable) { mEnableKeys = enable; } + + /** + * Adds one or more text rows to the browser, separated by '\n'. + */ + void addRows(std::string_view rows); + + /** * Adds a text row to the browser. */ - void addRow(const std::string &row); + void addRow(std::string_view row); /** * Remove all rows. @@ -145,29 +157,6 @@ class BrowserBox : public gcn::Widget, void drawFrame(gcn::Graphics *) override {} /** - * BrowserBox colors. - * - * NOTES (by Javila): - * - color values is "0x" prefix followed by HTML color style. - * - we can add up to 10 different colors: [0..9]. - * - not all colors will be fine with all backgrounds due transparent - * windows and widgets. So, I think it's better keep BrowserBox - * opaque (white background) by default. - */ - enum - { - RED = 0xff0000, /**< Color 1 */ - GREEN = 0x009000, /**< Color 2 */ - BLUE = 0x0000ff, /**< Color 3 */ - ORANGE = 0xe0980e, /**< Color 4 */ - YELLOW = 0xf1dc27, /**< Color 5 */ - PINK = 0xff00d8, /**< Color 6 */ - PURPLE = 0x8415e2, /**< Color 7 */ - GRAY = 0x919191, /**< Color 8 */ - BROWN = 0x8e4c17 /**< Color 9 */ - }; - - /** * Highlight modes for links. * This can be used for a bitmask. */ @@ -186,16 +175,16 @@ class BrowserBox : public gcn::Widget, std::deque<TextRow> mTextRows; LinkHandler *mLinkHandler = nullptr; + int mPalette = 0; Mode mMode; unsigned int mHighlightMode = UNDERLINE | BACKGROUND; int mWrapIndent = 0; bool mShadows = false; bool mOutline = false; bool mUseLinksAndUserColors = true; + bool mEnableKeys = false; std::optional<BrowserLink> mHoveredLink; unsigned int mMaxRows = 0; int mLastLayoutWidth = 0; Timer mLayoutTimer; }; - -#endif diff --git a/src/gui/widgets/button.cpp b/src/gui/widgets/button.cpp index 274f329b..31c3a677 100644 --- a/src/gui/widgets/button.cpp +++ b/src/gui/widgets/button.cpp @@ -21,25 +21,22 @@ #include "gui/widgets/button.h" -#include "configuration.h" #include "graphics.h" +#include "gui/gui.h" #include "gui/textpopup.h" #include "resources/image.h" #include "resources/theme.h" - -#include "utils/dtor.h" +#include "textrenderer.h" #include <guichan/exception.hpp> #include <guichan/font.hpp> int Button::mInstances = 0; -float Button::mAlpha = 1.0; -ImageRect *Button::mButton; TextPopup *Button::mTextPopup = nullptr; -enum{ +enum { BUTTON_STANDARD, // 0 BUTTON_HIGHLIGHTED, // 1 BUTTON_PRESSED, // 2 @@ -47,20 +44,6 @@ enum{ BUTTON_COUNT // 4 - Must be last. }; -struct ButtonData -{ - char const *file; - int gridX; - int gridY; -}; - -static ButtonData const data[BUTTON_COUNT] = { - { "button.png", 0, 0 }, - { "buttonhi.png", 9, 4 }, - { "buttonpress.png", 16, 19 }, - { "button_disabled.png", 25, 23 } -}; - Button::Button() { init(); @@ -80,18 +63,19 @@ Button::Button(const std::string &caption, const std::string &actionEventId, adjustSize(); } -bool Button::setButtonIcon(const std::string& iconFile) +Button::~Button() = default; + +bool Button::setButtonIcon(const std::string &iconFile) { // We clean up possible older references. - if (mButtonIcon) - removeButtonIcon(); + removeButtonIcon(); // If nothing relevant was set, we can quit now. if (iconFile.empty()) return false; // Load the icon frames. - Image *btnIcons = Theme::getImageFromTheme(iconFile); + auto btnIcons = Theme::getImageFromTheme(iconFile); if (!btnIcons) return false; @@ -101,141 +85,76 @@ bool Button::setButtonIcon(const std::string& iconFile) if (frameWidth > 0 && frameHeight > 0) { - mButtonIcon = new Image*[BUTTON_COUNT]; + mButtonIcon.resize(BUTTON_COUNT); + for (int mode = 0; mode < BUTTON_COUNT; ++mode) { - mButtonIcon[mode] = btnIcons->getSubImage(mode * frameWidth, 0, - frameWidth, frameHeight); + mButtonIcon[mode].reset( + btnIcons->getSubImage(mode * frameWidth, 0, frameWidth, frameHeight)); } adjustSize(); } - btnIcons->decRef(); - return (mButtonIcon); + return !mButtonIcon.empty(); } -void Button::removeButtonIcon(bool adjustButtonSize) +void Button::removeButtonIcon() { - if (!mButtonIcon) + if (mButtonIcon.empty()) return; - // Delete potential button icons - for (int mode = 0; mode < BUTTON_COUNT; ++mode) - { - delete mButtonIcon[mode]; - mButtonIcon[mode] = nullptr; - } - delete[] mButtonIcon; - mButtonIcon = nullptr; - - if (adjustButtonSize) - adjustSize(); + mButtonIcon.clear(); + adjustSize(); } void Button::init() { - setFrameSize(0); + auto &skin = gui->getTheme()->getSkin(SkinType::Button); + setFrameSize(skin.frameSize); + setSpacing(skin.padding); if (mInstances == 0) { - // Load the skin - mButton = new ImageRect[BUTTON_COUNT]; - - for (int mode = 0; mode < BUTTON_COUNT; ++mode) - { - Image *modeImage = Theme::getImageFromTheme(data[mode].file); - int a = 0; - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - mButton[mode].grid[a] = modeImage->getSubImage( - data[x].gridX, data[y].gridY, - data[x + 1].gridX - data[x].gridX + 1, - data[y + 1].gridY - data[y].gridY + 1); - a++; - } - } - modeImage->decRef(); - } - updateAlpha(); - - // Load the popup + // Create the tooltip popup. It is shared by all buttons and will get + // deleted by the WindowContainer. if (!mTextPopup) - mTextPopup = new TextPopup(); + mTextPopup = new TextPopup; } mInstances++; } -Button::~Button() +void Button::draw(gcn::Graphics *graphics) { - mInstances--; - - if (mInstances == 0) - { - for (int mode = 0; mode < BUTTON_COUNT; ++mode) - { - std::for_each(mButton[mode].grid, mButton[mode].grid + 9, - dtor<Image*>()); - } - delete[] mButton; + WidgetState widgetState(this); + if (mHasMouse) + widgetState.flags |= STATE_HOVERED; + if (isPressed()) + widgetState.flags |= STATE_SELECTED; - // Remove the popup - delete mTextPopup; - mTextPopup = nullptr; - } - // Don' try to readjust the size when it's about to be deleted. - removeButtonIcon(false); -} + auto &skin = gui->getTheme()->getSkin(SkinType::Button); + skin.draw(static_cast<Graphics *>(graphics), widgetState); -void Button::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); + auto skinState = skin.getState(widgetState.flags); + auto font = (skinState && skinState->textFormat.bold) ? boldFont : getFont(); - if (mAlpha != alpha) - { - mAlpha = alpha; - for (int mode = 0; mode < BUTTON_COUNT; ++mode) - { - mButton[mode].setAlpha(mAlpha); - } - } -} - -void Button::draw(gcn::Graphics *graphics) -{ int mode; - if (!isEnabled()) + if (widgetState.flags & STATE_DISABLED) mode = BUTTON_DISABLED; - else if (isPressed()) + else if (widgetState.flags & STATE_SELECTED) mode = BUTTON_PRESSED; - else if (mHasMouse || isFocused()) + else if (widgetState.flags & (STATE_HOVERED | STATE_FOCUSED)) mode = BUTTON_HIGHLIGHTED; else mode = BUTTON_STANDARD; - updateAlpha(); - - static_cast<Graphics*>(graphics)-> - drawImageRect(0, 0, getWidth(), getHeight(), mButton[mode]); - - if (mode == BUTTON_DISABLED) - graphics->setColor(Theme::getThemeColor(Theme::BUTTON_DISABLED)); - else - graphics->setColor(Theme::getThemeColor(Theme::BUTTON)); - + Image *icon = mButtonIcon.empty() ? nullptr : mButtonIcon[mode].get(); int textX = 0; - int textY = getHeight() / 2 - getFont()->getHeight() / 2; + int textY = getHeight() / 2 - font->getHeight() / 2; int btnIconX = 0; - int btnIconY = getHeight() / 2 - - ((mButtonIcon && mButtonIcon[mode]) ? - mButtonIcon[mode]->getHeight() / 2 : 0); - - int btnIconWidth = (mButtonIcon && mButtonIcon[mode]) ? - mButtonIcon[mode]->getWidth() : 0; + int btnIconY = getHeight() / 2 - (icon ? icon->getHeight() / 2 : 0); + int btnIconWidth = icon ? icon->getWidth() : 0; switch (getAlignment()) { @@ -243,7 +162,7 @@ void Button::draw(gcn::Graphics *graphics) if (btnIconWidth) { btnIconX = 4; - textX = btnIconX + mButtonIcon[mode]->getWidth() + 2; + textX = btnIconX + icon->getWidth() + 2; } else { @@ -253,9 +172,8 @@ void Button::draw(gcn::Graphics *graphics) case gcn::Graphics::CENTER: if (btnIconWidth) { - btnIconX = getWidth() / 2 - (getFont()->getWidth(mCaption) - + mButtonIcon[mode]->getWidth() + 2) / 2; - textX = getWidth() / 2 + mButtonIcon[mode]->getWidth() / 2 + 2; + btnIconX = (getWidth() - font->getWidth(mCaption) - icon->getWidth() - 2) / 2; + textX = (getWidth() + icon->getWidth()) / 2 + 2; } else { @@ -264,15 +182,13 @@ void Button::draw(gcn::Graphics *graphics) break; case gcn::Graphics::RIGHT: if (btnIconWidth) - btnIconX = getWidth() - 4 - getFont()->getWidth(mCaption) - 2; + btnIconX = getWidth() - 4 - font->getWidth(mCaption) - 2; textX = getWidth() - 4; break; default: throw GCN_EXCEPTION("Button::draw(). Unknown alignment."); } - graphics->setFont(getFont()); - if (isPressed()) { textX++; textY++; @@ -280,23 +196,32 @@ void Button::draw(gcn::Graphics *graphics) } if (btnIconWidth) - static_cast<Graphics*>(graphics)->drawImage(mButtonIcon[mode], - btnIconX, btnIconY); - graphics->drawText(getCaption(), textX, textY, getAlignment()); + static_cast<Graphics *>(graphics)->drawImage(icon, btnIconX, btnIconY); + + if (auto skinState = skin.getState(widgetState.flags)) + { + auto &textFormat = skinState->textFormat; + TextRenderer::renderText(static_cast<Graphics *>(graphics), + getCaption(), + textX, + textY, + getAlignment(), + font, + textFormat); + } } void Button::adjustSize() { // Size of the image button. int iconWidth = 0, iconHeight = 0; - if (mButtonIcon) + if (!mButtonIcon.empty()) { for (int mode = 0; mode < BUTTON_COUNT; ++mode) { - iconWidth = std::max(iconWidth, mButtonIcon[mode] ? - mButtonIcon[mode]->getWidth() + 2 : 0); - iconHeight = std::max(iconHeight, mButtonIcon[mode] ? - mButtonIcon[mode]->getHeight() : 0); + const Image *icon = mButtonIcon[mode].get(); + iconWidth = std::max(iconWidth, icon->getWidth() + 2); + iconHeight = std::max(iconHeight, icon->getHeight()); } } @@ -311,16 +236,9 @@ void Button::setCaption(const std::string& caption) adjustSize(); } -void Button::logic() -{ - gcn::Button::logic(); - mTextPopup->logic(); -} - void Button::mouseMoved(gcn::MouseEvent &event) { gcn::Button::mouseMoved(event); - mTextPopup->mouseMoved(event); int x = event.getX(); int y = event.getY(); @@ -344,7 +262,6 @@ void Button::mouseMoved(gcn::MouseEvent &event) void Button::mouseExited(gcn::MouseEvent &event) { gcn::Button::mouseExited(event); - mTextPopup->mouseExited(event); mTextPopup->setVisible(false); } diff --git a/src/gui/widgets/button.h b/src/gui/widgets/button.h index a09b4445..97f7307b 100644 --- a/src/gui/widgets/button.h +++ b/src/gui/widgets/button.h @@ -19,12 +19,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef BUTTON_H -#define BUTTON_H +#pragma once #include <guichan/widgets/button.hpp> -class ImageRect; +#include <memory> +#include <vector> + class Image; class TextPopup; @@ -55,11 +56,6 @@ class Button : public gcn::Button */ void draw(gcn::Graphics *graphics) override; - /** - * Update the alpha value to the button components. - */ - void updateAlpha(); - void adjustSize(); void setCaption(const std::string &caption); @@ -82,27 +78,22 @@ class Button : public gcn::Button void setButtonPopupText(const std::string &text) { mPopupText = text; } - void logic() override; void mouseMoved(gcn::MouseEvent &event) override; void mouseExited(gcn::MouseEvent &event) override; private: void init(); - void removeButtonIcon(bool adjustButtonSize = true); + void removeButtonIcon(); - static ImageRect* mButton; /**< Button state graphics */ static int mInstances; /**< Number of button instances */ - static float mAlpha; - Image** mButtonIcon = nullptr; /**< Button Icons graphics */ + std::vector<std::unique_ptr<Image>> mButtonIcon; /**< Button Icons graphics */ /** * The buttons popup * @note: This is a global object. One for all the buttons. */ - static TextPopup* mTextPopup; + static TextPopup *mTextPopup; std::string mPopupText; /**< the current button text */ }; - -#endif diff --git a/src/gui/widgets/channeltab.h b/src/gui/widgets/channeltab.h index 2894dacd..d6dc1268 100644 --- a/src/gui/widgets/channeltab.h +++ b/src/gui/widgets/channeltab.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CHANNELTAB_H -#define CHANNELTAB_H +#pragma once #include "chattab.h" @@ -51,5 +50,3 @@ class ChannelTab : public ChatTab private: Channel *mChannel; }; - -#endif // CHANNELTAB_H diff --git a/src/gui/widgets/chattab.cpp b/src/gui/widgets/chattab.cpp index 485de566..03c3270a 100644 --- a/src/gui/widgets/chattab.cpp +++ b/src/gui/widgets/chattab.cpp @@ -160,7 +160,7 @@ void ChatTab::chatLog(std::string line, Own own, bool ignoreRecord) tmp.nick = strprintf(_("Global announcement from %s:"), tmp.nick.c_str()); tmp.nick += " "; - lineColor = "##1"; // Equiv. to BrowserBox::RED + lineColor = "##g"; } break; case BY_PLAYER: diff --git a/src/gui/widgets/chattab.h b/src/gui/widgets/chattab.h index 3e770fe1..dfc07638 100644 --- a/src/gui/widgets/chattab.h +++ b/src/gui/widgets/chattab.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CHATTAB_H -#define CHATTAB_H +#pragma once #include "gui/chatwindow.h" @@ -135,5 +134,3 @@ class ChatTab : public Tab, public AutoCompleteLister, public EventListener }; extern ChatTab *localChatTab; - -#endif // CHATTAB_H diff --git a/src/gui/widgets/checkbox.cpp b/src/gui/widgets/checkbox.cpp index 274855fd..e6079f2f 100644 --- a/src/gui/widgets/checkbox.cpp +++ b/src/gui/widgets/checkbox.cpp @@ -21,121 +21,43 @@ #include "gui/widgets/checkbox.h" -#include "configuration.h" -#include "graphics.h" +#include "textrenderer.h" -#include "resources/image.h" +#include "gui/gui.h" #include "resources/theme.h" -int CheckBox::instances = 0; -float CheckBox::mAlpha = 1.0; -Image *CheckBox::checkBoxNormal; -Image *CheckBox::checkBoxChecked; -Image *CheckBox::checkBoxDisabled; -Image *CheckBox::checkBoxDisabledChecked; -Image *CheckBox::checkBoxNormalHi; -Image *CheckBox::checkBoxCheckedHi; +#include <guichan/font.hpp> -CheckBox::CheckBox(const std::string &caption, bool selected): - gcn::CheckBox(caption, selected) +CheckBox::CheckBox(const std::string &caption, bool selected) + : gcn::CheckBox(caption, selected) { - if (instances == 0) - { - Image *checkBox = Theme::getImageFromTheme("checkbox.png"); - checkBoxNormal = checkBox->getSubImage(0, 0, 9, 10); - checkBoxChecked = checkBox->getSubImage(9, 0, 9, 10); - checkBoxDisabled = checkBox->getSubImage(18, 0, 9, 10); - checkBoxDisabledChecked = checkBox->getSubImage(27, 0, 9, 10); - checkBoxNormalHi = checkBox->getSubImage(36, 0, 9, 10); - checkBoxCheckedHi = checkBox->getSubImage(45, 0, 9, 10); - checkBoxNormal->setAlpha(mAlpha); - checkBoxChecked->setAlpha(mAlpha); - checkBoxDisabled->setAlpha(mAlpha); - checkBoxDisabledChecked->setAlpha(mAlpha); - checkBoxNormalHi->setAlpha(mAlpha); - checkBoxCheckedHi->setAlpha(mAlpha); - checkBox->decRef(); - } - - instances++; -} - -CheckBox::~CheckBox() -{ - instances--; - - if (instances == 0) - { - delete checkBoxNormal; - delete checkBoxChecked; - delete checkBoxDisabled; - delete checkBoxDisabledChecked; - delete checkBoxNormalHi; - delete checkBoxCheckedHi; - } + auto &skin = gui->getTheme()->getSkin(SkinType::CheckBox); + setWidth(skin.getMinWidth() + 2 * skin.padding + skin.spacing + getFont()->getWidth(caption)); + setHeight(skin.getMinHeight() + 2 * skin.padding); } void CheckBox::draw(gcn::Graphics* graphics) { - drawBox(graphics); - - graphics->setFont(getFont()); - graphics->setColor(Theme::getThemeColor(Theme::TEXT)); - - const int h = getHeight() + getHeight() / 2; - - graphics->drawText(getCaption(), h - 2, 0); -} - -void CheckBox::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (mAlpha != alpha) - { - mAlpha = alpha; - checkBoxNormal->setAlpha(mAlpha); - checkBoxChecked->setAlpha(mAlpha); - checkBoxDisabled->setAlpha(mAlpha); - checkBoxDisabledChecked->setAlpha(mAlpha); - checkBoxNormal->setAlpha(mAlpha); - checkBoxCheckedHi->setAlpha(mAlpha); - } -} + WidgetState widgetState(this); + if (mHasMouse) + widgetState.flags |= STATE_HOVERED; + if (isSelected()) + widgetState.flags |= STATE_SELECTED; -void CheckBox::drawBox(gcn::Graphics* graphics) -{ - Image *box; + auto &skin = gui->getTheme()->getSkin(SkinType::CheckBox); + skin.draw(static_cast<Graphics *>(graphics), widgetState); - if (isEnabled()) - { - if (isSelected()) - { - if (mHasMouse) - box = checkBoxCheckedHi; - else - box = checkBoxChecked; - } - else - { - if (mHasMouse) - box = checkBoxNormalHi; - else - box = checkBoxNormal; - } - } - else + if (auto skinState = skin.getState(widgetState.flags)) { - if (isSelected()) - box = checkBoxDisabledChecked; - else - box = checkBoxDisabled; + auto &textFormat = skinState->textFormat; + TextRenderer::renderText(static_cast<Graphics *>(graphics), + getCaption(), + skin.getMinWidth() + skin.padding + skin.spacing, + skin.padding, + Graphics::LEFT, + textFormat.bold ? boldFont : getFont(), + textFormat); } - - updateAlpha(); - - static_cast<Graphics*>(graphics)->drawImage(box, 2, 2); } void CheckBox::mouseEntered(gcn::MouseEvent& event) diff --git a/src/gui/widgets/checkbox.h b/src/gui/widgets/checkbox.h index f77b1761..ea1a20e7 100644 --- a/src/gui/widgets/checkbox.h +++ b/src/gui/widgets/checkbox.h @@ -19,13 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CHECKBOX_H -#define CHECKBOX_H +#pragma once #include <guichan/widgets/checkbox.hpp> -class Image; - /** * Check box widget. Same as the Guichan check box but with custom look. * @@ -36,43 +33,26 @@ class CheckBox : public gcn::CheckBox public: CheckBox(const std::string &caption, bool selected = false); - ~CheckBox() override; - /** * Draws the caption, then calls drawBox to draw the check box. */ - void draw(gcn::Graphics* graphics) override; - - /** - * Update the alpha value to the checkbox components. - */ - void updateAlpha(); + void draw(gcn::Graphics *graphics) override; /** - * Draws the check box, not the caption. + * Overridden because box is drawn in CheckBox::draw. */ - void drawBox(gcn::Graphics* graphics) override; + void drawBox(gcn::Graphics *graphics) override {} /** * Called when the mouse enteres the widget area. */ - void mouseEntered(gcn::MouseEvent& event) override; + void mouseEntered(gcn::MouseEvent &event) override; /** * Called when the mouse leaves the widget area. */ - void mouseExited(gcn::MouseEvent& event) override; + void mouseExited(gcn::MouseEvent &event) override; private: - static int instances; - static float mAlpha; bool mHasMouse = false; - static Image *checkBoxNormal; - static Image *checkBoxChecked; - static Image *checkBoxDisabled; - static Image *checkBoxDisabledChecked; - static Image *checkBoxNormalHi; - static Image *checkBoxCheckedHi; }; - -#endif diff --git a/src/gui/widgets/container.h b/src/gui/widgets/container.h index ef44c8cd..54b7950b 100644 --- a/src/gui/widgets/container.h +++ b/src/gui/widgets/container.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_CONTAINER_H -#define GUI_CONTAINER_H +#pragma once #include <guichan/widgets/container.hpp> @@ -33,7 +32,7 @@ class LayoutHelper; * A widget container. * * The main difference between the standard Guichan container and this one is - * that childs added to this container are automatically deleted when the + * that children added to this container are automatically deleted when the * container is deleted. * * This container is also non-opaque by default. @@ -44,6 +43,9 @@ class Container : public gcn::Container Container(); ~Container() override; + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics *graphics) override {} + protected: /** * Gets the layout handler for this container. @@ -63,5 +65,3 @@ class Container : public gcn::Container private: LayoutHelper *mLayoutHelper = nullptr; }; - -#endif diff --git a/src/gui/widgets/desktop.cpp b/src/gui/widgets/desktop.cpp index c8ded9f5..e424beec 100644 --- a/src/gui/widgets/desktop.cpp +++ b/src/gui/widgets/desktop.cpp @@ -105,7 +105,7 @@ void Desktop::setBestFittingWallpaper() return; ResourceManager *resman = ResourceManager::getInstance(); - auto wallpaper = resman->getImageRef(wallpaperName); + auto wallpaper = resman->getImage(wallpaperName); if (wallpaper) { diff --git a/src/gui/widgets/desktop.h b/src/gui/widgets/desktop.h index 5909ac72..a7aa4a1e 100644 --- a/src/gui/widgets/desktop.h +++ b/src/gui/widgets/desktop.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef DESKTOP_H -#define DESKTOP_H +#pragma once #include "guichanfwd.h" @@ -66,5 +65,3 @@ class Desktop : public Container, gcn::WidgetListener ResourceRef<Image> mWallpaper; gcn::Label *mVersionLabel; }; - -#endif // DESKTOP_H diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp index 8811eb8d..7d8ad0e5 100644 --- a/src/gui/widgets/dropdown.cpp +++ b/src/gui/widgets/dropdown.cpp @@ -21,127 +21,44 @@ #include "gui/widgets/dropdown.h" -#include "configuration.h" #include "graphics.h" +#include "gui/gui.h" #include "gui/sdlinput.h" #include "gui/widgets/listbox.h" #include "gui/widgets/scrollarea.h" -#include "resources/image.h" #include "resources/theme.h" -#include "utils/dtor.h" - -#include <algorithm> - -int DropDown::instances = 0; -Image *DropDown::buttons[2][2]; -ImageRect DropDown::skin; -float DropDown::mAlpha = 1.0; +#include <guichan/font.hpp> DropDown::DropDown(gcn::ListModel *listModel): gcn::DropDown::DropDown(listModel, new ScrollArea, new ListBox(listModel)) { - setFrameSize(2); - - // Initialize graphics - if (instances == 0) - { - // Load the background skin - - // Get the button skin - buttons[1][0] = Theme::getImageFromTheme("vscroll_up_default.png"); - buttons[0][0] = Theme::getImageFromTheme("vscroll_down_default.png"); - buttons[1][1] = Theme::getImageFromTheme("vscroll_up_pressed.png"); - buttons[0][1] = Theme::getImageFromTheme("vscroll_down_pressed.png"); - - buttons[0][0]->setAlpha(mAlpha); - buttons[0][1]->setAlpha(mAlpha); - buttons[1][0]->setAlpha(mAlpha); - buttons[1][1]->setAlpha(mAlpha); - - // get the border skin - Image *boxBorder = Theme::getImageFromTheme("deepbox.png"); - int gridx[4] = {0, 3, 28, 31}; - int gridy[4] = {0, 3, 28, 31}; - int a = 0; - - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - skin.grid[a] = boxBorder->getSubImage(gridx[x], gridy[y], - gridx[x + 1] - - gridx[x] + 1, - gridy[y + 1] - - gridy[y] + 1); - a++; - } - } + auto &skin = gui->getTheme()->getSkin(SkinType::DropDownFrame); + setFrameSize(skin.frameSize); + mPadding = skin.padding; - skin.setAlpha(mAlpha); - - boxBorder->decRef(); - } - - instances++; + setHeight(getFont()->getHeight() + 2 * mPadding); } DropDown::~DropDown() { - instances--; - // Free images memory - if (instances == 0) - { - buttons[0][0]->decRef(); - buttons[0][1]->decRef(); - buttons[1][0]->decRef(); - buttons[1][1]->decRef(); - - std::for_each(skin.grid, skin.grid + 9, dtor<Image*>()); - } - delete mScrollArea; } -void DropDown::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (mAlpha != alpha) - { - mAlpha = alpha; - - buttons[0][0]->setAlpha(mAlpha); - buttons[0][1]->setAlpha(mAlpha); - buttons[1][0]->setAlpha(mAlpha); - buttons[1][1]->setAlpha(mAlpha); - - skin.setAlpha(mAlpha); - } -} - void DropDown::draw(gcn::Graphics* graphics) { - int h; + const int h = mDroppedDown ? mFoldedUpHeight : getHeight(); - if (mDroppedDown) - h = mFoldedUpHeight; - else - h = getHeight(); - - updateAlpha(); - - const int alpha = (int) (mAlpha * 255.0f); + const int alpha = gui->getTheme()->getGuiAlpha(); gcn::Color faceColor = getBaseColor(); faceColor.a = alpha; - const gcn::Color *highlightColor = &Theme::getThemeColor(Theme::HIGHLIGHT, - alpha); + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + highlightColor.a = alpha; gcn::Color shadowColor = faceColor - 0x303030; shadowColor.a = alpha; @@ -149,13 +66,16 @@ void DropDown::draw(gcn::Graphics* graphics) { graphics->setFont(getFont()); graphics->setColor(Theme::getThemeColor(Theme::TEXT)); - graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()), 1, 0); + graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()), + mPadding, + mPadding); } if (isFocused()) { - graphics->setColor(*highlightColor); - graphics->drawRectangle(gcn::Rectangle(0, 0, getWidth() - h, h)); + graphics->setColor(highlightColor); + graphics->drawRectangle( + gcn::Rectangle(mPadding, mPadding, getWidth() - h - mPadding * 2, h - 2 * mPadding)); } drawButton(graphics); @@ -166,7 +86,7 @@ void DropDown::draw(gcn::Graphics* graphics) // Draw two lines separating the ListBox with selected // element view. - graphics->setColor(*highlightColor); + graphics->setColor(highlightColor); graphics->drawLine(0, h, getWidth(), h); graphics->setColor(shadowColor); graphics->drawLine(0, h + 1, getWidth(), h + 1); @@ -176,18 +96,64 @@ void DropDown::draw(gcn::Graphics* graphics) void DropDown::drawFrame(gcn::Graphics *graphics) { const int bs = getFrameSize(); - const int w = getWidth() + bs * 2; - const int h = getHeight() + bs * 2; - static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, skin); + WidgetState state(this); + state.width += bs * 2; + state.height += bs * 2; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::DropDownFrame, state); +} + +// Overridden so that we can take mPadding into account +void DropDown::adjustHeight() +{ + const int listBoxHeight = mListBox->getHeight(); + int height = getFont()->getHeight() + 2 * mPadding; + + // The addition/subtraction of 2 compensates for the seperation lines + // seperating the selected element view and the scroll area. + + if (mDroppedDown && getParent()) + { + int availableHeight = getParent()->getChildrenArea().height - getY(); + + if (listBoxHeight > availableHeight - height - 2) + { + mScrollArea->setHeight(availableHeight - height - 2); + height = availableHeight; + } + else + { + height += listBoxHeight + 2; + mScrollArea->setHeight(listBoxHeight); + } + } + + setHeight(height); + + mScrollArea->setWidth(getWidth()); + // Resize the ListBox to exactly fit the ScrollArea. + mListBox->setWidth(mScrollArea->getChildrenArea().width); + mScrollArea->setPosition(0, 0); } void DropDown::drawButton(gcn::Graphics *graphics) { - int height = mDroppedDown ? mFoldedUpHeight : getHeight(); + WidgetState state(this); + if (mDroppedDown) + { + state.height = mFoldedUpHeight; + state.flags |= STATE_SELECTED; + } + if (mPushed) + state.flags |= STATE_HOVERED; + + auto &skin = gui->getTheme()->getSkin(SkinType::DropDownButton); + + // FIXME: Needs support for setting alignment in the theme. + state.x = state.width - skin.getMinWidth(); - static_cast<Graphics*>(graphics)-> - drawImage(buttons[mDroppedDown][mPushed], getWidth() - height + 2, 1); + skin.draw(static_cast<Graphics *>(graphics), state); } // -- KeyListener notifications @@ -253,3 +219,32 @@ void DropDown::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent) mouseEvent.consume(); distributeActionEvent(); } + +// Overridden to call our version of adjustHeight +void DropDown::dropDown() +{ + if (!mDroppedDown) + { + mDroppedDown = true; + mFoldedUpHeight = getHeight(); + adjustHeight(); + + if (getParent()) + { + getParent()->moveToTop(this); + } + } + + mListBox->requestFocus(); +} + +// Overridden to call our version of adjustHeight +void DropDown::foldUp() +{ + if (mDroppedDown) + { + mDroppedDown = false; + adjustHeight(); + mInternalFocusHandler.focusNone(); + } +} diff --git a/src/gui/widgets/dropdown.h b/src/gui/widgets/dropdown.h index f92c7dd5..a5e2e2f7 100644 --- a/src/gui/widgets/dropdown.h +++ b/src/gui/widgets/dropdown.h @@ -19,14 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef DROPDOWN_H -#define DROPDOWN_H +#pragma once #include <guichan/widgets/dropdown.hpp> -class Image; -class ImageRect; - /** * A drop down box from which you can select different values. * @@ -47,15 +43,12 @@ class DropDown : public gcn::DropDown ~DropDown() override; - /** - * Update the alpha value to the graphic components. - */ - static void updateAlpha(); - void draw(gcn::Graphics *graphics) override; void drawFrame(gcn::Graphics *graphics) override; + void adjustHeight(); + // Inherited from FocusListener void focusLost(const gcn::Event& event) override; @@ -80,12 +73,8 @@ class DropDown : public gcn::DropDown */ void drawButton(gcn::Graphics *graphics) override; - // Add own Images. - static int instances; - static Image *buttons[2][2]; - static ImageRect skin; - static float mAlpha; -}; - -#endif // end DROPDOWN_H + void dropDown() override; + void foldUp() override; + int mPadding = 1; +}; diff --git a/src/gui/widgets/emoteshortcutcontainer.cpp b/src/gui/widgets/emoteshortcutcontainer.cpp index 8ecbc9bf..06d80ec2 100644 --- a/src/gui/widgets/emoteshortcutcontainer.cpp +++ b/src/gui/widgets/emoteshortcutcontainer.cpp @@ -21,13 +21,12 @@ #include "gui/widgets/emoteshortcutcontainer.h" -#include "configuration.h" #include "emoteshortcut.h" #include "graphics.h" -#include "imagesprite.h" -#include "item.h" #include "keyboardconfig.h" +#include "gui/gui.h" + #include "resources/emotedb.h" #include "resources/image.h" #include "resources/theme.h" @@ -36,65 +35,52 @@ static const int MAX_ITEMS = 12; EmoteShortcutContainer::EmoteShortcutContainer() { - addMouseListener(this); - addWidgetListener(this); - - mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png"); - - mBackgroundImg->setAlpha(config.guiAlpha); - mMaxItems = std::min(EmoteDB::getEmoteCount(), MAX_ITEMS); - - mBoxHeight = mBackgroundImg->getHeight(); - mBoxWidth = mBackgroundImg->getWidth(); -} - -EmoteShortcutContainer::~EmoteShortcutContainer() -{ - mBackgroundImg->decRef(); } void EmoteShortcutContainer::draw(gcn::Graphics *graphics) { - if (config.guiAlpha != mAlpha) - { - mAlpha = config.guiAlpha; - mBackgroundImg->setAlpha(mAlpha); - } - auto *g = static_cast<Graphics*>(graphics); + auto theme = gui->getTheme(); graphics->setFont(getFont()); for (int i = 0; i < mMaxItems; i++) { - const int emoteX = (i % mGridWidth) * mBoxWidth; - const int emoteY = (i / mGridWidth) * mBoxHeight; - - g->drawImage(mBackgroundImg, emoteX, emoteY); + WidgetState state; + state.x = (i % mGridWidth) * mBoxWidth; + state.y = (i / mGridWidth) * mBoxHeight; + theme->drawSkin(g, SkinType::ShortcutBox, state); // Draw emote keyboard shortcut. const char *key = SDL_GetKeyName( keyboard.getKeyValue(KeyboardConfig::KEY_EMOTE_1 + i)); graphics->setColor(Theme::getThemeColor(Theme::TEXT)); - g->drawText(key, emoteX + 2, emoteY + 2, gcn::Graphics::LEFT); + g->drawText(key, state.x + 2, state.y + 2, gcn::Graphics::LEFT); int emoteId = emoteShortcut->getEmote(i); if (emoteId != -1) { - EmoteDB::get(emoteId).sprite->draw(g, emoteX + 2, emoteY + 10); + if (auto image = EmoteDB::get(emoteId).image) + { + image->setAlpha(1.0f); + g->drawImage(image, state.x + 2, state.y + 10); + } } } if (mEmoteMoved != -1) { // Draw the emote image being dragged by the cursor. - const ImageSprite *sprite = EmoteDB::get(mEmoteMoved).sprite.get(); + if (auto image = EmoteDB::get(mEmoteMoved).image) + { + image->setAlpha(1.0f); - const int tPosX = mCursorPosX - (sprite->getWidth() / 2); - const int tPosY = mCursorPosY - (sprite->getHeight() / 2); + const int tPosX = mCursorPosX - (image->getWidth() / 2); + const int tPosY = mCursorPosY - (image->getHeight() / 2); - sprite->draw(g, tPosX, tPosY); + g->drawImage(image, tPosX, tPosY); + } } } @@ -115,6 +101,7 @@ void EmoteShortcutContainer::mouseDragged(gcn::MouseEvent &event) emoteShortcut->removeEmote(index); } } + if (mEmoteMoved != -1) { mCursorPosX = event.getX(); @@ -126,7 +113,6 @@ void EmoteShortcutContainer::mouseDragged(gcn::MouseEvent &event) void EmoteShortcutContainer::mousePressed(gcn::MouseEvent &event) { const int index = getIndexFromGrid(event.getX(), event.getY()); - if (index == -1) return; @@ -170,4 +156,3 @@ void EmoteShortcutContainer::mouseReleased(gcn::MouseEvent &event) mEmoteClicked = false; } } - diff --git a/src/gui/widgets/emoteshortcutcontainer.h b/src/gui/widgets/emoteshortcutcontainer.h index ecd41736..57d5efd2 100644 --- a/src/gui/widgets/emoteshortcutcontainer.h +++ b/src/gui/widgets/emoteshortcutcontainer.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef EMOTESHORTCUTCONTAINER_H -#define EMOTESHORTCUTCONTAINER_H +#pragma once #include "gui/widgets/shortcutcontainer.h" @@ -34,8 +33,6 @@ class EmoteShortcutContainer : public ShortcutContainer public: EmoteShortcutContainer(); - ~EmoteShortcutContainer() override; - /** * Draws the items. */ @@ -60,5 +57,3 @@ class EmoteShortcutContainer : public ShortcutContainer bool mEmoteClicked = false; int mEmoteMoved = -1; }; - -#endif diff --git a/src/gui/widgets/flowcontainer.h b/src/gui/widgets/flowcontainer.h index 21daae16..46be0919 100644 --- a/src/gui/widgets/flowcontainer.h +++ b/src/gui/widgets/flowcontainer.h @@ -18,8 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef FLOWCONTAINER_H -#define FLOWCONTAINER_H +#pragma once #include "container.h" @@ -56,5 +55,3 @@ class FlowContainer : public Container, int mGridWidth = 1; int mGridHeight = 1; }; - -#endif diff --git a/src/gui/widgets/icon.cpp b/src/gui/widgets/icon.cpp index 67fd8384..61506a6b 100644 --- a/src/gui/widgets/icon.cpp +++ b/src/gui/widgets/icon.cpp @@ -27,7 +27,7 @@ #include "resources/resourcemanager.h" Icon::Icon(const std::string &file) - : Icon(ResourceManager::getInstance()->getImageRef(file)) + : Icon(ResourceManager::getInstance()->getImage(file)) { } diff --git a/src/gui/widgets/icon.h b/src/gui/widgets/icon.h index 3ebc2c16..5e61520c 100644 --- a/src/gui/widgets/icon.h +++ b/src/gui/widgets/icon.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef ICON_H -#define ICON_H +#pragma once #include "resources/resource.h" @@ -68,5 +67,3 @@ class Icon : public gcn::Widget private: ResourceRef<Image> mImage; }; - -#endif // ICON_H diff --git a/src/gui/widgets/inttextfield.h b/src/gui/widgets/inttextfield.h index d5829404..bebad71d 100644 --- a/src/gui/widgets/inttextfield.h +++ b/src/gui/widgets/inttextfield.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef INTTEXTFIELD_H -#define INTTEXTFIELD_H +#pragma once #include "textfield.h" @@ -71,5 +70,3 @@ class IntTextField : public TextField int mDefault; /**< Default value */ int mValue; /**< Current value */ }; - -#endif diff --git a/src/gui/widgets/itemcontainer.cpp b/src/gui/widgets/itemcontainer.cpp index 940c69f4..d0d24c51 100644 --- a/src/gui/widgets/itemcontainer.cpp +++ b/src/gui/widgets/itemcontainer.cpp @@ -25,7 +25,6 @@ #include "inventory.h" #include "item.h" #include "itemshortcut.h" -#include "log.h" #include "gui/chatwindow.h" #include "gui/itempopup.h" @@ -44,19 +43,12 @@ // TODO: Add support for adding items to the item shortcut window (global // itemShortcut). -static const int BOX_WIDTH = 35; -static const int BOX_HEIGHT = 43; - ItemContainer::ItemContainer(Inventory *inventory): mInventory(inventory) { mItemPopup = new ItemPopup; setFocusable(true); - mSelImg = Theme::getImageFromTheme("selection.png"); - if (!mSelImg) - logger->error("Unable to load selection.png"); - addKeyListener(this); addMouseListener(this); addWidgetListener(this); @@ -64,7 +56,6 @@ ItemContainer::ItemContainer(Inventory *inventory): ItemContainer::~ItemContainer() { - mSelImg->decRef(); delete mItemPopup; } @@ -113,39 +104,47 @@ void ItemContainer::draw(gcn::Graphics *graphics) } } + auto theme = gui->getTheme(); + auto &slotSkin = theme->getSkin(SkinType::ItemSlot); + WidgetState slotState; + for (int i = 0; i < mGridColumns; i++) { for (int j = 0; j < mGridRows; j++) { - int itemX = i * BOX_WIDTH; - int itemY = j * BOX_HEIGHT; + int itemX = i * slotSkin.width; + int itemY = j * slotSkin.height; int itemIndex = j * mGridColumns + i; + slotState.x = itemX; + slotState.y = itemY; + slotState.flags = 0; + + if (itemIndex == mSelectedIndex) + { + slotState.flags |= STATE_SELECTED; + + if (mSelectionStatus == SEL_DRAGGING) + { + // Reposition the coords to that of the cursor. + itemX = mDragPosX - (slotSkin.width / 2); + itemY = mDragPosY - (slotSkin.height / 2); + } + } + + slotSkin.draw(g, slotState); + Item *item = getItemAt(itemIndex); if (!item || item->getId() == 0) continue; - Image *image = item->getImage(); - if (image) + if (Image *image = item->getImage()) { - if (itemIndex == mSelectedIndex) - { - if (mSelectionStatus == SEL_DRAGGING) - { - // Reposition the coords to that of the cursor. - itemX = mDragPosX - (BOX_WIDTH / 2); - itemY = mDragPosY - (BOX_HEIGHT / 2); - } - else - { - // Draw selection border image. - g->drawImage(mSelImg, itemX, itemY); - } - } - image->setAlpha(1.0f); // ensure the image if fully drawn... - g->drawImage(image, itemX, itemY); + image->setAlpha(1.0f); + g->drawImage(image, itemX + slotSkin.padding, itemY + slotSkin.padding); } + // Draw item caption std::string caption; if (item->getQuantity() > 1) @@ -154,22 +153,22 @@ void ItemContainer::draw(gcn::Graphics *graphics) caption = "Eq."; if (item->isEquipped()) - g->setColor(Theme::getThemeColor(Theme::ITEM_EQUIPPED)); + g->setColor(theme->getColor(Theme::ITEM_EQUIPPED)); else g->setColor(gcn::Color(0, 0, 0)); - g->drawText(caption, itemX + BOX_WIDTH / 2, - itemY + BOX_HEIGHT - 14, gcn::Graphics::CENTER); + g->drawText(caption, itemX + slotSkin.width / 2, + itemY + slotSkin.height - 14, gcn::Graphics::CENTER); } } // Draw an orange box around the selected item if (isFocused() && mHighlightedIndex != -1) { - const int itemX = (mHighlightedIndex % mGridColumns) * BOX_WIDTH; - const int itemY = (mHighlightedIndex / mGridColumns) * BOX_HEIGHT; + const int itemX = (mHighlightedIndex % mGridColumns) * slotSkin.width; + const int itemY = (mHighlightedIndex / mGridColumns) * slotSkin.height; g->setColor(gcn::Color(255, 128, 0)); - g->drawRectangle(gcn::Rectangle(itemX, itemY, BOX_WIDTH, BOX_HEIGHT)); + g->drawRectangle(gcn::Rectangle(itemX, itemY, slotSkin.width, slotSkin.height)); } } @@ -355,9 +354,7 @@ void ItemContainer::mouseReleased(gcn::MouseEvent &event) // Show ItemTooltip void ItemContainer::mouseMoved(gcn::MouseEvent &event) { - Item *item = getItemAt(getSlotIndex(event.getX(), event.getY())); - - if (item) + if (Item *item = getItemAt(getSlotIndex(event.getX(), event.getY()))) { mItemPopup->setItem(item->getInfo()); mItemPopup->position(viewport->getMouseX(), viewport->getMouseY()); @@ -376,26 +373,36 @@ void ItemContainer::mouseExited(gcn::MouseEvent &event) void ItemContainer::widgetResized(const gcn::Event &event) { - mGridColumns = std::max(1, getWidth() / BOX_WIDTH); + auto &slotSkin = gui->getTheme()->getSkin(SkinType::ItemSlot); + + mGridColumns = std::max(1, getWidth() / slotSkin.width); adjustHeight(); } void ItemContainer::adjustHeight() { + auto &slotSkin = gui->getTheme()->getSkin(SkinType::ItemSlot); + mGridRows = (mLastUsedSlot + 1) / mGridColumns; if (mGridRows == 0 || (mLastUsedSlot + 1) % mGridColumns > 0) ++mGridRows; - setHeight(mGridRows * BOX_HEIGHT); + setHeight(mGridRows * slotSkin.height); } int ItemContainer::getSlotIndex(int x, int y) const { - if (x < getWidth() && y < getHeight()) - { - return (y / BOX_HEIGHT) * mGridColumns + (x / BOX_WIDTH); - } - return Inventory::NO_SLOT_INDEX; + if (x >= getWidth() || y >= getHeight()) + return Inventory::NO_SLOT_INDEX; + + auto &slotSkin = gui->getTheme()->getSkin(SkinType::ItemSlot); + const auto row = y / slotSkin.height; + const auto column = x / slotSkin.width; + + if (row < 0 || row >= mGridRows || column < 0 || column >= mGridColumns) + return Inventory::NO_SLOT_INDEX; + + return (row * mGridColumns) + column; } void ItemContainer::keyAction() diff --git a/src/gui/widgets/itemcontainer.h b/src/gui/widgets/itemcontainer.h index 51807aba..c1d611b9 100644 --- a/src/gui/widgets/itemcontainer.h +++ b/src/gui/widgets/itemcontainer.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef ITEMCONTAINER_H -#define ITEMCONTAINER_H +#pragma once #include <guichan/keylistener.hpp> #include <guichan/mouselistener.hpp> @@ -71,6 +70,9 @@ class ItemContainer : public gcn::Widget, */ void draw(gcn::Graphics *graphics) override; + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics *graphics) override {} + // KeyListener void keyPressed(gcn::KeyEvent &event) override; void keyReleased(gcn::KeyEvent &event) override; @@ -178,7 +180,6 @@ class ItemContainer : public gcn::Widget, Inventory *mInventory; int mGridColumns = 1; int mGridRows = 1; - Image *mSelImg; int mSelectedIndex = -1; int mHighlightedIndex = -1; int mLastUsedSlot = -1; @@ -196,5 +197,3 @@ class ItemContainer : public gcn::Widget, std::list<gcn::SelectionListener *> mSelectionListeners; }; - -#endif // ITEMCONTAINER_H diff --git a/src/gui/widgets/itemlinkhandler.h b/src/gui/widgets/itemlinkhandler.h index 28e9c11c..58202d33 100644 --- a/src/gui/widgets/itemlinkhandler.h +++ b/src/gui/widgets/itemlinkhandler.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef ITEM_LINK_HANDLER_H -#define ITEM_LINK_HANDLER_H +#pragma once #include "gui/widgets/linkhandler.h" @@ -49,5 +48,3 @@ class ItemLinkHandler : public LinkHandler, gcn::ActionListener Window *mParent = nullptr; std::string mLink; }; - -#endif diff --git a/src/gui/widgets/itemshortcutcontainer.cpp b/src/gui/widgets/itemshortcutcontainer.cpp index 0b8f0c8c..b47fa29d 100644 --- a/src/gui/widgets/itemshortcutcontainer.cpp +++ b/src/gui/widgets/itemshortcutcontainer.cpp @@ -21,7 +21,6 @@ #include "gui/widgets/itemshortcutcontainer.h" -#include "configuration.h" #include "graphics.h" #include "inventory.h" #include "item.h" @@ -39,64 +38,45 @@ #include "utils/stringutils.h" ItemShortcutContainer::ItemShortcutContainer() + : mItemPopup(new ItemPopup) { - addMouseListener(this); - addWidgetListener(this); - - mItemPopup = new ItemPopup; - - mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png"); mMaxItems = itemShortcut->getItemCount(); - - mBackgroundImg->setAlpha(config.guiAlpha); - - mBoxHeight = mBackgroundImg->getHeight(); - mBoxWidth = mBackgroundImg->getWidth(); } -ItemShortcutContainer::~ItemShortcutContainer() -{ - mBackgroundImg->decRef(); - delete mItemPopup; -} +ItemShortcutContainer::~ItemShortcutContainer() = default; void ItemShortcutContainer::draw(gcn::Graphics *graphics) { - if (config.guiAlpha != mAlpha) - { - mAlpha = config.guiAlpha; - mBackgroundImg->setAlpha(mAlpha); - } - auto *g = static_cast<Graphics*>(graphics); + auto theme = gui->getTheme(); + auto &skin = theme->getSkin(SkinType::ShortcutBox); graphics->setFont(getFont()); for (int i = 0; i < mMaxItems; i++) { - const int itemX = (i % mGridWidth) * mBoxWidth; - const int itemY = (i / mGridWidth) * mBoxHeight; - - g->drawImage(mBackgroundImg, itemX, itemY); + WidgetState state; + state.x = (i % mGridWidth) * mBoxWidth; + state.y = (i / mGridWidth) * mBoxHeight; + skin.draw(g, state); // Draw item keyboard shortcut. const char *key = SDL_GetKeyName( keyboard.getKeyValue(KeyboardConfig::KEY_SHORTCUT_1 + i)); graphics->setColor(Theme::getThemeColor(Theme::TEXT)); - g->drawText(key, itemX + 2, itemY + 2, gcn::Graphics::LEFT); + g->drawText(key, + state.x + skin.padding + 2, + state.y + skin.padding + 2, + gcn::Graphics::LEFT); - if (itemShortcut->getItem(i) < 0) + const int itemId = itemShortcut->getItem(i); + if (itemId < 0) continue; - Item *item = - PlayerInfo::getInventory()->findItem(itemShortcut->getItem(i)); - - if (item) + if (Item *item = PlayerInfo::getInventory()->findItem(itemId)) { // Draw item icon. - Image* image = item->getImage(); - - if (image) + if (Image *image = item->getImage()) { std::string caption; if (item->getQuantity() > 1) @@ -105,11 +85,13 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) caption = "Eq."; image->setAlpha(1.0f); - g->drawImage(image, itemX, itemY); + g->drawImage(image, state.x + skin.padding, state.y + skin.padding); if (item->isEquipped()) g->setColor(Theme::getThemeColor(Theme::ITEM_EQUIPPED)); - g->drawText(caption, itemX + mBoxWidth / 2, - itemY + mBoxHeight - 14, gcn::Graphics::CENTER); + g->drawText(caption, + state.x + mBoxWidth / 2, + state.y + mBoxHeight - 14, + gcn::Graphics::CENTER); } } } @@ -117,7 +99,7 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) if (mItemMoved) { // Draw the item image being dragged by the cursor. - if (Image* image = mItemMoved->getImage()) + if (Image *image = mItemMoved->getImage()) { const int tPosX = mCursorPosX - (image->getWidth() / 2); const int tPosY = mCursorPosY - (image->getHeight() / 2); @@ -137,23 +119,20 @@ void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) if (!mItemMoved && mItemClicked) { const int index = getIndexFromGrid(event.getX(), event.getY()); - if (index == -1) return; const int itemId = itemShortcut->getItem(index); - if (itemId < 0) return; - Item *item = PlayerInfo::getInventory()->findItem(itemId); - - if (item) + if (Item *item = PlayerInfo::getInventory()->findItem(itemId)) { mItemMoved = item; itemShortcut->removeItem(index); } } + if (mItemMoved) { mCursorPosX = event.getX(); @@ -225,19 +204,7 @@ void ItemShortcutContainer::mouseReleased(gcn::MouseEvent &event) // Show ItemTooltip void ItemShortcutContainer::mouseMoved(gcn::MouseEvent &event) { - const int index = getIndexFromGrid(event.getX(), event.getY()); - - if (index == -1) - return; - - const int itemId = itemShortcut->getItem(index); - - if (itemId < 0) - return; - - Item *item = PlayerInfo::getInventory()->findItem(itemId); - - if (item) + if (Item *item = getItemAt(event.getX(), event.getY())) { mItemPopup->setItem(item->getInfo()); mItemPopup->position(viewport->getMouseX(), viewport->getMouseY()); @@ -248,6 +215,19 @@ void ItemShortcutContainer::mouseMoved(gcn::MouseEvent &event) } } +Item *ItemShortcutContainer::getItemAt(int x, int y) const +{ + const int index = getIndexFromGrid(x, y); + if (index == -1) + return nullptr; + + const int itemId = itemShortcut->getItem(index); + if (itemId < 0) + return nullptr; + + return PlayerInfo::getInventory()->findItem(itemId); +} + // Hide ItemTooltip void ItemShortcutContainer::mouseExited(gcn::MouseEvent &event) { diff --git a/src/gui/widgets/itemshortcutcontainer.h b/src/gui/widgets/itemshortcutcontainer.h index 243920a0..a01857db 100644 --- a/src/gui/widgets/itemshortcutcontainer.h +++ b/src/gui/widgets/itemshortcutcontainer.h @@ -19,13 +19,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef ITEMSHORTCUTCONTAINER_H -#define ITEMSHORTCUTCONTAINER_H +#pragma once #include "gui/widgets/shortcutcontainer.h" #include <guichan/mouselistener.hpp> +#include <memory> + class Image; class Item; class ItemPopup; @@ -66,10 +67,10 @@ class ItemShortcutContainer : public ShortcutContainer void mouseExited(gcn::MouseEvent &event) override; void mouseMoved(gcn::MouseEvent &event) override; + Item *getItemAt(int x, int y) const; + bool mItemClicked = false; Item *mItemMoved = nullptr; - ItemPopup *mItemPopup; + std::unique_ptr<ItemPopup> mItemPopup; }; - -#endif diff --git a/src/gui/widgets/label.cpp b/src/gui/widgets/label.cpp index af5220ef..a2ed8820 100644 --- a/src/gui/widgets/label.cpp +++ b/src/gui/widgets/label.cpp @@ -21,8 +21,13 @@ #include "gui/widgets/label.h" +#include "textrenderer.h" + #include "resources/theme.h" +#include <guichan/exception.hpp> +#include <guichan/font.hpp> + Label::Label() { setForegroundColor(Theme::getThemeColor(Theme::TEXT)); @@ -36,5 +41,33 @@ Label::Label(const std::string &caption) : void Label::draw(gcn::Graphics *graphics) { - gcn::Label::draw(static_cast<gcn::Graphics*>(graphics)); + int textX; + int textY = (getHeight() - getFont()->getHeight()) / 2; + + switch (getAlignment()) + { + case Graphics::LEFT: + textX = 0; + break; + case Graphics::CENTER: + textX = getWidth() / 2; + break; + case Graphics::RIGHT: + textX = getWidth(); + break; + default: + throw GCN_EXCEPTION("Unknown alignment."); + } + + TextRenderer::renderText(static_cast<Graphics *>(graphics), + getCaption(), + textX, + textY, + getAlignment(), + getForegroundColor(), + getFont(), + mOutlineColor.has_value(), + mShadowColor.has_value(), + mOutlineColor, + mShadowColor); } diff --git a/src/gui/widgets/label.h b/src/gui/widgets/label.h index cb7a8b1c..85bcbe23 100644 --- a/src/gui/widgets/label.h +++ b/src/gui/widgets/label.h @@ -19,14 +19,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef LABEL_H -#define LABEL_H +#pragma once #include <guichan/widgets/label.hpp> +#include <optional> /** * Label widget. Same as the Guichan label but modified to use the palette - * system. + * system and support outlines and shadows. * * \ingroup GUI */ @@ -42,9 +42,31 @@ class Label : public gcn::Label Label(const std::string &caption); /** + * Sets the color of the outline. + */ + void setOutlineColor(std::optional<gcn::Color> color); + + /** + * Sets the color of the shadow. + */ + void setShadowColor(std::optional<gcn::Color> color); + + /** * Draws the label. */ void draw(gcn::Graphics *graphics) override; + + private: + std::optional<gcn::Color> mOutlineColor; + std::optional<gcn::Color> mShadowColor; }; -#endif +inline void Label::setOutlineColor(std::optional<gcn::Color> color) +{ + mOutlineColor = color; +} + +inline void Label::setShadowColor(std::optional<gcn::Color> color) +{ + mShadowColor = color; +} diff --git a/src/gui/widgets/layout.h b/src/gui/widgets/layout.h index 4e4b28c5..42f08758 100644 --- a/src/gui/widgets/layout.h +++ b/src/gui/widgets/layout.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef WIDGET_LAYOUT_H -#define WIDGET_LAYOUT_H +#pragma once #include <guichan/widgets/container.hpp> @@ -168,9 +167,12 @@ class LayoutCell }; LayoutCell() = default; - ~LayoutCell(); + // Copy not allowed, as the cell may own an array. + LayoutCell(LayoutCell const &) = delete; + LayoutCell &operator=(LayoutCell const &) = delete; + /** * Sets the padding around the cell content. */ @@ -232,10 +234,6 @@ class LayoutCell void computeSizes(); private: - // Copy not allowed, as the cell may own an array. - LayoutCell(LayoutCell const &); - LayoutCell &operator=(LayoutCell const &); - union { gcn::Widget *mWidget; @@ -310,5 +308,3 @@ class Layout : public LayoutCell private: bool mComputed; }; - -#endif // WIDGET_LAYOUT_H diff --git a/src/gui/widgets/layouthelper.h b/src/gui/widgets/layouthelper.h index 26360a9a..ad01c565 100644 --- a/src/gui/widgets/layouthelper.h +++ b/src/gui/widgets/layouthelper.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef LAYOUTHELPER_H -#define LAYOUTHELPER_H +#pragma once #include "gui/widgets/layout.h" @@ -74,5 +73,3 @@ class LayoutHelper : public gcn::WidgetListener Layout mLayout; /**< Layout handler */ gcn::Container *mContainer; /**< Managed container */ }; - -#endif // LAYOUTHELPER_H diff --git a/src/gui/widgets/linkhandler.h b/src/gui/widgets/linkhandler.h index 33263a25..48b182a1 100644 --- a/src/gui/widgets/linkhandler.h +++ b/src/gui/widgets/linkhandler.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef LINK_HANDLER_H -#define LINK_HANDLER_H +#pragma once #include <string> @@ -35,5 +34,3 @@ class LinkHandler virtual void handleLink(const std::string &link) = 0; }; - -#endif diff --git a/src/gui/widgets/listbox.cpp b/src/gui/widgets/listbox.cpp index 55f0f422..112de232 100644 --- a/src/gui/widgets/listbox.cpp +++ b/src/gui/widgets/listbox.cpp @@ -21,8 +21,7 @@ #include "gui/widgets/listbox.h" -#include "configuration.h" - +#include "gui/gui.h" #include "gui/sdlinput.h" #include "resources/theme.h" @@ -32,39 +31,29 @@ #include <guichan/key.hpp> #include <guichan/listmodel.hpp> -float ListBox::mAlpha = 1.0; - ListBox::ListBox(gcn::ListModel *listModel): gcn::ListBox(listModel) { } -void ListBox::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (mAlpha != alpha) - mAlpha = alpha; -} - void ListBox::draw(gcn::Graphics *graphics) { if (!mListModel) return; - updateAlpha(); - - graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, - (int) (mAlpha * 255.0f))); graphics->setFont(getFont()); const int height = getRowHeight(); // Draw filled rectangle around the selected list element if (mSelected >= 0) + { + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + highlightColor.a = gui->getTheme()->getGuiAlpha(); + graphics->setColor(highlightColor); graphics->fillRectangle(gcn::Rectangle(0, height * mSelected, getWidth(), height)); + } // Draw the list elements graphics->setColor(Theme::getThemeColor(Theme::TEXT)); diff --git a/src/gui/widgets/listbox.h b/src/gui/widgets/listbox.h index d16256b1..40bc2fbc 100644 --- a/src/gui/widgets/listbox.h +++ b/src/gui/widgets/listbox.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef LISTBOX_H -#define LISTBOX_H +#pragma once #include <guichan/widgets/listbox.hpp> @@ -43,10 +42,8 @@ class ListBox : public gcn::ListBox */ void draw(gcn::Graphics *graphics) override; - /** - * Update the alpha value to the graphic components. - */ - static void updateAlpha(); + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics *graphics) override {} // Inherited from KeyListener @@ -61,9 +58,4 @@ class ListBox : public gcn::ListBox void mouseWheelMovedDown(gcn::MouseEvent& mouseEvent) override; void mouseDragged(gcn::MouseEvent &event) override; - - protected: - static float mAlpha; }; - -#endif diff --git a/src/gui/widgets/passwordfield.h b/src/gui/widgets/passwordfield.h index 4bed0e05..36964843 100644 --- a/src/gui/widgets/passwordfield.h +++ b/src/gui/widgets/passwordfield.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PASSWORDFIELD_H -#define PASSWORDFIELD_H +#pragma once #include "textfield.h" @@ -42,5 +41,3 @@ class PasswordField : public TextField */ void draw(gcn::Graphics *graphics) override; }; - -#endif diff --git a/src/gui/widgets/playerbox.cpp b/src/gui/widgets/playerbox.cpp index 3bdd6bd1..f251035d 100644 --- a/src/gui/widgets/playerbox.cpp +++ b/src/gui/widgets/playerbox.cpp @@ -22,86 +22,22 @@ #include "gui/widgets/playerbox.h" #include "being.h" -#include "configuration.h" #include "graphics.h" -#include "resources/image.h" -#include "resources/theme.h" - -#include "utils/dtor.h" - -int PlayerBox::instances = 0; -float PlayerBox::mAlpha = 1.0; -ImageRect PlayerBox::background; - -PlayerBox::PlayerBox(const Being *being): - mBeing(being) +PlayerBox::PlayerBox(const Being *being) + : mBeing(being) { - setFrameSize(2); - - if (instances == 0) - { - // Load the background skin - Image *textbox = Theme::getImageFromTheme("deepbox.png"); - int bggridx[4] = {0, 3, 28, 31}; - int bggridy[4] = {0, 3, 28, 31}; - int a = 0; - - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - background.grid[a] = textbox->getSubImage( - bggridx[x], bggridy[y], - bggridx[x + 1] - bggridx[x] + 1, - bggridy[y + 1] - bggridy[y] + 1); - a++; - } - } - - background.setAlpha(config.guiAlpha); - - textbox->decRef(); - } - - instances++; -} - -PlayerBox::~PlayerBox() -{ - instances--; - - mBeing = nullptr; - - if (instances == 0) - { - std::for_each(background.grid, background.grid + 9, dtor<Image*>()); - } } void PlayerBox::draw(gcn::Graphics *graphics) { + ScrollArea::draw(graphics); + if (mBeing) { // Draw character - const int bs = getFrameSize(); - const int x = getWidth() / 2 + bs; - const int y = getHeight() - bs; + const int x = getWidth() / 2; + const int y = (getHeight() + mBeing->getHeight()) / 2 - 12; mBeing->drawSpriteAt(static_cast<Graphics*>(graphics), x, y); } - - if (config.guiAlpha != mAlpha) - { - mAlpha = config.guiAlpha; - background.setAlpha(config.guiAlpha); - } -} - -void PlayerBox::drawFrame(gcn::Graphics *graphics) -{ - const int bs = getFrameSize(); - const int w = getWidth() + bs * 2; - const int h = getHeight() + bs * 2; - - static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, background); } diff --git a/src/gui/widgets/playerbox.h b/src/gui/widgets/playerbox.h index 68dd670e..39392c63 100644 --- a/src/gui/widgets/playerbox.h +++ b/src/gui/widgets/playerbox.h @@ -19,20 +19,18 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PLAYERBOX_H -#define PLAYERBOX_H +#pragma once -#include <guichan/widgets/scrollarea.hpp> +#include "scrollarea.h" class Being; -class ImageRect; /** * A box showing a player character. * * \ingroup GUI */ -class PlayerBox : public gcn::ScrollArea +class PlayerBox : public ScrollArea { public: /** @@ -41,8 +39,6 @@ class PlayerBox : public gcn::ScrollArea */ PlayerBox(const Being *being = nullptr); - ~PlayerBox() override; - /** * Sets a new player character to be displayed by this box. Setting the * player to <code>NULL</code> causes the box not to draw any @@ -52,21 +48,10 @@ class PlayerBox : public gcn::ScrollArea { mBeing = being; } /** - * Draws the scroll area. + * Draws the scroll area and the player. */ void draw(gcn::Graphics *graphics) override; - /** - * Draws the background and border of the scroll area. - */ - void drawFrame(gcn::Graphics *graphics) override; - private: const Being *mBeing; /**< The character used for display */ - - static float mAlpha; - static int instances; - static ImageRect background; }; - -#endif diff --git a/src/gui/widgets/popup.cpp b/src/gui/widgets/popup.cpp index 94d8cf85..b245b9e6 100644 --- a/src/gui/widgets/popup.cpp +++ b/src/gui/widgets/popup.cpp @@ -22,31 +22,32 @@ #include "gui/widgets/popup.h" +#include "browserbox.h" #include "graphics.h" #include "log.h" +#include "textbox.h" +#include "gui/gui.h" #include "gui/viewport.h" - +#include "gui/widgets/label.h" #include "gui/widgets/windowcontainer.h" -#include "resources/theme.h" - #include <guichan/exception.hpp> -Popup::Popup(const std::string &name, const std::string &skin): - mPopupName(name), - mMaxWidth(graphics->getWidth()), - mMaxHeight(graphics->getHeight()) +Popup::Popup(const std::string &name, SkinType skinType) + : mPopupName(name) + , mMaxWidth(graphics->getWidth()) + , mMaxHeight(graphics->getHeight()) + , mSkinType(skinType) { logger->log("Popup::Popup(\"%s\")", name.c_str()); if (!windowContainer) throw GCN_EXCEPTION("Popup::Popup(): no windowContainer set"); - setPadding(6); - - // Loads the skin - mSkin = Theme::instance()->load(skin); + auto &skin = getSkin(); + setFrameSize(skin.frameSize); + setPadding(skin.padding); // Add this window to the window container windowContainer->add(this); @@ -58,8 +59,6 @@ Popup::Popup(const std::string &name, const std::string &skin): Popup::~Popup() { logger->log("Popup::~Popup(\"%s\")", mPopupName.c_str()); - - mSkin->instances--; } void Popup::setWindowContainer(WindowContainer *wc) @@ -67,15 +66,57 @@ void Popup::setWindowContainer(WindowContainer *wc) windowContainer = wc; } -void Popup::draw(gcn::Graphics *graphics) +void Popup::add(gcn::Widget *widget) +{ + Container::add(widget); + widgetAdded(widget); +} + +void Popup::add(gcn::Widget *widget, int x, int y) +{ + Container::add(widget, x, y); + widgetAdded(widget); +} + +void Popup::widgetAdded(gcn::Widget *widget) const { - auto *g = static_cast<Graphics*>(graphics); + if (const int paletteId = getSkin().palette) + { + if (auto browserBox = dynamic_cast<BrowserBox*>(widget)) + { + browserBox->setPalette(paletteId); + } + else if (auto label = dynamic_cast<Label*>(widget)) + { + auto &palette = gui->getTheme()->getPalette(paletteId); + label->setForegroundColor(palette.getColor(Theme::TEXT)); + label->setOutlineColor(palette.getOutlineColor(Theme::TEXT)); + } + else if (auto textBox = dynamic_cast<TextBox*>(widget)) + { + auto &palette = gui->getTheme()->getPalette(paletteId); + textBox->setTextColor(&palette.getColor(Theme::TEXT)); + textBox->setOutlineColor(palette.getOutlineColor(Theme::TEXT)); + } + } +} - g->drawImageRect(0, 0, getWidth(), getHeight(), mSkin->getBorder()); +void Popup::draw(gcn::Graphics *graphics) +{ + if (getFrameSize() == 0) + drawFrame(graphics); drawChildren(graphics); } +void Popup::drawFrame(gcn::Graphics *graphics) +{ + WidgetState state(this); + state.width += getFrameSize() * 2; + state.height += getFrameSize() * 2; + getSkin().draw(static_cast<Graphics *>(graphics), state); +} + gcn::Rectangle Popup::getChildrenArea() { return gcn::Rectangle(getPadding(), getPadding(), @@ -116,12 +157,12 @@ void Popup::setLocationRelativeTo(gcn::Widget *widget) void Popup::setMinWidth(int width) { - mMinWidth = std::max(width, mSkin->getMinWidth()); + mMinWidth = std::max(getSkin().getMinWidth(), width); } void Popup::setMinHeight(int height) { - mMinHeight = std::max(height, mSkin->getMinHeight()); + mMinHeight = std::max(getSkin().getMinHeight(), height); } void Popup::setMaxWidth(int width) @@ -156,6 +197,11 @@ void Popup::position(int x, int y) requestMoveToTop(); } +const Skin &Popup::getSkin() const +{ + return gui->getTheme()->getSkin(mSkinType); +} + void Popup::mouseMoved(gcn::MouseEvent &event) { if (viewport) diff --git a/src/gui/widgets/popup.h b/src/gui/widgets/popup.h index c77bf814..b88cafc9 100644 --- a/src/gui/widgets/popup.h +++ b/src/gui/widgets/popup.h @@ -20,12 +20,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef POPUP_H -#define POPUP_H +#pragma once #include "guichanfwd.h" #include "gui/widgets/container.h" +#include "resources/theme.h" #include <guichan/mouselistener.hpp> @@ -53,10 +53,10 @@ class Popup : public Container, public gcn::MouseListener * * @param name A human readable name for the popup. Only useful for * debugging purposes. - * @param skin The location where the Popup's skin XML can be found. + * @param skinType The skin type used when drawing the popup. */ - Popup(const std::string &name = std::string(), - const std::string &skin = "window.xml"); + explicit Popup(const std::string &name = std::string(), + SkinType skinType = SkinType::Popup); /** * Destructor. Deletes all the added widgets. @@ -68,12 +68,21 @@ class Popup : public Container, public gcn::MouseListener */ static void setWindowContainer(WindowContainer *windowContainer); + // Container interface + void add(gcn::Widget *widget) override; + void add(gcn::Widget *widget, int x, int y) override; + /** * Draws the popup. */ void draw(gcn::Graphics *graphics) override; /** + * Draws the popup frame. + */ + void drawFrame(gcn::Graphics *graphics) override; + + /** * Sets the size of this popup. */ void setContentSize(int width, int height); @@ -151,15 +160,20 @@ class Popup : public Container, public gcn::MouseListener */ void position(int x, int y); + /** + * Returns the Skin used by this popup. + */ + const Skin &getSkin() const; + private: - std::string mPopupName; /**< Name of the popup */ - int mMinWidth = 100; /**< Minimum popup width */ - int mMinHeight = 40; /**< Minimum popup height */ - int mMaxWidth; /**< Maximum popup width */ - int mMaxHeight; /**< Maximum popup height */ - int mPadding; /**< Holds the padding of the popup. */ - - Skin *mSkin; /**< Skin in use by this popup */ -}; + void widgetAdded(gcn::Widget *widget) const; -#endif + std::string mPopupName; /**< Name of the popup */ + int mMinWidth = 100; /**< Minimum popup width */ + int mMinHeight = 40; /**< Minimum popup height */ + int mMaxWidth; /**< Maximum popup width */ + int mMaxHeight; /**< Maximum popup height */ + int mPadding; /**< Holds the padding of the popup. */ + + SkinType mSkinType; /**< The skin type used when drawing the popup widget. */ +}; diff --git a/src/gui/widgets/progressbar.cpp b/src/gui/widgets/progressbar.cpp index 9d41d1af..5cf1b05a 100644 --- a/src/gui/widgets/progressbar.cpp +++ b/src/gui/widgets/progressbar.cpp @@ -21,23 +21,14 @@ #include "gui/widgets/progressbar.h" -#include "configuration.h" #include "graphics.h" -#include "textrenderer.h" #include "gui/gui.h" -#include "resources/image.h" #include "resources/theme.h" -#include "utils/dtor.h" - #include <guichan/font.hpp> -ImageRect ProgressBar::mBorder; -int ProgressBar::mInstances = 0; -float ProgressBar::mAlpha = 1.0; - ProgressBar::ProgressBar(float progress, int width, int height, int color): @@ -53,36 +44,6 @@ ProgressBar::ProgressBar(float progress, mProgress); setSize(width, height); - - if (mInstances == 0) - { - Image *dBorders = Theme::getImageFromTheme("vscroll_grey.png"); - mBorder.grid[0] = dBorders->getSubImage(0, 0, 4, 4); - mBorder.grid[1] = dBorders->getSubImage(4, 0, 3, 4); - mBorder.grid[2] = dBorders->getSubImage(7, 0, 4, 4); - mBorder.grid[3] = dBorders->getSubImage(0, 4, 4, 10); - mBorder.grid[4] = dBorders->getSubImage(4, 4, 3, 10); - mBorder.grid[5] = dBorders->getSubImage(7, 4, 4, 10); - mBorder.grid[6] = dBorders->getSubImage(0, 15, 4, 4); - mBorder.grid[7] = dBorders->getSubImage(4, 15, 3, 4); - mBorder.grid[8] = dBorders->getSubImage(7, 15, 4, 4); - - mBorder.setAlpha(mAlpha); - - dBorders->decRef(); - } - - mInstances++; -} - -ProgressBar::~ProgressBar() -{ - mInstances--; - - if (mInstances == 0) - { - std::for_each(mBorder.grid, mBorder.grid + 9, dtor<Image*>()); - } } void ProgressBar::logic() @@ -114,30 +75,19 @@ void ProgressBar::logic() } } -void ProgressBar::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (mAlpha != alpha) - { - mAlpha = alpha; - mBorder.setAlpha(mAlpha); - } -} - void ProgressBar::draw(gcn::Graphics *graphics) { - updateAlpha(); - - mColor.a = (int) (mAlpha * 255); + mColor.a = gui->getTheme()->getGuiAlpha(); gcn::Rectangle rect = getDimension(); rect.x = 0; rect.y = 0; - render(static_cast<Graphics*>(graphics), rect, mColor, - mProgress, mText); + gui->getTheme()->drawProgressBar(static_cast<Graphics *>(graphics), + rect, + mColor, + mProgress, + mText); } void ProgressBar::setProgress(float progress) @@ -149,9 +99,7 @@ void ProgressBar::setProgress(float progress) mProgress = p; if (mProgressPalette >= 0) - { mColorToGo = Theme::getProgressColor(mProgressPalette, progress); - } } void ProgressBar::setProgressPalette(int progressPalette) @@ -160,9 +108,7 @@ void ProgressBar::setProgressPalette(int progressPalette) mProgressPalette = progressPalette; if (mProgressPalette != oldPalette && mProgressPalette >= 0) - { mColorToGo = Theme::getProgressColor(mProgressPalette, mProgressToGo); - } } void ProgressBar::setColor(const gcn::Color &color) @@ -172,37 +118,3 @@ void ProgressBar::setColor(const gcn::Color &color) if (!mSmoothColorChange) mColor = color; } - -void ProgressBar::render(Graphics *graphics, const gcn::Rectangle &area, - const gcn::Color &color, float progress, - const std::string &text) -{ - gcn::Font *oldFont = graphics->getFont(); - gcn::Color oldColor = graphics->getColor(); - - graphics->drawImageRect(area, mBorder); - - // The bar - if (progress > 0) - { - graphics->setColor(color); - graphics->fillRectangle(gcn::Rectangle(area.x + 4, area.y + 4, - (int) (progress * (area.width - 8)), - area.height - 8)); - } - - // The label - if (!text.empty()) - { - const int textX = area.x + area.width / 2; - const int textY = area.y + (area.height - boldFont->getHeight()) / 2; - - TextRenderer::renderText(graphics, text, textX, textY, - gcn::Graphics::CENTER, - Theme::getThemeColor(Theme::PROGRESS_BAR), - gui->getFont(), true, false); - } - - graphics->setFont(oldFont); - graphics->setColor(oldColor); -} diff --git a/src/gui/widgets/progressbar.h b/src/gui/widgets/progressbar.h index 2f9e665f..52904f5a 100644 --- a/src/gui/widgets/progressbar.h +++ b/src/gui/widgets/progressbar.h @@ -19,16 +19,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PROGRESSBAR_H -#define PROGRESSBAR_H +#pragma once #include <guichan/widget.hpp> #include <string> -class Graphics; -class ImageRect; - /** * A progress bar. * @@ -44,19 +40,12 @@ class ProgressBar : public gcn::Widget int width = 40, int height = 7, int color = -1); - ~ProgressBar() override; - /** * Performs progress bar logic (fading colors) */ void logic() override; /** - * Update the alpha value to the graphic components. - */ - static void updateAlpha(); - - /** * Draws the progress bar. */ void draw(gcn::Graphics *graphics) override; @@ -111,13 +100,6 @@ class ProgressBar : public gcn::Widget void setSmoothColorChange(bool smoothColorChange) { mSmoothColorChange = smoothColorChange; } - /** - * Renders a progressbar with the given properties. - */ - static void render(Graphics *graphics, const gcn::Rectangle &area, - const gcn::Color &color, float progress, - const std::string &text = std::string()); - private: float mProgress, mProgressToGo; bool mSmoothProgress = true; @@ -128,12 +110,4 @@ class ProgressBar : public gcn::Widget bool mSmoothColorChange = true; std::string mText; - - static ImageRect mBorder; - static int mInstances; - static float mAlpha; - - static const gcn::Color TEXT_COLOR; }; - -#endif diff --git a/src/gui/widgets/progressindicator.cpp b/src/gui/widgets/progressindicator.cpp index 496bd8a1..ccd4fd54 100644 --- a/src/gui/widgets/progressindicator.cpp +++ b/src/gui/widgets/progressindicator.cpp @@ -21,10 +21,10 @@ #include "progressindicator.h" #include "graphics.h" +#include "gui/gui.h" #include "simpleanimation.h" #include "resources/animation.h" -#include "resources/imageset.h" #include "resources/resourcemanager.h" #include "resources/theme.h" @@ -32,12 +32,12 @@ ProgressIndicator::ProgressIndicator() { - ImageSet *images = Theme::getImageSetFromTheme("progress-indicator.png", - 32, 32); + const std::string path = gui->getTheme()->resolvePath("progress-indicator.png"); + mImageSet = ResourceManager::getInstance()->getImageSet(path, 32, 32); Animation anim; - for (size_t i = 0; i < images->size(); ++i) - anim.addFrame(images->get(i), 100, 0, 0); + for (size_t i = 0; i < mImageSet->size(); ++i) + anim.addFrame(mImageSet->get(i), 100, 0, 0); mIndicator = std::make_unique<SimpleAnimation>(std::move(anim)); diff --git a/src/gui/widgets/progressindicator.h b/src/gui/widgets/progressindicator.h index 428bbd02..4a6ea339 100644 --- a/src/gui/widgets/progressindicator.h +++ b/src/gui/widgets/progressindicator.h @@ -18,8 +18,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PROGRESSINDICATOR_H -#define PROGRESSINDICATOR_H +#pragma once + +#include "resources/imageset.h" #include <guichan/widget.hpp> @@ -42,6 +43,5 @@ public: private: std::unique_ptr<SimpleAnimation> mIndicator; + ResourceRef<ImageSet> mImageSet; }; - -#endif // PROGRESSINDICATOR_H diff --git a/src/gui/widgets/radiobutton.cpp b/src/gui/widgets/radiobutton.cpp index 92cdacd1..ceba78eb 100644 --- a/src/gui/widgets/radiobutton.cpp +++ b/src/gui/widgets/radiobutton.cpp @@ -21,109 +21,43 @@ #include "gui/widgets/radiobutton.h" -#include "configuration.h" -#include "graphics.h" +#include "textrenderer.h" -#include "resources/image.h" +#include "gui/gui.h" #include "resources/theme.h" -int RadioButton::instances = 0; -float RadioButton::mAlpha = 1.0; -Image *RadioButton::radioNormal; -Image *RadioButton::radioChecked; -Image *RadioButton::radioDisabled; -Image *RadioButton::radioDisabledChecked; -Image *RadioButton::radioNormalHi; -Image *RadioButton::radioCheckedHi; - -RadioButton::RadioButton(const std::string &caption, const std::string &group, - bool marked): - gcn::RadioButton(caption, group, marked) +RadioButton::RadioButton(const std::string &caption, + const std::string &group, + bool marked) + : gcn::RadioButton(caption, group, marked) { - if (instances == 0) - { - radioNormal = Theme::getImageFromTheme("radioout.png"); - radioChecked = Theme::getImageFromTheme("radioin.png"); - radioDisabled = Theme::getImageFromTheme("radioout.png"); - radioDisabledChecked = Theme::getImageFromTheme("radioin.png"); - radioNormalHi = Theme::getImageFromTheme("radioout_highlight.png"); - radioCheckedHi = Theme::getImageFromTheme("radioin_highlight.png"); - radioNormal->setAlpha(mAlpha); - radioChecked->setAlpha(mAlpha); - radioDisabled->setAlpha(mAlpha); - radioDisabledChecked->setAlpha(mAlpha); - radioNormalHi->setAlpha(mAlpha); - radioCheckedHi->setAlpha(mAlpha); - } - - instances++; + auto &skin = gui->getTheme()->getSkin(SkinType::RadioButton); + setWidth(skin.getMinWidth() + 2 * skin.padding + skin.spacing + getFont()->getWidth(caption)); + setHeight(skin.getMinHeight() + 2 * skin.padding); } -RadioButton::~RadioButton() +void RadioButton::draw(gcn::Graphics* graphics) { - instances--; + WidgetState widgetState(this); + if (mHasMouse) + widgetState.flags |= STATE_HOVERED; + if (isSelected()) + widgetState.flags |= STATE_SELECTED; - if (instances == 0) - { - radioNormal->decRef(); - radioChecked->decRef(); - radioDisabled->decRef(); - radioDisabledChecked->decRef(); - radioNormalHi->decRef(); - radioCheckedHi->decRef(); - } -} + auto &skin = gui->getTheme()->getSkin(SkinType::RadioButton); + skin.draw(static_cast<Graphics *>(graphics), widgetState); -void RadioButton::drawBox(gcn::Graphics* graphics) -{ - if (config.guiAlpha != mAlpha) + if (auto skinState = skin.getState(widgetState.flags)) { - mAlpha = config.guiAlpha; - radioNormal->setAlpha(mAlpha); - radioChecked->setAlpha(mAlpha); - radioDisabled->setAlpha(mAlpha); - radioDisabledChecked->setAlpha(mAlpha); - radioNormalHi->setAlpha(mAlpha); - radioCheckedHi->setAlpha(mAlpha); + auto &textFormat = skinState->textFormat; + TextRenderer::renderText(static_cast<Graphics *>(graphics), + getCaption(), + skin.getMinWidth() + skin.padding + skin.spacing, + skin.padding, + Graphics::LEFT, + textFormat.bold ? boldFont : getFont(), + textFormat); } - - Image *box = nullptr; - - if (isEnabled()) - if (isSelected()) - if (mHasMouse) - box = radioCheckedHi; - else - box = radioChecked; - else - if (mHasMouse) - box = radioNormalHi; - else - box = radioNormal; - else - if (isSelected()) - box = radioDisabledChecked; - else - box = radioDisabled; - - if (box) - static_cast<Graphics*>(graphics)->drawImage(box, 2, 2); -} - -void RadioButton::draw(gcn::Graphics* graphics) -{ - graphics->pushClipArea(gcn::Rectangle(1, 1, getWidth() - 1, - getHeight() - 1)); - - drawBox(graphics); - - graphics->popClipArea(); - - graphics->setFont(getFont()); - graphics->setColor(getForegroundColor()); - - int h = getHeight() + getHeight() / 2; - graphics->drawText(getCaption(), h - 2, 0); } void RadioButton::mouseEntered(gcn::MouseEvent& event) diff --git a/src/gui/widgets/radiobutton.h b/src/gui/widgets/radiobutton.h index 2a96ff6e..fda43d01 100644 --- a/src/gui/widgets/radiobutton.h +++ b/src/gui/widgets/radiobutton.h @@ -19,55 +19,40 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef RADIOBUTTON_H -#define RADIOBUTTON_H +#pragma once #include <guichan/widgets/radiobutton.hpp> -class Image; - /** * Guichan based RadioButton with custom look */ class RadioButton : public gcn::RadioButton { public: - RadioButton(const std::string &caption,const std::string &group, - bool marked = false); - - ~RadioButton() override; + RadioButton(const std::string &caption, + const std::string &group, + bool marked = false); /** - * Draws the radiobutton, not the caption. + * Implementation of the draw method. */ - void drawBox(gcn::Graphics* graphics) override; + void draw(gcn::Graphics *graphics) override; /** - * Implementation of the draw methods. - * Thus, avoiding the rhomb around the radio button. + * Overridden because box is drawn in RadioButton::draw. */ - void draw(gcn::Graphics* graphics) override; + void drawBox(gcn::Graphics *graphics) override {} /** * Called when the mouse enteres the widget area. */ - void mouseEntered(gcn::MouseEvent& event) override; + void mouseEntered(gcn::MouseEvent &event) override; /** * Called when the mouse leaves the widget area. */ - void mouseExited(gcn::MouseEvent& event) override; + void mouseExited(gcn::MouseEvent &event) override; private: - static int instances; - static float mAlpha; bool mHasMouse = false; - static Image *radioNormal; - static Image *radioChecked; - static Image *radioDisabled; - static Image *radioDisabledChecked; - static Image *radioNormalHi; - static Image *radioCheckedHi; }; - -#endif // RADIOBUTTON_H diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp index dd29a977..0c5a7fb5 100644 --- a/src/gui/widgets/resizegrip.cpp +++ b/src/gui/widgets/resizegrip.cpp @@ -21,48 +21,22 @@ #include "gui/widgets/resizegrip.h" -#include "configuration.h" #include "graphics.h" -#include "resources/image.h" +#include "gui/gui.h" #include "resources/theme.h" #include <guichan/graphics.hpp> -Image *ResizeGrip::gripImage = nullptr; -int ResizeGrip::mInstances = 0; -float ResizeGrip::mAlpha = 1.0; - -ResizeGrip::ResizeGrip(const std::string &image) -{ - if (mInstances == 0) - { - // Load the grip image - gripImage = Theme::getImageFromTheme(image); - gripImage->setAlpha(mAlpha); - } - - mInstances++; - - setWidth(gripImage->getWidth() + 2); - setHeight(gripImage->getHeight() + 2); -} - -ResizeGrip::~ResizeGrip() +ResizeGrip::ResizeGrip() { - mInstances--; - - if (mInstances == 0) - gripImage->decRef(); + auto &skin = gui->getTheme()->getSkin(SkinType::ResizeGrip); + setSize(skin.width, skin.height); } void ResizeGrip::draw(gcn::Graphics *graphics) { - if (config.guiAlpha != mAlpha) - { - mAlpha = config.guiAlpha; - gripImage->setAlpha(mAlpha); - } - - static_cast<Graphics*>(graphics)->drawImage(gripImage, 0, 0); + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), + SkinType::ResizeGrip, + WidgetState(this)); } diff --git a/src/gui/widgets/resizegrip.h b/src/gui/widgets/resizegrip.h index d2f8ca4d..9b4e0611 100644 --- a/src/gui/widgets/resizegrip.h +++ b/src/gui/widgets/resizegrip.h @@ -19,13 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef RESIZEGRIP_H -#define RESIZEGRIP_H +#pragma once #include <guichan/widget.hpp> -class Image; - /** * Resize grip. The resize grip is part of a resizable Window. It relies on the * fact that uncaught mouse events are automatically routed to the parent @@ -36,19 +33,10 @@ class Image; class ResizeGrip : public gcn::Widget { public: - ResizeGrip(const std::string &image = "resize.png"); - - ~ResizeGrip() override; + ResizeGrip(); /** * Draws the resize grip. */ void draw(gcn::Graphics *graphics) override; - - private: - static Image *gripImage; /**< Resize grip image */ - static int mInstances; /**< Number of resize grip instances */ - static float mAlpha; }; - -#endif diff --git a/src/gui/widgets/scrollarea.cpp b/src/gui/widgets/scrollarea.cpp index 225a231d..1dec34be 100644 --- a/src/gui/widgets/scrollarea.cpp +++ b/src/gui/widgets/scrollarea.cpp @@ -21,24 +21,14 @@ #include "gui/widgets/scrollarea.h" -#include "configuration.h" #include "graphics.h" -#include "resources/image.h" -#include "resources/theme.h" +#include "gui/gui.h" -#include "utils/dtor.h" - -int ScrollArea::instances = 0; -float ScrollArea::mAlpha = 1.0; -ImageRect ScrollArea::background; -ImageRect ScrollArea::vMarker; -ImageRect ScrollArea::vMarkerHi; -Image *ScrollArea::buttons[4][2]; +#include <guichan/exception.hpp> ScrollArea::ScrollArea() { - addWidgetListener(this); init(); } @@ -51,24 +41,11 @@ ScrollArea::ScrollArea(gcn::Widget *widget): ScrollArea::~ScrollArea() { delete getContent(); +} - instances--; - - if (instances == 0) - { - std::for_each(background.grid, background.grid + 9, dtor<Image*>()); - std::for_each(vMarker.grid, vMarker.grid + 9, dtor<Image*>()); - std::for_each(vMarkerHi.grid, vMarkerHi.grid + 9, dtor<Image*>()); - - buttons[UP][0]->decRef(); - buttons[UP][1]->decRef(); - buttons[DOWN][0]->decRef(); - buttons[DOWN][1]->decRef(); - buttons[LEFT][0]->decRef(); - buttons[LEFT][1]->decRef(); - buttons[RIGHT][0]->decRef(); - buttons[RIGHT][1]->decRef(); - } +void ScrollArea::setShowButtons(bool showButtons) +{ + mShowButtons = showButtons; } void ScrollArea::init() @@ -76,83 +53,27 @@ void ScrollArea::init() // Draw background by default setOpaque(true); - setUpButtonScrollAmount(2); - setDownButtonScrollAmount(2); - setLeftButtonScrollAmount(2); - setRightButtonScrollAmount(2); - - if (instances == 0) - { - // Load the background skin - Image *textbox = Theme::getImageFromTheme("deepbox.png"); - const int bggridx[4] = {0, 3, 28, 31}; - const int bggridy[4] = {0, 3, 28, 31}; - int a = 0; - - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - background.grid[a] = textbox->getSubImage( - bggridx[x], bggridy[y], - bggridx[x + 1] - bggridx[x] + 1, - bggridy[y + 1] - bggridy[y] + 1); - a++; - } - } - background.setAlpha(config.guiAlpha); + auto theme = gui->getTheme(); - textbox->decRef(); + int scrollBarWidth = theme->getSkin(SkinType::ScrollAreaVBar).width; + if (scrollBarWidth > 0) + setScrollbarWidth(scrollBarWidth); - // Load vertical scrollbar skin - Image *vscroll = Theme::getImageFromTheme("vscroll_grey.png"); - Image *vscrollHi = Theme::getImageFromTheme("vscroll_highlight.png"); + auto &scrollAreaSkin = theme->getSkin(SkinType::ScrollArea); + setShowButtons(scrollAreaSkin.showButtons); - int vsgridx[4] = {0, 4, 7, 11}; - int vsgridy[4] = {0, 4, 15, 19}; - a = 0; - - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - vMarker.grid[a] = vscroll->getSubImage( - vsgridx[x], vsgridy[y], - vsgridx[x + 1] - vsgridx[x], - vsgridy[y + 1] - vsgridy[y]); - vMarkerHi.grid[a] = vscrollHi->getSubImage( - vsgridx[x], vsgridy[y], - vsgridx[x + 1] - vsgridx[x], - vsgridy[y + 1] - vsgridy[y]); - a++; - } - } + if (auto content = getContent()) + content->setFrameSize(scrollAreaSkin.padding); - vMarker.setAlpha(config.guiAlpha); - vMarkerHi.setAlpha(config.guiAlpha); - - vscroll->decRef(); - vscrollHi->decRef(); - - buttons[UP][0] = - Theme::getImageFromTheme("vscroll_up_default.png"); - buttons[DOWN][0] = - Theme::getImageFromTheme("vscroll_down_default.png"); - buttons[LEFT][0] = - Theme::getImageFromTheme("hscroll_left_default.png"); - buttons[RIGHT][0] = - Theme::getImageFromTheme("hscroll_right_default.png"); - buttons[UP][1] = - Theme::getImageFromTheme("vscroll_up_pressed.png"); - buttons[DOWN][1] = - Theme::getImageFromTheme("vscroll_down_pressed.png"); - buttons[LEFT][1] = - Theme::getImageFromTheme("hscroll_left_pressed.png"); - buttons[RIGHT][1] = - Theme::getImageFromTheme("hscroll_right_pressed.png"); - } + // The base color is only used when rendering a square in the corner where + // the scrollbars meet. We disable rendering of this square by setting the + // base color to transparent. + setBaseColor(gcn::Color(0, 0, 0, 0)); - instances++; + setUpButtonScrollAmount(5); + setDownButtonScrollAmount(5); + setLeftButtonScrollAmount(5); + setRightButtonScrollAmount(5); } void ScrollArea::logic() @@ -201,159 +122,237 @@ void ScrollArea::logic() } } -void ScrollArea::updateAlpha() +void ScrollArea::draw(gcn::Graphics *graphics) { - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (alpha != mAlpha) - { - mAlpha = alpha; + if (getFrameSize() == 0) + drawFrame(graphics); - background.setAlpha(mAlpha); - vMarker.setAlpha(mAlpha); - vMarkerHi.setAlpha(mAlpha); - } + gcn::ScrollArea::draw(graphics); } -void ScrollArea::draw(gcn::Graphics *graphics) +void ScrollArea::drawFrame(gcn::Graphics *graphics) { - if (mVBarVisible) - { - drawUpButton(graphics); - drawDownButton(graphics); - drawVBar(graphics); - drawVMarker(graphics); - } - - if (mHBarVisible) - { - drawLeftButton(graphics); - drawRightButton(graphics); - drawHBar(graphics); - drawHMarker(graphics); - } + if (!mOpaque) + return; - if (mHBarVisible && mVBarVisible) - { - graphics->setColor(getBaseColor()); - graphics->fillRectangle(gcn::Rectangle(getWidth() - mScrollbarWidth, - getHeight() - mScrollbarWidth, - mScrollbarWidth, - mScrollbarWidth)); - } + const int bs = getFrameSize(); - updateAlpha(); + WidgetState state(this); + state.width += bs * 2; + state.height += + bs * 2; - drawChildren(graphics); + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollArea, state); } -void ScrollArea::drawFrame(gcn::Graphics *graphics) +void ScrollArea::drawChildren(gcn::Graphics *graphics) { - if (mOpaque) - { - const int bs = getFrameSize(); - const int w = getWidth() + bs * 2; - const int h = getHeight() + bs * 2; + auto g = static_cast<Graphics*>(graphics); + g->pushClipRect(getChildrenArea()); - static_cast<Graphics*>(graphics)-> - drawImageRect(0, 0, w, h, background); - } + gcn::ScrollArea::drawChildren(graphics); + + g->popClipRect(); } void ScrollArea::setOpaque(bool opaque) { mOpaque = opaque; - setFrameSize(mOpaque ? 2 : 0); + + auto &skin = gui->getTheme()->getSkin(SkinType::ScrollArea); + setFrameSize(mOpaque ? skin.frameSize : 0); } -void ScrollArea::drawButton(gcn::Graphics *graphics, BUTTON_DIR dir) +void ScrollArea::drawBackground(gcn::Graphics *graphics) { - int state = 0; - gcn::Rectangle dim; + // background is drawn as part of the frame instead +} - switch (dir) - { - case UP: - state = mUpButtonPressed ? 1 : 0; - dim = getUpButtonDimension(); - break; - case DOWN: - state = mDownButtonPressed ? 1 : 0; - dim = getDownButtonDimension(); - break; - case LEFT: - state = mLeftButtonPressed ? 1 : 0; - dim = getLeftButtonDimension(); - break; - case RIGHT: - state = mRightButtonPressed ? 1 : 0; - dim = getRightButtonDimension(); - break; - } +static void drawButton(gcn::Graphics *graphics, + SkinType skinType, + bool pressed, + const gcn::Rectangle &dim) +{ + WidgetState state(dim); + if (pressed) + state.flags |= STATE_SELECTED; - static_cast<Graphics*>(graphics)-> - drawImage(buttons[dir][state], dim.x, dim.y); + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), skinType, state); } void ScrollArea::drawUpButton(gcn::Graphics *graphics) { - drawButton(graphics, UP); + if (!mShowButtons) + return; + + drawButton(graphics, SkinType::ButtonUp, mUpButtonPressed, getUpButtonDimension()); } void ScrollArea::drawDownButton(gcn::Graphics *graphics) { - drawButton(graphics, DOWN); + if (!mShowButtons) + return; + + drawButton(graphics, SkinType::ButtonDown, mDownButtonPressed, getDownButtonDimension()); } void ScrollArea::drawLeftButton(gcn::Graphics *graphics) { - drawButton(graphics, LEFT); + if (!mShowButtons) + return; + + drawButton(graphics, SkinType::ButtonLeft, mLeftButtonPressed, getLeftButtonDimension()); } void ScrollArea::drawRightButton(gcn::Graphics *graphics) { - drawButton(graphics, RIGHT); + if (!mShowButtons) + return; + + drawButton(graphics, SkinType::ButtonRight, mRightButtonPressed, getRightButtonDimension()); } void ScrollArea::drawVBar(gcn::Graphics *graphics) { - const gcn::Rectangle dim = getVerticalBarDimension(); - graphics->setColor(gcn::Color(0, 0, 0, 32)); - graphics->fillRectangle(dim); - graphics->setColor(gcn::Color(255, 255, 255)); + WidgetState state(getVerticalBarDimension()); + if (mHasMouse && (mX > (getWidth() - getScrollbarWidth()))) + state.flags |= STATE_HOVERED; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaVBar, state); } void ScrollArea::drawHBar(gcn::Graphics *graphics) { - const gcn::Rectangle dim = getHorizontalBarDimension(); - graphics->setColor(gcn::Color(0, 0, 0, 32)); - graphics->fillRectangle(dim); - graphics->setColor(gcn::Color(255, 255, 255)); + WidgetState state(getHorizontalBarDimension()); + if (mHasMouse && (mY > (getHeight() - getScrollbarWidth()))) + state.flags |= STATE_HOVERED; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaHBar, state); } void ScrollArea::drawVMarker(gcn::Graphics *graphics) { - gcn::Rectangle dim = getVerticalMarkerDimension(); + WidgetState state(getVerticalMarkerDimension()); + if (state.height == 0) + return; - if ((mHasMouse) && (mX > (getWidth() - getScrollbarWidth()))) - static_cast<Graphics*>(graphics)-> - drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarkerHi); - else - static_cast<Graphics*>(graphics)-> - drawImageRect(dim.x, dim.y, dim.width, dim.height,vMarker); + if (mHasMouse && (mX > (getWidth() - getScrollbarWidth()))) + state.flags |= STATE_HOVERED; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaVMarker, state); } void ScrollArea::drawHMarker(gcn::Graphics *graphics) { - gcn::Rectangle dim = getHorizontalMarkerDimension(); + WidgetState state(getHorizontalMarkerDimension()); + if (state.width == 0) + return; + + if (mHasMouse && (mY > (getHeight() - getScrollbarWidth()))) + state.flags |= STATE_HOVERED; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaHMarker, state); +} + +/** + * Code copied from gcn::ScrollArea::checkPolicies to make sure it takes the + * frame size of the content into account. + */ +void ScrollArea::checkPolicies() +{ + int w = getWidth(); + int h = getHeight(); - if ((mHasMouse) && (mY > (getHeight() - getScrollbarWidth()))) - static_cast<Graphics*>(graphics)-> - drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarkerHi); - else - static_cast<Graphics*>(graphics)-> - drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarker); + mHBarVisible = false; + mVBarVisible = false; + + if (!getContent()) + { + mHBarVisible = (mHPolicy == SHOW_ALWAYS); + mVBarVisible = (mVPolicy == SHOW_ALWAYS); + return; + } + + const int contentFrameSize = getContent()->getFrameSize(); + w -= 2 * contentFrameSize; + h -= 2 * contentFrameSize; + + if (mHPolicy == SHOW_AUTO && + mVPolicy == SHOW_AUTO) + { + if (getContent()->getWidth() <= w + && getContent()->getHeight() <= h) + { + mHBarVisible = false; + mVBarVisible = false; + } + + if (getContent()->getWidth() > w) + { + mHBarVisible = true; + } + + if ((getContent()->getHeight() > h) + || (mHBarVisible && getContent()->getHeight() > h - mScrollbarWidth)) + { + mVBarVisible = true; + } + + if (mVBarVisible && getContent()->getWidth() > w - mScrollbarWidth) + { + mHBarVisible = true; + } + + return; + } + + switch (mHPolicy) + { + case SHOW_NEVER: + mHBarVisible = false; + break; + + case SHOW_ALWAYS: + mHBarVisible = true; + break; + + case SHOW_AUTO: + if (mVPolicy == SHOW_NEVER) + { + mHBarVisible = getContent()->getWidth() > w; + } + else // (mVPolicy == SHOW_ALWAYS) + { + mHBarVisible = getContent()->getWidth() > w - mScrollbarWidth; + } + break; + + default: + throw GCN_EXCEPTION("Horizontal scroll policy invalid."); + } + + switch (mVPolicy) + { + case SHOW_NEVER: + mVBarVisible = false; + break; + + case SHOW_ALWAYS: + mVBarVisible = true; + break; + + case SHOW_AUTO: + if (mHPolicy == SHOW_NEVER) + { + mVBarVisible = getContent()->getHeight() > h; + } + else // (mHPolicy == SHOW_ALWAYS) + { + mVBarVisible = getContent()->getHeight() > h - mScrollbarWidth; + } + break; + default: + throw GCN_EXCEPTION("Vertical scroll policy invalid."); + } } void ScrollArea::mouseMoved(gcn::MouseEvent& event) @@ -372,8 +371,299 @@ void ScrollArea::mouseExited(gcn::MouseEvent& event) mHasMouse = false; } -void ScrollArea::widgetResized(const gcn::Event &event) +/** + * Code copied from gcn::ScrollArea::mousePressed to make it call our custom + * getVerticalMarkerDimension and getHorizontalMarkerDimension functions. + */ +void ScrollArea::mousePressed(gcn::MouseEvent &mouseEvent) { - getContent()->setSize(getWidth() - 2 * getFrameSize(), - getHeight() - 2 * getFrameSize()); + int x = mouseEvent.getX(); + int y = mouseEvent.getY(); + + if (getUpButtonDimension().isPointInRect(x, y)) + { + setVerticalScrollAmount(getVerticalScrollAmount() + - mUpButtonScrollAmount); + mUpButtonPressed = true; + } + else if (getDownButtonDimension().isPointInRect(x, y)) + { + setVerticalScrollAmount(getVerticalScrollAmount() + + mDownButtonScrollAmount); + mDownButtonPressed = true; + } + else if (getLeftButtonDimension().isPointInRect(x, y)) + { + setHorizontalScrollAmount(getHorizontalScrollAmount() + - mLeftButtonScrollAmount); + mLeftButtonPressed = true; + } + else if (getRightButtonDimension().isPointInRect(x, y)) + { + setHorizontalScrollAmount(getHorizontalScrollAmount() + + mRightButtonScrollAmount); + mRightButtonPressed = true; + } + else if (getVerticalMarkerDimension().isPointInRect(x, y)) + { + mIsHorizontalMarkerDragged = false; + mIsVerticalMarkerDragged = true; + + mVerticalMarkerDragOffset = y - getVerticalMarkerDimension().y; + } + else if (getVerticalBarDimension().isPointInRect(x,y)) + { + if (y < getVerticalMarkerDimension().y) + { + setVerticalScrollAmount(getVerticalScrollAmount() + - (int)(getChildrenArea().height * 0.95)); + } + else + { + setVerticalScrollAmount(getVerticalScrollAmount() + + (int)(getChildrenArea().height * 0.95)); + } + } + else if (getHorizontalMarkerDimension().isPointInRect(x, y)) + { + mIsHorizontalMarkerDragged = true; + mIsVerticalMarkerDragged = false; + + mHorizontalMarkerDragOffset = x - getHorizontalMarkerDimension().x; + } + else if (getHorizontalBarDimension().isPointInRect(x,y)) + { + if (x < getHorizontalMarkerDimension().x) + { + setHorizontalScrollAmount(getHorizontalScrollAmount() + - (int)(getChildrenArea().width * 0.95)); + } + else + { + setHorizontalScrollAmount(getHorizontalScrollAmount() + + (int)(getChildrenArea().width * 0.95)); + } + } +} + +/** + * Code copied from gcn::ScrollArea::mouseDragged to make it call our custom + * getVerticalMarkerDimension and getHorizontalMarkerDimension functions. + */ +void ScrollArea::mouseDragged(gcn::MouseEvent &mouseEvent) +{ + if (mIsVerticalMarkerDragged) + { + int pos = mouseEvent.getY() - getVerticalBarDimension().y - mVerticalMarkerDragOffset; + int length = getVerticalMarkerDimension().height; + + gcn::Rectangle barDim = getVerticalBarDimension(); + + if ((barDim.height - length) > 0) + { + setVerticalScrollAmount((getVerticalMaxScroll() * pos) + / (barDim.height - length)); + } + else + { + setVerticalScrollAmount(0); + } + } + + if (mIsHorizontalMarkerDragged) + { + int pos = mouseEvent.getX() - getHorizontalBarDimension().x - mHorizontalMarkerDragOffset; + int length = getHorizontalMarkerDimension().width; + + gcn::Rectangle barDim = getHorizontalBarDimension(); + + if ((barDim.width - length) > 0) + { + setHorizontalScrollAmount((getHorizontalMaxScroll() * pos) + / (barDim.width - length)); + } + else + { + setHorizontalScrollAmount(0); + } + } + + mouseEvent.consume(); +} + +gcn::Rectangle ScrollArea::getUpButtonDimension() +{ + if (!mVBarVisible || !mShowButtons) + return gcn::Rectangle(); + + return gcn::Rectangle(getWidth() - mScrollbarWidth, 0, mScrollbarWidth, mScrollbarWidth); +} + +gcn::Rectangle ScrollArea::getDownButtonDimension() +{ + if (!mVBarVisible || !mShowButtons) + return gcn::Rectangle(); + + gcn::Rectangle dim(getWidth() - mScrollbarWidth, + getHeight() - mScrollbarWidth, + mScrollbarWidth, + mScrollbarWidth); + + if (mHBarVisible) + dim.y -= mScrollbarWidth; + + return dim; +} + +gcn::Rectangle ScrollArea::getLeftButtonDimension() +{ + if (!mHBarVisible || !mShowButtons) + return gcn::Rectangle(); + + return gcn::Rectangle(0, getHeight() - mScrollbarWidth, mScrollbarWidth, mScrollbarWidth); +} + +gcn::Rectangle ScrollArea::getRightButtonDimension() +{ + if (!mHBarVisible || !mShowButtons) + return gcn::Rectangle(); + + gcn::Rectangle dim(getWidth() - mScrollbarWidth, + getHeight() - mScrollbarWidth, + mScrollbarWidth, + mScrollbarWidth); + + if (mVBarVisible) + dim.x -= mScrollbarWidth; + + return dim; +} + +gcn::Rectangle ScrollArea::getVerticalBarDimension() +{ + if (!mVBarVisible) + return gcn::Rectangle(); + + gcn::Rectangle dim(getWidth() - mScrollbarWidth, + getUpButtonDimension().height, + mScrollbarWidth, + getHeight() + - getUpButtonDimension().height + - getDownButtonDimension().height); + + if (mHBarVisible) + dim.height -= mScrollbarWidth; + + if (dim.height < 0) + dim.height = 0; + + return dim; +} + +gcn::Rectangle ScrollArea::getHorizontalBarDimension() +{ + if (!mHBarVisible) + return gcn::Rectangle(); + + gcn::Rectangle dim(getLeftButtonDimension().width, + getHeight() - mScrollbarWidth, + getWidth() + - getLeftButtonDimension().width + - getRightButtonDimension().width, + mScrollbarWidth); + + if (mVBarVisible) + dim.width -= mScrollbarWidth; + + if (dim.width < 0) + dim.width = 0; + + return dim; +} + +static void getMarkerValues(int barSize, + int maxScroll, int scrollAmount, + int contentHeight, int viewHeight, + int fixedMarkerSize, int minMarkerSize, + int &markerSize, int &markerPos) +{ + if (fixedMarkerSize == 0) + { + if (contentHeight != 0 && contentHeight > viewHeight) + markerSize = std::max((barSize * viewHeight) / contentHeight, minMarkerSize); + else + markerSize = barSize; + } + else + { + if (contentHeight > viewHeight) + markerSize = fixedMarkerSize; + else + markerSize = 0; + } + + // Hide the marker when it doesn't fit + if (markerSize > barSize) + markerSize = 0; + + if (maxScroll != 0) + markerPos = ((barSize - markerSize) * scrollAmount + maxScroll / 2) / maxScroll; + else + markerPos = 0; +} + +gcn::Rectangle ScrollArea::getVerticalMarkerDimension() +{ + if (!mVBarVisible) + return gcn::Rectangle(); + + auto &markerSkin = gui->getTheme()->getSkin(SkinType::ScrollAreaVMarker); + const gcn::Rectangle barDim = getVerticalBarDimension(); + + int contentHeight = 0; + if (auto content = getContent()) + contentHeight = content->getHeight() + content->getFrameSize() * 2; + + int length; + int pos; + + getMarkerValues(barDim.height, + getVerticalMaxScroll(), + getVerticalScrollAmount(), + contentHeight, + getChildrenArea().height, + markerSkin.height, + mScrollbarWidth, + length, + pos); + + return gcn::Rectangle(barDim.x, barDim.y + pos, mScrollbarWidth, length); +} + +gcn::Rectangle ScrollArea::getHorizontalMarkerDimension() +{ + if (!mHBarVisible) + return gcn::Rectangle(); + + auto &markerSkin = gui->getTheme()->getSkin(SkinType::ScrollAreaHMarker); + const gcn::Rectangle barDim = getHorizontalBarDimension(); + + int contentWidth = 0; + if (auto content = getContent()) + contentWidth = content->getWidth() + content->getFrameSize() * 2; + + int length; + int pos; + + getMarkerValues(barDim.width, + getHorizontalMaxScroll(), + getHorizontalScrollAmount(), + contentWidth, + getChildrenArea().width, + markerSkin.width, + mScrollbarWidth, + length, + pos); + + return gcn::Rectangle(barDim.x + pos, barDim.y, length, mScrollbarWidth); } diff --git a/src/gui/widgets/scrollarea.h b/src/gui/widgets/scrollarea.h index 2fae2d4b..0ba10578 100644 --- a/src/gui/widgets/scrollarea.h +++ b/src/gui/widgets/scrollarea.h @@ -19,14 +19,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SCROLLAREA_H -#define SCROLLAREA_H +#pragma once #include <guichan/widgets/scrollarea.hpp> -#include <guichan/widgetlistener.hpp> - -class Image; -class ImageRect; /** * A scroll area. @@ -35,9 +30,11 @@ class ImageRect; * content. However, it won't delete a previously set content widget when * setContent is called! * + * Also overrides several functions to support fixed-size scroll bar markers. + * * \ingroup GUI */ -class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener +class ScrollArea : public gcn::ScrollArea { public: /** @@ -59,18 +56,18 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener ~ScrollArea() override; /** - * Logic function optionally adapts width or height of contents. This - * depends on the scrollbar settings. + * Sets whether the scroll bar buttons are shown. */ - void logic() override; + void setShowButtons(bool showButtons); /** - * Update the alpha value to the graphic components. + * Logic function optionally adapts width or height of contents. This + * depends on the scrollbar settings. */ - static void updateAlpha(); + void logic() override; /** - * Draws the scroll area. + * Overridden to draw the frame if its size is 0. */ void draw(gcn::Graphics *graphics) override; @@ -80,6 +77,11 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener void drawFrame(gcn::Graphics *graphics) override; /** + * Applies clipping to the contents. + */ + void drawChildren(gcn::Graphics *graphics) override; + + /** * Sets whether the widget should draw its background or not. */ void setOpaque(bool opaque); @@ -92,34 +94,28 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener /** * Called when the mouse moves in the widget area. */ - void mouseMoved(gcn::MouseEvent& event) override; + void mouseMoved(gcn::MouseEvent &event) override; /** * Called when the mouse enteres the widget area. */ - void mouseEntered(gcn::MouseEvent& event) override; + void mouseEntered(gcn::MouseEvent &event) override; /** * Called when the mouse leaves the widget area. */ - void mouseExited(gcn::MouseEvent& event) override; + void mouseExited(gcn::MouseEvent &event) override; - void widgetResized(const gcn::Event &event) override; + void mousePressed(gcn::MouseEvent &mouseEvent) override; + void mouseDragged(gcn::MouseEvent &mouseEvent) override; protected: - enum BUTTON_DIR { - UP, - DOWN, - LEFT, - RIGHT - }; - /** * Initializes the scroll area. */ void init(); - void drawButton(gcn::Graphics *graphics, BUTTON_DIR dir); + void drawBackground(gcn::Graphics *graphics) override; void drawUpButton(gcn::Graphics *graphics) override; void drawDownButton(gcn::Graphics *graphics) override; void drawLeftButton(gcn::Graphics *graphics) override; @@ -129,17 +125,31 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener void drawVMarker(gcn::Graphics *graphics) override; void drawHMarker(gcn::Graphics *graphics) override; - static int instances; - static float mAlpha; - static ImageRect background; - static ImageRect vMarker; - static ImageRect vMarkerHi; - static Image *buttons[4][2]; + void checkPolicies() override; + + /** + * Shadowing these functions from gcn::ScrollArea with versions that + * support hiding the buttons. We need to make sure we always use these + * versions. + */ + gcn::Rectangle getUpButtonDimension(); + gcn::Rectangle getDownButtonDimension(); + gcn::Rectangle getLeftButtonDimension(); + gcn::Rectangle getRightButtonDimension(); + gcn::Rectangle getVerticalBarDimension(); + gcn::Rectangle getHorizontalBarDimension(); + + /** + * Shadowing these functions from gcn::ScrollArea with versions that + * supports fixed-size scroll bar markers. We need to make sure we + * always use these versions. + */ + gcn::Rectangle getVerticalMarkerDimension(); + gcn::Rectangle getHorizontalMarkerDimension(); int mX = 0; int mY = 0; bool mHasMouse = false; bool mOpaque = true; + bool mShowButtons = true; }; - -#endif diff --git a/src/gui/widgets/setuptab.h b/src/gui/widgets/setuptab.h index 0cc35a98..78cef5b2 100644 --- a/src/gui/widgets/setuptab.h +++ b/src/gui/widgets/setuptab.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_SETUPTAB_H -#define GUI_SETUPTAB_H +#pragma once #include "gui/widgets/container.h" @@ -58,5 +57,3 @@ protected: private: std::string mName; }; - -#endif diff --git a/src/gui/widgets/shopitems.h b/src/gui/widgets/shopitems.h index e213f67c..1b6e1727 100644 --- a/src/gui/widgets/shopitems.h +++ b/src/gui/widgets/shopitems.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SHOP_H -#define SHOP_H +#pragma once #include <guichan/listmodel.hpp> @@ -111,5 +110,3 @@ class ShopItems : public gcn::ListModel /** Look for duplicate entries on addition. */ bool mMergeDuplicates; }; - -#endif // SHOP_H diff --git a/src/gui/widgets/shoplistbox.cpp b/src/gui/widgets/shoplistbox.cpp index 31c733a6..e2313c85 100644 --- a/src/gui/widgets/shoplistbox.cpp +++ b/src/gui/widgets/shoplistbox.cpp @@ -71,14 +71,13 @@ void ShopListBox::draw(gcn::Graphics *gcnGraphics) return; const int alpha = (int)(config.guiAlpha * 255.0f); - const gcn::Color &highlightColor = - Theme::getThemeColor(Theme::HIGHLIGHT, alpha); - const gcn::Color &backgroundColor = - Theme::getThemeColor(Theme::BACKGROUND, alpha); - const gcn::Color &warningColor = - Theme::getThemeColor(Theme::SHOP_WARNING, alpha); - const gcn::Color &textColor = - Theme::getThemeColor(Theme::TEXT); + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + auto backgroundColor = Theme::getThemeColor(Theme::BACKGROUND); + auto warningColor = Theme::getThemeColor(Theme::SHOP_WARNING); + auto textColor = Theme::getThemeColor(Theme::TEXT); + highlightColor.a = alpha; + backgroundColor.a = alpha; + warningColor.a = alpha; auto *graphics = static_cast<Graphics*>(gcnGraphics); @@ -168,8 +167,7 @@ void ShopListBox::mouseMoved(gcn::MouseEvent &event) } else { - Item *item = mShopItems->at(index); - if (item) + if (Item *item = mShopItems->at(index)) { mItemPopup->setItem(item->getInfo()); mItemPopup->position(viewport->getMouseX(), viewport->getMouseY()); diff --git a/src/gui/widgets/shoplistbox.h b/src/gui/widgets/shoplistbox.h index 4dbd756b..f6a1b12a 100644 --- a/src/gui/widgets/shoplistbox.h +++ b/src/gui/widgets/shoplistbox.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SHOPLISTBOX_H -#define SHOPLISTBOX_H +#pragma once #include "gui/widgets/listbox.h" @@ -97,5 +96,3 @@ class ShopListBox : public ListBox bool mPriceCheck; }; - -#endif // SHOPLISTBOX_H diff --git a/src/gui/widgets/shortcutcontainer.cpp b/src/gui/widgets/shortcutcontainer.cpp index 5925752e..9271b1f4 100644 --- a/src/gui/widgets/shortcutcontainer.cpp +++ b/src/gui/widgets/shortcutcontainer.cpp @@ -21,10 +21,18 @@ #include "gui/widgets/shortcutcontainer.h" -float ShortcutContainer::mAlpha = 1.0; +#include "gui/gui.h" + +#include "resources/theme.h" ShortcutContainer::ShortcutContainer() { + addMouseListener(this); + addWidgetListener(this); + + auto &skin = gui->getTheme()->getSkin(SkinType::ShortcutBox); + mBoxWidth = skin.width; + mBoxHeight = skin.height; } void ShortcutContainer::widgetResized(const gcn::Event &event) diff --git a/src/gui/widgets/shortcutcontainer.h b/src/gui/widgets/shortcutcontainer.h index cab20f27..35a88d7f 100644 --- a/src/gui/widgets/shortcutcontainer.h +++ b/src/gui/widgets/shortcutcontainer.h @@ -19,15 +19,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SHORTCUTCONTAINER_H -#define SHORTCUTCONTAINER_H +#pragma once #include <guichan/mouselistener.hpp> #include <guichan/widget.hpp> #include <guichan/widgetlistener.hpp> -class Image; - /** * A generic shortcut container. * @@ -45,20 +42,18 @@ class ShortcutContainer : public gcn::Widget, */ void draw(gcn::Graphics *graphics) override = 0; + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics *graphics) override {} + /** * Invoked when a widget changes its size. This is used to determine * the new height of the container. */ void widgetResized(const gcn::Event &event) override; - int getMaxItems() const - { return mMaxItems; } - - int getBoxWidth() const - { return mBoxWidth; } - - int getBoxHeight() const - { return mBoxHeight; } + int getMaxItems() const { return mMaxItems; } + int getBoxWidth() const { return mBoxWidth; } + int getBoxHeight() const { return mBoxHeight; } protected: /** @@ -70,10 +65,6 @@ class ShortcutContainer : public gcn::Widget, */ int getIndexFromGrid(int pointX, int pointY) const; - Image *mBackgroundImg; - - static float mAlpha; - int mMaxItems = 0; int mBoxWidth = 0; int mBoxHeight = 0; @@ -82,5 +73,3 @@ class ShortcutContainer : public gcn::Widget, int mGridWidth = 1; int mGridHeight = 1; }; - -#endif diff --git a/src/gui/widgets/slider.cpp b/src/gui/widgets/slider.cpp index a7ba37e8..bad10c15 100644 --- a/src/gui/widgets/slider.cpp +++ b/src/gui/widgets/slider.cpp @@ -21,19 +21,11 @@ #include "gui/widgets/slider.h" -#include "configuration.h" #include "graphics.h" -#include "resources/image.h" +#include "gui/gui.h" #include "resources/theme.h" -Image *Slider::hStart, *Slider::hMid, *Slider::hEnd, *Slider::hGrip; -Image *Slider::vStart, *Slider::vMid, *Slider::vEnd, *Slider::vGrip; -Image *Slider::hStartHi, *Slider::hMidHi, *Slider::hEndHi, *Slider::hGripHi; -Image *Slider::vStartHi, *Slider::vMidHi, *Slider::vEndHi, *Slider::vGripHi; -float Slider::mAlpha = 1.0; -int Slider::mInstances = 0; - Slider::Slider(double scaleEnd): gcn::Slider(scaleEnd) { @@ -46,154 +38,30 @@ Slider::Slider(double scaleStart, double scaleEnd): init(); } -Slider::~Slider() -{ - mInstances--; - - if (mInstances == 0) - { - delete hStart; - delete hMid; - delete hEnd; - delete hGrip; - delete vStart; - delete vMid; - delete vEnd; - delete vGrip; - delete hStartHi; - delete hMidHi; - delete hEndHi; - delete hGripHi; - delete vStartHi; - delete vMidHi; - delete vEndHi; - delete vGripHi; - } -} - void Slider::init() { - int x, y, w, h,o1,o2; - setFrameSize(0); - - // Load resources - if (mInstances == 0) - { - Image *slider = Theme::getImageFromTheme("slider.png"); - Image *sliderHi = Theme::getImageFromTheme("slider_hilight.png"); - - x = 0; y = 0; - w = 15; h = 6; - o1 = 4; o2 = 11; - hStart = slider->getSubImage(x, y, o1 - x, h); - hMid = slider->getSubImage(o1, y, o2 - o1, h); - hEnd = slider->getSubImage(o2, y, w - o2 + x, h); - hStartHi = sliderHi->getSubImage(x, y, o1 - x, h); - hMidHi = sliderHi->getSubImage(o1, y, o2 - o1, h); - hEndHi = sliderHi->getSubImage(o2, y, w - o2 + x, h); - - x = 6; y = 8; - w = 9; h = 10; - hGrip = slider->getSubImage(x, y, w, h); - hGripHi = sliderHi->getSubImage(x, y, w, h); - - x = 0; y = 6; - w = 6; h = 21; - o1 = 10; o2 = 18; - vStart = slider->getSubImage(x, y, w, o1 - y); - vMid = slider->getSubImage(x, o1, w, o2 - o1); - vEnd = slider->getSubImage(x, o2, w, h - o2 + y); - vStartHi = sliderHi->getSubImage(x, y, w, o1 - y); - vMidHi = sliderHi->getSubImage(x, o1, w, o2 - o1); - vEndHi = sliderHi->getSubImage(x, o2, w, h - o2 + y); - - x = 6; y = 8; - w = 9; h = 10; - vGrip = slider->getSubImage(x, y, w, h); - vGripHi = sliderHi->getSubImage(x, y, w, h); - - slider->decRef(); - sliderHi->decRef(); - } - - mInstances++; - - setMarkerLength(hGrip->getWidth()); -} - -void Slider::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (alpha != mAlpha) - { - mAlpha = alpha; - hStart->setAlpha(mAlpha); - hMid->setAlpha(mAlpha); - hEnd->setAlpha(mAlpha); - hGrip->setAlpha(mAlpha); - hStartHi->setAlpha(mAlpha); - hMidHi->setAlpha(mAlpha); - hEndHi->setAlpha(mAlpha); - hGripHi->setAlpha(mAlpha); - - vStart->setAlpha(mAlpha); - vMid->setAlpha(mAlpha); - vEnd->setAlpha(mAlpha); - vGrip->setAlpha(mAlpha); - vStartHi->setAlpha(mAlpha); - vMidHi->setAlpha(mAlpha); - vEndHi->setAlpha(mAlpha); - vGripHi->setAlpha(mAlpha); - } - + auto theme = gui->getTheme(); + auto &sliderSkin = theme->getSkin(SkinType::Slider); + auto &sliderHandleSkin = theme->getSkin(SkinType::SliderHandle); + setFrameSize(sliderSkin.frameSize); + setMarkerLength(sliderHandleSkin.getMinWidth()); + + setWidth(100); + setHeight(sliderSkin.getMinHeight() + 2 * sliderSkin.padding); } void Slider::draw(gcn::Graphics *graphics) { - int w = getWidth(); - int h = getHeight(); - int x = 0; - int y = mHasMouse?(h - hStartHi->getHeight()) / 2:(h - hStart->getHeight()) / 2; - - updateAlpha(); - - if (!mHasMouse) - { - static_cast<Graphics*>(graphics)->drawImage(hStart, x, y); - - w -= hStart->getWidth() + hEnd->getWidth(); - x += hStart->getWidth(); - - static_cast<Graphics*>(graphics)-> - drawImagePattern(hMid, x, y, w, hMid->getHeight()); + WidgetState state(this); + if (mHasMouse) + state.flags |= STATE_HOVERED; - x += w; - static_cast<Graphics*>(graphics)->drawImage(hEnd, x, y); - } - else - { - static_cast<Graphics*>(graphics)->drawImage(hStartHi, x, y); + auto theme = gui->getTheme(); + theme->drawSkin(static_cast<Graphics*>(graphics), SkinType::Slider, state); - w -= hStartHi->getWidth() + hEndHi->getWidth(); - x += hStartHi->getWidth(); - - static_cast<Graphics*>(graphics)-> - drawImagePattern(hMidHi, x, y, w, hMidHi->getHeight()); - - x += w; - static_cast<Graphics*>(graphics)->drawImage(hEndHi, x, y); - } - - drawMarker(graphics); -} - -void Slider::drawMarker(gcn::Graphics *graphics) -{ - static_cast<Graphics*>(graphics)-> - drawImage(mHasMouse?hGripHi:hGrip, getMarkerPosition(), - (getHeight() - (mHasMouse?hGripHi:hGrip)->getHeight()) / 2); + WidgetState handleState(state); + handleState.x += getMarkerPosition(); + theme->drawSkin(static_cast<Graphics*>(graphics), SkinType::SliderHandle, handleState); } void Slider::mouseEntered(gcn::MouseEvent& event) @@ -205,4 +73,3 @@ void Slider::mouseExited(gcn::MouseEvent& event) { mHasMouse = false; } - diff --git a/src/gui/widgets/slider.h b/src/gui/widgets/slider.h index 3896cb52..52b3b3af 100644 --- a/src/gui/widgets/slider.h +++ b/src/gui/widgets/slider.h @@ -19,13 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SLIDER_H -#define SLIDER_H +#pragma once #include <guichan/widgets/slider.hpp> -class Image; - /** * Slider widget. Same as the Guichan slider but with custom look. * @@ -47,22 +44,16 @@ class Slider : public gcn::Slider */ Slider(double scaleStart, double scaleEnd); - ~Slider() override; - - /** - * Update the alpha value to the graphic components. - */ - static void updateAlpha(); - /** * Draws the slider. */ void draw(gcn::Graphics *graphics) override; - /** - * Draws the marker. - */ - void drawMarker(gcn::Graphics *graphics) override; + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics *graphics) override {} + + // Marker is drawn in Slider::draw + void drawMarker(gcn::Graphics *graphics) override {} /** * Called when the mouse enteres the widget area. @@ -80,13 +71,5 @@ class Slider : public gcn::Slider */ void init(); - static Image *hStart, *hMid, *hEnd, *hGrip; - static Image *vStart, *vMid, *vEnd, *vGrip; - static Image *hStartHi, *hMidHi, *hEndHi, *hGripHi; - static Image *vStartHi, *vMidHi, *vEndHi, *vGripHi; bool mHasMouse = false; - static float mAlpha; - static int mInstances; }; - -#endif diff --git a/src/gui/widgets/spacer.h b/src/gui/widgets/spacer.h index f6a210dc..2fda6e8c 100644 --- a/src/gui/widgets/spacer.h +++ b/src/gui/widgets/spacer.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SPACER_H -#define SPACER_H +#pragma once #include "guichan/graphics.hpp" #include "guichan/widget.hpp" @@ -48,5 +47,3 @@ class Spacer : public gcn::Widget */ void draw(gcn::Graphics *g) override {} }; - -#endif // SPACER_H diff --git a/src/gui/widgets/tab.cpp b/src/gui/widgets/tab.cpp index 64f49eac..b2779c4f 100644 --- a/src/gui/widgets/tab.cpp +++ b/src/gui/widgets/tab.cpp @@ -21,149 +21,90 @@ #include "gui/widgets/tab.h" -#include "configuration.h" #include "graphics.h" +#include "gui/gui.h" +#include "gui/widgets/label.h" #include "gui/widgets/tabbedarea.h" -#include "resources/image.h" #include "resources/theme.h" -#include "utils/dtor.h" - #include <guichan/widgets/label.hpp> -int Tab::mInstances = 0; -float Tab::mAlpha = 1.0; - -enum { - TAB_STANDARD, // 0 - TAB_HIGHLIGHTED, // 1 - TAB_SELECTED, // 2 - TAB_UNUSED, // 3 - TAB_COUNT // 4 - Must be last. -}; - -struct TabData -{ - char const *file; - int gridX[4]; - int gridY[4]; -}; - -static TabData const data[TAB_COUNT] = { - { "tab.png", {0, 9, 16, 25}, {0, 13, 19, 20} }, - { "tab_hilight.png", {0, 9, 16, 25}, {0, 13, 19, 20} }, - { "tabselected.png", {0, 9, 16, 25}, {0, 4, 12, 20} }, - { "tab.png", {0, 9, 16, 25}, {0, 13, 19, 20} } -}; - -ImageRect Tab::tabImg[TAB_COUNT]; - -Tab::Tab() : - mTabColor(&Theme::getThemeColor(Theme::TAB)) -{ - init(); -} - -Tab::~Tab() -{ - mInstances--; - - if (mInstances == 0) - { - for (auto &imgRect : tabImg) - { - std::for_each(imgRect.grid, imgRect.grid + 9, dtor<Image*>()); - } - } -} - -void Tab::init() +Tab::Tab() { setFocusable(false); - setFrameSize(0); - mFlash = false; - if (mInstances == 0) - { - // Load the skin - Image *tab[TAB_COUNT]; - - for (int mode = 0; mode < TAB_COUNT; mode++) - { - tab[mode] = Theme::getImageFromTheme(data[mode].file); - int a = 0; - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - tabImg[mode].grid[a] = tab[mode]->getSubImage( - data[mode].gridX[x], data[mode].gridY[y], - data[mode].gridX[x + 1] - data[mode].gridX[x] + 1, - data[mode].gridY[y + 1] - data[mode].gridY[y] + 1); - a++; - } - } - tabImg[mode].setAlpha(mAlpha); - tab[mode]->decRef(); - } - } - mInstances++; + // Replace the label with customized version + delete mLabel; + mLabel = new Label(); + add(mLabel); + + auto &skin = gui->getTheme()->getSkin(SkinType::Tab); + setFrameSize(skin.frameSize); + mPadding = skin.padding; + mLabel->setPosition(mPadding, mPadding); } -void Tab::updateAlpha() +void Tab::setCaption(const std::string &caption) { - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); + mLabel->setCaption(caption); + mLabel->adjustSize(); - // TODO We don't need to do this for every tab on every draw - // Maybe use a config listener to do it as the value changes. - if (alpha != mAlpha) - { - mAlpha = alpha; + setSize(mLabel->getWidth() + mPadding * 2, + mLabel->getHeight() + mPadding * 2); - for (auto &t : tabImg) - { - t.setAlpha(mAlpha); - } - } + if (mTabbedArea) + static_cast<TabbedArea*>(mTabbedArea)->adjustTabPositions(); } void Tab::draw(gcn::Graphics *graphics) { - int mode = TAB_STANDARD; + if (getFrameSize() == 0) + drawFrame(graphics); - // check which type of tab to draw - if (mTabbedArea) + // if tab is selected, it doesnt need to highlight activity + if (mTabbedArea && mTabbedArea->isTabSelected(this)) + mFlash = false; + + uint8_t flags = 0; + if (mHasMouse) + flags |= STATE_HOVERED; + if (mTabbedArea && mTabbedArea->isTabSelected(this)) + flags |= STATE_SELECTED; + + auto &skin = gui->getTheme()->getSkin(SkinType::Tab); + if (auto state = skin.getState(flags)) { - mLabel->setForegroundColor(*mTabColor); - if (mTabbedArea->isTabSelected(this)) - { - mode = TAB_SELECTED; - // if tab is selected, it doesnt need to highlight activity - mFlash = false; - } - else if (mHasMouse) - { - mode = TAB_HIGHLIGHTED; - } + gcn::Color foregroundColor = state->textFormat.color; if (mFlash) - { - mLabel->setForegroundColor(Theme::getThemeColor(Theme::TAB_FLASH)); - } + foregroundColor = Theme::getThemeColor(Theme::TAB_FLASH); + else if (mTabColor) + foregroundColor = *mTabColor; + + auto label = static_cast<Label*>(mLabel); + label->setForegroundColor(foregroundColor); + label->setOutlineColor(state->textFormat.outlineColor); + label->setShadowColor(state->textFormat.shadowColor); } - updateAlpha(); - - // draw tab - static_cast<Graphics*>(graphics)-> - drawImageRect(0, 0, getWidth(), getHeight(), tabImg[mode]); - // draw label drawChildren(graphics); } +void Tab::drawFrame(gcn::Graphics *graphics) +{ + WidgetState state(this); + state.width += getFrameSize() * 2; + state.height += getFrameSize() * 2; + if (mHasMouse) + state.flags |= STATE_HOVERED; + if (mTabbedArea && mTabbedArea->isTabSelected(this)) + state.flags |= STATE_SELECTED; + + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::Tab, state); +} + void Tab::setTabColor(const gcn::Color *color) { mTabColor = color; diff --git a/src/gui/widgets/tab.h b/src/gui/widgets/tab.h index 86650257..534abaff 100644 --- a/src/gui/widgets/tab.h +++ b/src/gui/widgets/tab.h @@ -19,12 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TAB_H -#define TAB_H +#pragma once #include <guichan/widgets/tab.hpp> -class ImageRect; class TabbedArea; /** @@ -35,19 +33,25 @@ class Tab : public gcn::Tab { public: Tab(); - ~Tab() override; /** - * Update the alpha value to the graphic components. + * Sets the caption of the tab. Shadowing gcn::Tab::setCaption, which + * shouldn't be used because it calls gcn::Tab::adjustSize, which does + * not take into account the padding. */ - static void updateAlpha(); + void setCaption(const std::string& caption); /** - * Draw the tabbed area. + * Draw the tab. */ void draw(gcn::Graphics *graphics) override; /** + * Draw the tab frame. + */ + void drawFrame(gcn::Graphics *graphics) override; + + /** * Set the normal color fo the tab's text. */ void setTabColor(const gcn::Color *color); @@ -62,15 +66,7 @@ class Tab : public gcn::Tab virtual void setCurrent() {} private: - /** Load images if no other instances exist yet */ - void init(); - - static ImageRect tabImg[4]; /**< Tab state graphics */ - static int mInstances; /**< Number of tab instances */ - static float mAlpha; - - const gcn::Color *mTabColor; - bool mFlash; + const gcn::Color *mTabColor = nullptr; + bool mFlash = false; + int mPadding = 8; }; - -#endif diff --git a/src/gui/widgets/tabbedarea.cpp b/src/gui/widgets/tabbedarea.cpp index af0c11cb..3609791a 100644 --- a/src/gui/widgets/tabbedarea.cpp +++ b/src/gui/widgets/tabbedarea.cpp @@ -21,6 +21,8 @@ #include "gui/widgets/tabbedarea.h" +#include "graphics.h" + #include "gui/widgets/tab.h" #include <guichan/widgets/container.hpp> @@ -30,13 +32,13 @@ TabbedArea::TabbedArea() mWidgetContainer->setOpaque(false); addWidgetListener(this); - mArrowButton[0] = new Button(std::string(), "shift_left", this); - mArrowButton[1] = new Button(std::string(), "shift_right", this); + mArrowButton[0] = std::make_unique<Button>(std::string(), "shift_left", this); + mArrowButton[1] = std::make_unique<Button>(std::string(), "shift_right", this); mArrowButton[0]->setButtonIcon("tab_arrows_left.png"); mArrowButton[1]->setButtonIcon("tab_arrows_right.png"); - add(mArrowButton[0]); - add(mArrowButton[1]); + add(mArrowButton[0].get()); + add(mArrowButton[1].get()); widgetResized(nullptr); } @@ -61,7 +63,12 @@ void TabbedArea::draw(gcn::Graphics *graphics) if (mTabs.empty()) return; + auto g = static_cast<Graphics*>(graphics); + g->pushClipRect(getChildrenArea()); + drawChildren(graphics); + + g->popClipRect(); } gcn::Widget *TabbedArea::getWidget(const std::string &name) const @@ -105,7 +112,7 @@ void TabbedArea::addTab(const std::string &caption, gcn::Widget *widget) addTab(tab, widget); } -void TabbedArea::removeTab(Tab *tab) +void TabbedArea::removeTab(gcn::Tab *tab) { if (tab == mSelectedTab) { @@ -206,9 +213,8 @@ void TabbedArea::updateTabsWidth() { mTabsWidth = 0; for (const auto &[tab, _] : mTabs) - { mTabsWidth += tab->getWidth(); - } + updateVisibleTabsWidth(); } @@ -216,9 +222,7 @@ void TabbedArea::updateVisibleTabsWidth() { mVisibleTabsWidth = 0; for (unsigned int i = mTabScrollIndex; i < mTabs.size(); ++i) - { mVisibleTabsWidth += mTabs[i].first->getWidth(); - } } void TabbedArea::adjustTabPositions() @@ -265,7 +269,7 @@ void TabbedArea::action(const gcn::ActionEvent& actionEvent) { if (actionEvent.getId() == "shift_left") { - if (mTabScrollIndex) + if (mTabScrollIndex > 0) --mTabScrollIndex; } else if (actionEvent.getId() == "shift_right") @@ -282,33 +286,18 @@ void TabbedArea::action(const gcn::ActionEvent& actionEvent) void TabbedArea::updateArrowEnableState() { updateTabsWidth(); - if (mTabsWidth > getWidth() - 2) - { - mArrowButton[0]->setVisible(true); - mArrowButton[1]->setVisible(true); - } - else - { - mArrowButton[0]->setVisible(false); - mArrowButton[1]->setVisible(false); + + const bool arrowButtonsVisible = mTabsWidth > getWidth() - 2; + mArrowButton[0]->setVisible(arrowButtonsVisible); + mArrowButton[1]->setVisible(arrowButtonsVisible); + + if (!arrowButtonsVisible) mTabScrollIndex = 0; - } - // Left arrow consistency check - if (!mTabScrollIndex) - mArrowButton[0]->setEnabled(false); - else - mArrowButton[0]->setEnabled(true); + mArrowButton[0]->setEnabled(mTabScrollIndex > 0); // Right arrow consistency check - if (mVisibleTabsWidth < getWidth() - 2 - - mArrowButton[0]->getWidth() - - mArrowButton[1]->getWidth()) - { - mArrowButton[1]->setEnabled(false); - } - else - { - mArrowButton[1]->setEnabled(true); - } + const int availableWidth = getWidth() - 2 - mArrowButton[0]->getWidth() + - mArrowButton[1]->getWidth(); + mArrowButton[1]->setEnabled(mVisibleTabsWidth >= availableWidth); } diff --git a/src/gui/widgets/tabbedarea.h b/src/gui/widgets/tabbedarea.h index 8e6dcb5f..5d0ccfcc 100644 --- a/src/gui/widgets/tabbedarea.h +++ b/src/gui/widgets/tabbedarea.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TABBEDAREA_H -#define TABBEDAREA_H +#pragma once #include <guichan/widget.hpp> #include <guichan/widgetlistener.hpp> @@ -29,6 +28,7 @@ #include "gui/widgets/button.h" +#include <memory> #include <string> class Tab; @@ -36,7 +36,7 @@ class Tab; /** * A tabbed area, the same as the guichan tabbed area in 0.8, but extended */ -class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener +class TabbedArea final : public gcn::TabbedArea, public gcn::WidgetListener { public: TabbedArea(); @@ -86,12 +86,12 @@ class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener void addTab(const std::string &caption, gcn::Widget *widget) override; /** - * Overload the remove tab function as it's broken in guichan 0.8. + * Override the remove tab function as it's broken in guichan 0.8. */ - void removeTab(Tab *tab); + void removeTab(gcn::Tab *tab) override; /** - * Overload the logic function since it's broken in guichan 0.8. + * Override the logic function since it's broken in guichan 0.8. */ void logic() override; @@ -115,7 +115,7 @@ class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener private: /** The tab arrows */ - Button *mArrowButton[2]; + std::unique_ptr<Button> mArrowButton[2]; /** Check whether the arrow should be clickable */ void updateArrowEnableState(); @@ -151,5 +151,3 @@ class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener */ unsigned mTabScrollIndex = 0; }; - -#endif diff --git a/src/gui/widgets/table.cpp b/src/gui/widgets/table.cpp index 905bb166..909617a0 100644 --- a/src/gui/widgets/table.cpp +++ b/src/gui/widgets/table.cpp @@ -21,8 +21,7 @@ #include "gui/widgets/table.h" -#include "configuration.h" - +#include "gui/gui.h" #include "gui/sdlinput.h" #include "resources/theme.h" @@ -33,13 +32,10 @@ #include <guichan/graphics.hpp> #include <guichan/key.hpp> -float GuiTable::mAlpha = 1.0; - class GuiTableActionListener : public gcn::ActionListener { public: - GuiTableActionListener(GuiTable *_table, gcn::Widget *_widget, int _row, int _column); - + GuiTableActionListener(GuiTable *table, gcn::Widget *widget, int row, int column); ~GuiTableActionListener() override; void action(const gcn::ActionEvent& actionEvent) override; @@ -81,18 +77,12 @@ void GuiTableActionListener::action(const gcn::ActionEvent& actionEvent) } -GuiTable::GuiTable(TableModel *initial_model, gcn::Color background, +GuiTable::GuiTable(TableModel *initialModel, gcn::Color background, bool opacity) : - mLinewiseMode(false), - mWrappingEnabled(false), mOpaque(opacity), - mBackgroundColor(background), - mModel(nullptr), - mSelectedRow(0), - mSelectedColumn(0), - mTopWidget(nullptr) + mBackgroundColor(background) { - setModel(initial_model); + setModel(initialModel); setFocusable(true); addMouseListener(this); @@ -269,16 +259,19 @@ void GuiTable::draw(gcn::Graphics* graphics) if (!mModel) return; - if (config.guiAlpha != mAlpha) - mAlpha = config.guiAlpha; + const auto guiAlpha = gui->getTheme()->getGuiAlpha(); if (mOpaque) { - graphics->setColor(Theme::getThemeColor(Theme::BACKGROUND, - (int)(mAlpha * 255.0f))); + auto backgroundColor = Theme::getThemeColor(Theme::BACKGROUND); + backgroundColor.a = guiAlpha; + graphics->setColor(backgroundColor); graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); } + auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT); + highlightColor.a = guiAlpha; + // First, determine how many rows we need to draw, and where we should start. int first_row = -(getY() / getRowHeight()); @@ -320,8 +313,7 @@ void GuiTable::draw(gcn::Graphics* graphics) widget->setDimension(bounds); - graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, - (int)(mAlpha * 255.0f))); + graphics->setColor(highlightColor); if (mLinewiseMode && r == mSelectedRow && c == 0) { @@ -368,7 +360,7 @@ void GuiTable::moveToBottom(gcn::Widget *widget) mTopWidget = nullptr; } -gcn::Rectangle GuiTable::getChildrenArea() const +gcn::Rectangle GuiTable::getChildrenArea() { return gcn::Rectangle(0, 0, getWidth(), getHeight()); } @@ -487,7 +479,7 @@ void GuiTable::modelUpdated(bool completed) } } -gcn::Widget *GuiTable::getWidgetAt(int x, int y) const +gcn::Widget *GuiTable::getWidgetAt(int x, int y) { int row = getRowForY(y); int column = getColumnForX(x); @@ -497,14 +489,12 @@ gcn::Widget *GuiTable::getWidgetAt(int x, int y) const if (row > -1 && column > -1) { - gcn::Widget *w = mModel->getElementAt(row, column); - if (w && w->isFocusable()) - return w; - else - return nullptr; // Grab the event locally + if (gcn::Widget *w = mModel->getElementAt(row, column)) + if (w->isFocusable()) + return w; } - else - return nullptr; + + return nullptr; // Grab the event locally } int GuiTable::getRowForY(int y) const @@ -516,8 +506,8 @@ int GuiTable::getRowForY(int y) const if (row < 0 || row >= mModel->getRows()) return -1; - else - return row; + + return row; } int GuiTable::getColumnForX(int x) const @@ -534,24 +524,23 @@ int GuiTable::getColumnForX(int x) const if (column < 0 || column >= mModel->getColumns()) return -1; - else - return column; + + return column; } void GuiTable::_setFocusHandler(gcn::FocusHandler* focusHandler) { gcn::Widget::_setFocusHandler(focusHandler); - if (mModel) + if (!mModel) + return; + + for (int r = 0; r < mModel->getRows(); ++r) { - for (int r = 0; r < mModel->getRows(); ++r) + for (int c = 0; c < mModel->getColumns(); ++c) { - for (int c = 0; c < mModel->getColumns(); ++c) - { - gcn::Widget *w = mModel->getElementAt(r, c); - if (w) - w->_setFocusHandler(focusHandler); - } + if (gcn::Widget *w = mModel->getElementAt(r, c)) + w->_setFocusHandler(focusHandler); } } } diff --git a/src/gui/widgets/table.h b/src/gui/widgets/table.h index a9202022..88350928 100644 --- a/src/gui/widgets/table.h +++ b/src/gui/widgets/table.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TABLE_H -#define TABLE_H +#pragma once #include "tablemodel.h" @@ -41,16 +40,16 @@ class GuiTableActionListener; * * \ingroup GUI */ -class GuiTable : public gcn::Widget, - public gcn::MouseListener, - public gcn::KeyListener, - public TableModelListener +class GuiTable final : public gcn::Widget, + public gcn::MouseListener, + public gcn::KeyListener, + public TableModelListener { // so that the action listener can call distributeActionEvent friend class GuiTableActionListener; public: - GuiTable(TableModel * initial_model = nullptr, gcn::Color background = 0xffffff, + GuiTable(TableModel *initialModel = nullptr, gcn::Color background = 0xffffff, bool opacity = true); ~GuiTable() override; @@ -85,7 +84,7 @@ public: void setWrappingEnabled(bool wrappingEnabled) {mWrappingEnabled = wrappingEnabled;} - gcn::Rectangle getChildrenArea() const; + gcn::Rectangle getChildrenArea() override; /** * Toggle whether to use linewise selection mode, in which the table selects @@ -103,7 +102,10 @@ public: // Inherited from Widget void draw(gcn::Graphics* graphics) override; - virtual gcn::Widget *getWidgetAt(int x, int y) const; + // Overridden to disable drawing of the frame + void drawFrame(gcn::Graphics* graphics) override {} + + gcn::Widget *getWidgetAt(int x, int y) override; void moveToTop(gcn::Widget *child) override; @@ -120,7 +122,7 @@ public: * * @param opaque True if the table should be opaque, false otherwise. */ - virtual void setOpaque(bool opaque) {mOpaque = opaque;} + void setOpaque(bool opaque) {mOpaque = opaque;} /** * Checks if the table is opaque, that is if the table area displays its @@ -128,7 +130,7 @@ public: * * @return True if the table is opaque, false otherwise. */ - virtual bool isOpaque() const {return mOpaque;} + bool isOpaque() const {return mOpaque;} // Inherited from MouseListener void mousePressed(gcn::MouseEvent& mouseEvent) override; @@ -144,42 +146,35 @@ public: protected: /** Frees all action listeners on inner widgets. */ - virtual void uninstallActionListeners(); + void uninstallActionListeners(); /** Installs all action listeners on inner widgets. */ - virtual void installActionListeners(); + void installActionListeners(); - virtual int getRowHeight() const; - virtual int getColumnWidth(int i) const; + int getRowHeight() const; + int getColumnWidth(int i) const; private: int getRowForY(int y) const; // -1 on error int getColumnForX(int x) const; // -1 on error void recomputeDimensions(); - bool mLinewiseMode; - bool mWrappingEnabled; - bool mOpaque; - static float mAlpha; + bool mLinewiseMode = false; + bool mWrappingEnabled = false; + bool mOpaque; /** * Holds the background color of the table. */ gcn::Color mBackgroundColor; - TableModel *mModel; + TableModel *mModel = nullptr; - int mSelectedRow; - int mSelectedColumn; - - /** Number of frames to skip upwards when drawing the selected widget. */ - int mPopFramesNr; + int mSelectedRow = 0; + int mSelectedColumn = 0; /** If someone moves a fresh widget to the top, we must display it. */ - gcn::Widget *mTopWidget; + gcn::Widget *mTopWidget = nullptr; /** Vector for compactness; used as a list in practice. */ std::vector<GuiTableActionListener *> mActionListeners; }; - - -#endif // TABLE_H diff --git a/src/gui/widgets/tablemodel.h b/src/gui/widgets/tablemodel.h index d4274e39..2ba36556 100644 --- a/src/gui/widgets/tablemodel.h +++ b/src/gui/widgets/tablemodel.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TABLE_MODEL_H -#define TABLE_MODEL_H +#pragma once #include <guichanfwd.h> @@ -143,5 +142,3 @@ protected: std::vector<gcn::Widget *> mTableModel; std::vector<int> mWidths; }; - -#endif // TABLE_MODEL_H diff --git a/src/gui/widgets/textbox.cpp b/src/gui/widgets/textbox.cpp index 419fa16e..6cc514fe 100644 --- a/src/gui/widgets/textbox.cpp +++ b/src/gui/widgets/textbox.cpp @@ -21,15 +21,20 @@ #include "gui/widgets/textbox.h" +#include "gui/gui.h" #include "resources/theme.h" +#include "textrenderer.h" #include <guichan/font.hpp> #include <sstream> -TextBox::TextBox() : - mTextColor(&Theme::getThemeColor(Theme::TEXT)) +TextBox::TextBox() { + auto &palette = gui->getTheme()->getPalette(0); + mTextColor = &palette.getColor(Theme::TEXT); + mOutlineColor = palette.getOutlineColor(Theme::TEXT); + setOpaque(false); setFrameSize(0); mMinWidth = getWidth(); @@ -147,3 +152,42 @@ void TextBox::setTextWrapped(const std::string &text, int minDimension) gcn::TextBox::setText(wrappedStream.str()); } + +/** + * Overridden so we can customize the color and outline of the text. + */ +void TextBox::draw(gcn::Graphics *graphics) +{ + unsigned int i; + + if (mOpaque) + { + graphics->setColor(getBackgroundColor()); + graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); + } + + if (isFocused() && isEditable()) + { + drawCaret(graphics, + getFont()->getWidth(mTextRows[mCaretRow].substr(0, mCaretColumn)), + mCaretRow * getFont()->getHeight()); + } + + graphics->setColor(*mTextColor); + graphics->setFont(getFont()); + + for (i = 0; i < mTextRows.size(); i++) + { + // Move the text one pixel so we can have a caret before a letter. + TextRenderer::renderText(graphics, + mTextRows[i], + 1, + i * getFont()->getHeight(), + gcn::Graphics::LEFT, + *mTextColor, + getFont(), + mOutlineColor.has_value(), + false, + mOutlineColor); + } +} diff --git a/src/gui/widgets/textbox.h b/src/gui/widgets/textbox.h index bcf09ee2..49a5a2ad 100644 --- a/src/gui/widgets/textbox.h +++ b/src/gui/widgets/textbox.h @@ -19,11 +19,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TEXTBOX_H -#define TEXTBOX_H +#pragma once #include <guichan/widgets/textbox.hpp> +#include <optional> + /** * A text box, meant to be used inside a scroll area. Same as the Guichan text * box except this one doesn't have a background or border, instead completely @@ -39,6 +40,9 @@ class TextBox : public gcn::TextBox void setTextColor(const gcn::Color *color) { mTextColor = color; } + void setOutlineColor(const std::optional<gcn::Color> &color) + { mOutlineColor = color; } + /** * Sets the text after wrapping it to the current width of the widget. */ @@ -52,15 +56,10 @@ class TextBox : public gcn::TextBox /** * Draws the text. */ - void draw(gcn::Graphics *graphics) override - { - setForegroundColor(*mTextColor); - gcn::TextBox::draw(graphics); - } + void draw(gcn::Graphics *graphics) override; private: int mMinWidth; const gcn::Color *mTextColor; + std::optional<gcn::Color> mOutlineColor; }; - -#endif diff --git a/src/gui/widgets/textfield.cpp b/src/gui/widgets/textfield.cpp index dd0adecd..6c866477 100644 --- a/src/gui/widgets/textfield.cpp +++ b/src/gui/widgets/textfield.cpp @@ -21,107 +21,63 @@ #include "gui/widgets/textfield.h" -#include "configuration.h" #include "graphics.h" +#include "gui/gui.h" #include "gui/sdlinput.h" -#include "resources/image.h" #include "resources/theme.h" #include "utils/copynpaste.h" -#include "utils/dtor.h" #include "utils/stringutils.h" #include <guichan/font.hpp> #include <SDL.h> -#undef DELETE //Win32 compatibility hack - -int TextField::instances = 0; -float TextField::mAlpha = 1.0; -ImageRect TextField::skin; - -TextField::TextField(const std::string &text, bool loseFocusOnTab): - gcn::TextField(text) -{ - setFrameSize(2); - - mLoseFocusOnTab = loseFocusOnTab; - - if (instances == 0) - { - // Load the skin - Image *textbox = Theme::getImageFromTheme("deepbox.png"); - int gridx[4] = {0, 3, 28, 31}; - int gridy[4] = {0, 3, 28, 31}; - int a = 0; - - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - skin.grid[a] = textbox->getSubImage( - gridx[x], gridy[y], - gridx[x + 1] - gridx[x] + 1, - gridy[y + 1] - gridy[y] + 1); - a++; - } - } - skin.setAlpha(config.guiAlpha); - - textbox->decRef(); - } - - instances++; -} - -TextField::~TextField() +TextField::TextField(const std::string &text, bool loseFocusOnTab) + : gcn::TextField(text) + , mLoseFocusOnTab(loseFocusOnTab) { - instances--; - - if (instances == 0) - std::for_each(skin.grid, skin.grid + 9, dtor<Image*>()); -} + auto &skin = gui->getTheme()->getSkin(SkinType::TextField); + setFrameSize(skin.frameSize); + mPadding = skin.padding; -void TextField::updateAlpha() -{ - float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); - - if (alpha != mAlpha) - { - mAlpha = alpha; - skin.setAlpha(mAlpha); - } + setWidth(getFont()->getWidth(mText) + 2 * mPadding); + setHeight(getFont()->getHeight() + 2 * mPadding); + fixScroll(); } void TextField::draw(gcn::Graphics *graphics) { - updateAlpha(); + if (getFrameSize() == 0) + drawFrame(graphics); + + auto g = static_cast<Graphics *>(graphics); + g->pushClipRect(gcn::Rectangle(0, 0, getWidth(), getHeight())); if (isFocused()) { drawCaret(graphics, - getFont()->getWidth(mText.substr(0, mCaretPosition)) - - mXScroll); + getFont()->getWidth(mText.substr(0, mCaretPosition)) - mXScroll); } graphics->setColor(Theme::getThemeColor(Theme::TEXT)); graphics->setFont(getFont()); - graphics->drawText(mText, 1 - mXScroll, 1); + graphics->drawText(mText, mPadding - mXScroll, mPadding); + + g->popClipRect(); } void TextField::drawFrame(gcn::Graphics *graphics) { - //updateAlpha(); -> Not useful... + const int bs = getFrameSize(); - int bs = getFrameSize(); - int w = getWidth() + bs * 2; - int h = getHeight() + bs * 2; + WidgetState state(this); + state.width += bs * 2; + state.height += bs * 2; - static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, skin); + gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::TextField, state); } void TextField::setNumeric(bool numeric) @@ -156,6 +112,12 @@ int TextField::getValue() const return value; } +void TextField::drawCaret(gcn::Graphics *graphics, int x) +{ + graphics->setColor(Theme::getThemeColor(Theme::CARET)); + graphics->drawLine(mPadding + x, mPadding, mPadding + x, getHeight() - mPadding); +} + void TextField::keyPressed(gcn::KeyEvent &keyEvent) { switch (keyEvent.getKey().getValue()) diff --git a/src/gui/widgets/textfield.h b/src/gui/widgets/textfield.h index 9235f4b8..b84dd723 100644 --- a/src/gui/widgets/textfield.h +++ b/src/gui/widgets/textfield.h @@ -19,16 +19,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TEXTFIELD_H -#define TEXTFIELD_H +#pragma once #include <guichan/widgets/textfield.hpp> #include <vector> class TextInput; -class ImageRect; -class TextField; struct TextHistory { @@ -79,7 +76,6 @@ class TextField : public gcn::TextField */ TextField(const std::string &text = std::string(), bool loseFocusOnTab = true); - ~TextField() override; /** * Draws the text field. @@ -87,11 +83,6 @@ class TextField : public gcn::TextField void draw(gcn::Graphics *graphics) override; /** - * Update the alpha value to the graphic components. - */ - static void updateAlpha(); - - /** * Draws the background and border. */ void drawFrame(gcn::Graphics *graphics) override; @@ -139,42 +130,41 @@ class TextField : public gcn::TextField * Sets the TextField's source of autocomplete. Passing null will * disable autocomplete. */ - void setAutoComplete(AutoCompleteLister *lister) - { mAutoComplete = lister; } + void setAutoComplete(AutoCompleteLister *lister) + { mAutoComplete = lister; } - /** + /** * Returns the TextField's source of autocomplete. */ - AutoCompleteLister *getAutoComplete() const - { return mAutoComplete; } + AutoCompleteLister *getAutoComplete() const + { return mAutoComplete; } - /** + /** * Sets the TextField's source of input history. */ - void setHistory(TextHistory *history) - { mHistory = history; } + void setHistory(TextHistory *history) + { mHistory = history; } - /** + /** * Returns the TextField's source of input history. */ - TextHistory *getHistory() const - { return mHistory; } + TextHistory *getHistory() const + { return mHistory; } + + protected: + void drawCaret(gcn::Graphics *graphics, int x) override; private: void autoComplete(); void handlePaste(); - static int instances; - static float mAlpha; - static ImageRect skin; bool mNumeric = false; - int mMinimum; - int mMaximum; + int mMinimum = 0; + int mMaximum = 0; bool mLoseFocusOnTab; + int mPadding = 1; AutoCompleteLister *mAutoComplete = nullptr; TextHistory *mHistory = nullptr; /**< Text history. */ }; - -#endif diff --git a/src/gui/widgets/textpreview.cpp b/src/gui/widgets/textpreview.cpp index f9e85052..2f80bd23 100644 --- a/src/gui/widgets/textpreview.cpp +++ b/src/gui/widgets/textpreview.cpp @@ -29,49 +29,19 @@ #include <typeinfo> -float TextPreview::mAlpha = 1.0; - -TextPreview::TextPreview(const std::string &text): - mText(text) +TextPreview::TextPreview(const std::string &text) + : mText(text) { mFont = gui->getFont(); mTextColor = &Theme::getThemeColor(Theme::TEXT); - mBGColor = &Theme::getThemeColor(Theme::BACKGROUND); } void TextPreview::draw(gcn::Graphics* graphics) { - if (config.guiAlpha != mAlpha) - mAlpha = config.guiAlpha; - - int alpha = (int) (mAlpha * 255.0f); - - if (!mTextAlpha) - alpha = 255; - - if (mOpaque) - { - graphics->setColor(gcn::Color((int) mBGColor->r, - (int) mBGColor->g, - (int) mBGColor->b, - (int)(mAlpha * 255.0f))); - graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); - } - - if (mTextBGColor && typeid(*mFont) == typeid(TrueTypeFont)) - { - auto *font = static_cast<TrueTypeFont*>(mFont); - int x = font->getWidth(mText) + 1 + 2 * ((mOutline || mShadow) ? 1 :0); - int y = font->getHeight() + 1 + 2 * ((mOutline || mShadow) ? 1 : 0); - graphics->setColor(gcn::Color((int) mTextBGColor->r, - (int) mTextBGColor->g, - (int) mTextBGColor->b, - (int)(mAlpha * 255.0f))); - graphics->fillRectangle(gcn::Rectangle(1, 1, x, y)); - } - TextRenderer::renderText(graphics, mText, 2, 2, gcn::Graphics::LEFT, - gcn::Color(mTextColor->r, mTextColor->g, - mTextColor->b, alpha), + gcn::Color(mTextColor->r, + mTextColor->g, + mTextColor->b, + 255), mFont, mOutline, mShadow); } diff --git a/src/gui/widgets/textpreview.h b/src/gui/widgets/textpreview.h index 7e88248f..8246a200 100644 --- a/src/gui/widgets/textpreview.h +++ b/src/gui/widgets/textpreview.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TEXTPREVIEW_H -#define TEXTPREVIEW_H +#pragma once #include <guichan/color.hpp> #include <guichan/font.hpp> @@ -45,37 +44,6 @@ class TextPreview : public gcn::Widget } /** - * Sets the text to use the set alpha value. - * - * @param alpha whether to use alpha values for the text or not - */ - void useTextAlpha(bool alpha) - { - mTextAlpha = alpha; - } - - /** - * Sets the color the text background is drawn in. This is only the - * rectangle directly behind the text, not to full widget. - * - * @param color the color to set - */ - void setTextBGColor(const gcn::Color *color) - { - mTextBGColor = color; - } - - /** - * Sets the background color of the widget. - * - * @param color the color to set - */ - void setBGColor(const gcn::Color *color) - { - mBGColor = color; - } - - /** * Sets the font to render the text in. * * @param font the font to use. @@ -112,31 +80,10 @@ class TextPreview : public gcn::Widget */ void draw(gcn::Graphics *graphics) override; - /** - * Set opacity for this widget (whether or not to show the background - * color) - * - * @param opaque Whether the widget should be opaque or not - */ - void setOpaque(bool opaque) { mOpaque = opaque; } - - /** - * Gets opacity for this widget (whether or not the background color - * is shown below the widget) - */ - bool isOpaque() const { return mOpaque; } - private: gcn::Font *mFont; std::string mText; const gcn::Color *mTextColor; - const gcn::Color *mBGColor; - const gcn::Color *mTextBGColor = nullptr; - static float mAlpha; - bool mTextAlpha = false; - bool mOpaque = false; bool mShadow = false; bool mOutline = false; }; - -#endif diff --git a/src/gui/widgets/vertcontainer.h b/src/gui/widgets/vertcontainer.h index b66957d3..a684453f 100644 --- a/src/gui/widgets/vertcontainer.h +++ b/src/gui/widgets/vertcontainer.h @@ -18,8 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef GUI_VERTCONTAINER_H -#define GUI_VERTCONTAINER_H +#pragma once #include "gui/widgets/container.h" @@ -42,5 +41,3 @@ class VertContainer : public Container, public gcn::WidgetListener int mSpacing; int mCount = 0; }; - -#endif diff --git a/src/gui/widgets/whispertab.cpp b/src/gui/widgets/whispertab.cpp index 636f48dd..28181971 100644 --- a/src/gui/widgets/whispertab.cpp +++ b/src/gui/widgets/whispertab.cpp @@ -36,7 +36,7 @@ WhisperTab::WhisperTab(const std::string &nick) : ChatTab(nick), mNick(nick) { - setTabColor(&Theme::getThemeColor(Theme::WHISPER)); + setTabColor(&Theme::getThemeColor(Theme::WHISPER_TAB)); } WhisperTab::~WhisperTab() diff --git a/src/gui/widgets/whispertab.h b/src/gui/widgets/whispertab.h index 0f01bacc..1a0a4a0f 100644 --- a/src/gui/widgets/whispertab.h +++ b/src/gui/widgets/whispertab.h @@ -19,8 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef WHISPERTAB_H -#define WHISPERTAB_H +#pragma once #include "chattab.h" @@ -63,5 +62,3 @@ class WhisperTab : public ChatTab private: std::string mNick; }; - -#endif // CHANNELTAB_H diff --git a/src/gui/widgets/window.cpp b/src/gui/widgets/window.cpp index 8bf9d081..e57918b7 100644 --- a/src/gui/widgets/window.cpp +++ b/src/gui/widgets/window.cpp @@ -23,6 +23,7 @@ #include "configuration.h" #include "log.h" +#include "textrenderer.h" #include "gui/gui.h" #include "gui/viewport.h" @@ -31,9 +32,6 @@ #include "gui/widgets/resizegrip.h" #include "gui/widgets/windowcontainer.h" -#include "resources/image.h" -#include "resources/theme.h" - #include <guichan/exception.hpp> #include <guichan/focushandler.hpp> @@ -43,13 +41,17 @@ int Window::instances = 0; int Window::mouseResize = 0; -Window::Window(const std::string &caption, bool modal, Window *parent, - const std::string &skin): - gcn::Window(caption), - mParent(parent), - mModal(modal), - mMaxWinWidth(graphics->getWidth()), - mMaxWinHeight(graphics->getHeight()) +Window::Window(const std::string &caption, bool modal, Window *parent) + : Window(SkinType::Window, caption, modal, parent) +{} + +Window::Window(SkinType skinType, const std::string &caption, bool modal, Window *parent) + : gcn::Window(caption) + , mParent(parent) + , mModal(modal) + , mSkinType(skinType) + , mMaxWinWidth(graphics->getWidth()) + , mMaxWinHeight(graphics->getHeight()) { logger->log("Window::Window(\"%s\")", caption.c_str()); @@ -58,19 +60,17 @@ Window::Window(const std::string &caption, bool modal, Window *parent, instances++; - setFrameSize(0); - setPadding(3); - setTitleBarHeight(20); - - // Loads the skin - mSkin = Theme::instance()->load(skin); + auto &skin = getSkin(); + setFrameSize(skin.frameSize); + setPadding(skin.padding); + setTitleBarHeight(skin.titleBarHeight); // Add this window to the window container windowContainer->add(this); if (mModal) { - gui->setCursorType(Cursor::POINTER); + gui->setCursorType(Cursor::Pointer); requestModalFocus(); } @@ -94,8 +94,6 @@ Window::~Window() removeWidgetListener(this); instances--; - - mSkin->instances--; } void Window::setWindowContainer(WindowContainer *wc) @@ -105,44 +103,57 @@ void Window::setWindowContainer(WindowContainer *wc) void Window::draw(gcn::Graphics *graphics) { - auto *g = static_cast<Graphics*>(graphics); + if (getFrameSize() == 0) + drawFrame(graphics); - g->drawImageRect(0, 0, getWidth(), getHeight(), mSkin->getBorder()); - - // Draw title - if (mShowTitle) - { - g->setColor(Theme::getThemeColor(Theme::TEXT)); - g->setFont(getFont()); - g->drawText(getCaption(), 7, 5, gcn::Graphics::LEFT); - } + auto g = static_cast<Graphics*>(graphics); - // Draw Close Button if (mCloseButton) { - g->drawImage(mSkin->getCloseImage(), - getWidth() - mSkin->getCloseImage()->getWidth() - getPadding(), - getPadding()); + WidgetState state(getCloseButtonRect(), mCloseButtonHovered ? STATE_HOVERED : 0); + gui->getTheme()->drawSkin(g, SkinType::ButtonClose, state); } - // Draw Sticky Button if (mStickyButton) { - Image *button = mSkin->getStickyImage(mSticky); - int x = getWidth() - button->getWidth() - getPadding(); - if (mCloseButton) - x -= mSkin->getCloseImage()->getWidth(); - - g->drawImage(button, x, getPadding()); + WidgetState state(getStickyButtonRect(), mSticky ? STATE_SELECTED : 0); + gui->getTheme()->drawSkin(g, SkinType::ButtonSticky, state); } drawChildren(graphics); } +void Window::drawFrame(gcn::Graphics *graphics) +{ + auto g = static_cast<Graphics*>(graphics); + + WidgetState widgetState(this); + widgetState.width += getFrameSize() * 2; + widgetState.height += getFrameSize() * 2; + + auto &skin = getSkin(); + skin.draw(g, widgetState); + + if (mShowTitle) + { + if (auto skinState = skin.getState(widgetState.flags)) + { + auto &textFormat = skinState->textFormat; + TextRenderer::renderText(g, + getCaption(), + getFrameSize() + skin.titleOffsetX, + getFrameSize() + skin.titleOffsetY, + gcn::Graphics::LEFT, + textFormat.bold ? boldFont : getFont(), + textFormat); + } + } +} + void Window::setContentSize(int width, int height) { - width = width + 2 * getPadding(); - height = height + getPadding() + getTitleBarHeight(); + width += 2 * getPadding(); + height += getPadding() + getTitleBarHeight(); if (getMinWidth() > width) width = getMinWidth(); @@ -156,6 +167,16 @@ void Window::setContentSize(int width, int height) setSize(width, height); } +void Window::setMinimumContentSize(int width, int height) +{ + const int padding = getPadding(); + const int titleBarHeight = getTitleBarHeight(); + auto &skin = getSkin(); + + setMinWidth(std::max(skin.getMinWidth(), width + 2 * padding)); + setMinHeight(std::max(skin.getMinHeight(), height + padding + titleBarHeight)); +} + void Window::setLocationRelativeTo(gcn::Widget *widget) { int wx; @@ -218,13 +239,12 @@ void Window::setLocationRelativeTo(ImageRect::ImagePosition position, void Window::setMinWidth(int width) { - mMinWinWidth = width > mSkin->getMinWidth() ? width : mSkin->getMinWidth(); + mMinWinWidth = std::max(getSkin().getMinWidth(), width); } void Window::setMinHeight(int height) { - mMinWinHeight = height > mSkin->getMinHeight() ? - height : mSkin->getMinHeight(); + mMinWinHeight = std::max(getSkin().getMinHeight(), height); } void Window::setMaxWidth(int width) @@ -276,9 +296,7 @@ void Window::widgetResized(const gcn::Event &event) void Window::widgetHidden(const gcn::Event &event) { if (gui) - { - gui->setCursorType(Cursor::POINTER); - } + gui->setCursorType(Cursor::Pointer); WidgetListIterator it; @@ -341,38 +359,13 @@ void Window::mousePressed(gcn::MouseEvent &event) const int x = event.getX(); const int y = event.getY(); - // Handle close button - if (mCloseButton) - { - gcn::Rectangle closeButtonRect( - getWidth() - mSkin->getCloseImage()->getWidth() - getPadding(), - getPadding(), - mSkin->getCloseImage()->getWidth(), - mSkin->getCloseImage()->getHeight()); - - if (closeButtonRect.isPointInRect(x, y)) - { - close(); - } - } + if (mCloseButton && getCloseButtonRect().isPointInRect(x, y)) + close(); - // Handle sticky button - if (mStickyButton) - { - Image *button = mSkin->getStickyImage(mSticky); - int rx = getWidth() - button->getWidth() - getPadding(); - if (mCloseButton) - rx -= mSkin->getCloseImage()->getWidth(); - gcn::Rectangle stickyButtonRect(rx, getPadding(), - button->getWidth(), button->getHeight()); - - if (stickyButtonRect.isPointInRect(x, y)) - { - setSticky(!isSticky()); - } - } + if (mStickyButton && getStickyButtonRect().isPointInRect(x, y)) + setSticky(!isSticky()); - // Handle window resizing + // Update resizing state and disable moving if we're resizing the window mouseResize = getResizeHandles(event); if (mouseResize) mMoved = false; @@ -384,6 +377,11 @@ void Window::close() setVisible(false); } +const Skin &Window::getSkin() const +{ + return gui->getTheme()->getSkin(mSkinType); +} + void Window::mouseReleased(gcn::MouseEvent &event) { mouseResize = 0; @@ -394,11 +392,15 @@ void Window::mouseReleased(gcn::MouseEvent &event) void Window::mouseExited(gcn::MouseEvent &event) { if (mGrip && !mouseResize) - gui->setCursorType(Cursor::POINTER); + gui->setCursorType(Cursor::Pointer); + + mCloseButtonHovered = false; } void Window::mouseMoved(gcn::MouseEvent &event) { + mCloseButtonHovered = false; + // Make sure BeingPopup is hidden (Viewport does not receive mouseExited) if (viewport) viewport->hideBeingPopup(); @@ -408,30 +410,36 @@ void Window::mouseMoved(gcn::MouseEvent &event) if (event.isConsumed()) return; - int resizeHandles = getResizeHandles(event); + mCloseButtonHovered = getCloseButtonRect().isPointInRect(event.getX(), event.getY()); + Cursor cursor = Cursor::Pointer; // Changes the custom mouse cursor based on its current position. - switch (resizeHandles) + if (!mCloseButtonHovered) { + switch (getResizeHandles(event)) + { case BOTTOM | RIGHT: case TOP | LEFT: - gui->setCursorType(Cursor::RESIZE_DOWN_RIGHT); + cursor = Cursor::ResizeDownRight; break; case BOTTOM | LEFT: case TOP | RIGHT: - gui->setCursorType(Cursor::RESIZE_DOWN_LEFT); + cursor = Cursor::ResizeDownLeft; break; case BOTTOM: case TOP: - gui->setCursorType(Cursor::RESIZE_DOWN); + cursor = Cursor::ResizeDown; break; case RIGHT: case LEFT: - gui->setCursorType(Cursor::RESIZE_ACROSS); + cursor = Cursor::ResizeAcross; break; default: - gui->setCursorType(Cursor::POINTER); + break; + } } + + gui->setCursorType(cursor); } void Window::mouseDragged(gcn::MouseEvent &event) @@ -675,6 +683,13 @@ int Window::getResizeHandles(gcn::MouseEvent &event) if (inPadding && event.getSource() == this) { + /** + * The width of the resize border. Is independent of the actual window + * border width, and determines mostly the size of the corner area + * where two borders are moved at the same time. + */ + const int resizeBorderWidth = std::max(mGrip->getWidth(), 10); + resizeHandles |= (x >= getWidth() - resizeBorderWidth) ? RIGHT : (x < resizeBorderWidth) ? LEFT : 0; resizeHandles |= (y >= getHeight() - resizeBorderWidth) ? BOTTOM : @@ -692,10 +707,55 @@ int Window::getResizeHandles(gcn::MouseEvent &event) return resizeHandles; } +gcn::Rectangle Window::getCloseButtonRect() const +{ + if (!mCloseButton) + return {}; + + auto theme = gui->getTheme(); + + auto &closeSkin = theme->getSkin(SkinType::ButtonClose); + const int closeWidth = closeSkin.getMinWidth(); + const int closeHeight = closeSkin.getMinHeight(); + + return { + getWidth() - closeWidth - closeSkin.padding, + closeSkin.padding, + closeWidth, + closeHeight + }; +} + +gcn::Rectangle Window::getStickyButtonRect() const +{ + if (!mStickyButton) + return {}; + + auto theme = gui->getTheme(); + + auto &closeSkin = theme->getSkin(SkinType::ButtonClose); + const int closeWidth = closeSkin.getMinWidth(); + + auto &stickySkin = theme->getSkin(SkinType::ButtonSticky); + const int stickyWidth = stickySkin.getMinWidth(); + const int stickyHeight = stickySkin.getMinHeight(); + + int stickyX = getWidth() - stickyWidth - stickySkin.padding - stickySkin.spacing; + if (mCloseButton) + stickyX -= closeWidth + closeSkin.padding; + + return { + stickyX, + stickySkin.padding, + stickyWidth, + stickyHeight + }; +} + int Window::getGuiAlpha() { float alpha = std::max(config.guiAlpha, - Theme::instance()->getMinimumOpacity()); + gui->getTheme()->getMinimumOpacity()); return (int) (alpha * 255.0f); } diff --git a/src/gui/widgets/window.h b/src/gui/widgets/window.h index bf6f363c..6331a715 100644 --- a/src/gui/widgets/window.h +++ b/src/gui/widgets/window.h @@ -19,14 +19,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef WINDOW_H -#define WINDOW_H +#pragma once #include "graphics.h" #include "guichanfwd.h" -#include <guichan/widgetlistener.hpp> +#include "resources/theme.h" +#include <guichan/widgetlistener.hpp> #include <guichan/widgets/window.hpp> class ContainerPlacer; @@ -54,10 +54,18 @@ class Window : public gcn::Window, gcn::WidgetListener * @param parent The parent window. This is the window standing above * this one in the window hiearchy. When reordering, * a window will never go below its parent window. - * @param skin The location where the window's skin XML can be found. */ - Window(const std::string &caption = "Window", bool modal = false, - Window *parent = nullptr, const std::string &skin = "window.xml"); + Window(const std::string &caption = "Window", + bool modal = false, + Window *parent = nullptr); + + /** + * Constructor that allows customizing the SkinType used by the window. + */ + Window(SkinType skinType, + const std::string &caption = "Window", + bool modal = false, + Window *parent = nullptr); /** * Destructor. Deletes all the added widgets. @@ -70,16 +78,26 @@ class Window : public gcn::Window, gcn::WidgetListener static void setWindowContainer(WindowContainer *windowContainer); /** - * Draws the window. + * Draws the window contents. */ void draw(gcn::Graphics *graphics) override; /** + * Draws the window frame. + */ + void drawFrame(gcn::Graphics *graphics) override; + + /** * Sets the size of this window. */ void setContentSize(int width, int height); /** + * Sets the minimum size of the window content. + */ + void setMinimumContentSize(int width, int height); + + /** * Sets the location relative to the given widget. */ void setLocationRelativeTo(gcn::Widget *widget); @@ -345,6 +363,11 @@ class Window : public gcn::Window, gcn::WidgetListener virtual void close(); /** + * Returns the Skin used by this window. + */ + const Skin &getSkin() const; + + /** * Gets the alpha value used by the window, in a Guichan usable format. */ static int getGuiAlpha(); @@ -377,6 +400,9 @@ class Window : public gcn::Window, gcn::WidgetListener */ int getResizeHandles(gcn::MouseEvent &event); + gcn::Rectangle getCloseButtonRect() const; + gcn::Rectangle getStickyButtonRect() const; + ResizeGrip *mGrip = nullptr; /**< Resize grip */ Window *mParent; /**< The parent window */ Layout *mLayout = nullptr; /**< Layout handler */ @@ -384,10 +410,12 @@ class Window : public gcn::Window, gcn::WidgetListener bool mShowTitle = true; /**< Window has a title bar */ bool mModal; /**< Window is modal */ bool mCloseButton = false; /**< Window has a close button */ + bool mCloseButtonHovered = false; bool mDefaultVisible = false; /**< Window's default visibility */ bool mSaveVisible = false; /**< Window will save visibility */ bool mStickyButton = false; /**< Window has a sticky button */ bool mSticky = false; /**< Window resists hiding*/ + SkinType mSkinType; /**< The skin type used when drawing the window. */ int mMinWinWidth = 100; /**< Minimum window width */ int mMinWinHeight = 40; /**< Minimum window height */ int mMaxWinWidth; /**< Maximum window width */ @@ -398,15 +426,4 @@ class Window : public gcn::Window, gcn::WidgetListener int mDefaultHeight; /**< Default window height */ static int instances; /**< Number of Window instances */ - - Skin *mSkin; /**< Skin in use by this window */ - - /** - * The width of the resize border. Is independent of the actual window - * border width, and determines mostly the size of the corner area - * where two borders are moved at the same time. - */ - static const int resizeBorderWidth = 10; }; - -#endif diff --git a/src/gui/widgets/windowcontainer.cpp b/src/gui/widgets/windowcontainer.cpp index 36f0998f..4e350a9e 100644 --- a/src/gui/widgets/windowcontainer.cpp +++ b/src/gui/widgets/windowcontainer.cpp @@ -24,16 +24,15 @@ #include "gui/gui.h" #include "gui/widgets/window.h" -#include "utils/dtor.h" - #include <guichan/focushandler.hpp> WindowContainer *windowContainer = nullptr; void WindowContainer::logic() { - delete_all(mDeathList); - mDeathList.clear(); + for (auto widget : mScheduledDeletions) + delete widget; + mScheduledDeletions.clear(); gcn::Container::logic(); } @@ -48,7 +47,7 @@ void WindowContainer::draw(gcn::Graphics *graphics) void WindowContainer::scheduleDelete(gcn::Widget *widget) { - mDeathList.push_back(widget); + mScheduledDeletions.insert(widget); } void WindowContainer::adjustAfterResize(int oldScreenWidth, diff --git a/src/gui/widgets/windowcontainer.h b/src/gui/widgets/windowcontainer.h index 861839c9..ff03a903 100644 --- a/src/gui/widgets/windowcontainer.h +++ b/src/gui/widgets/windowcontainer.h @@ -19,11 +19,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef WINDOWCONTAINER_H -#define WINDOWCONTAINER_H +#pragma once #include "gui/widgets/container.h" +#include <set> + /** * A window container. This container adds functionality for more convenient * widget (windows in particular) destruction. @@ -65,11 +66,9 @@ class WindowContainer : public Container bool widgetIsVisible(gcn::Widget *widget); /** - * List of widgets that are scheduled to be deleted. + * Set of widgets that are scheduled to be deleted. */ - std::list<gcn::Widget *> mDeathList; + std::set<gcn::Widget *> mScheduledDeletions; }; extern WindowContainer *windowContainer; - -#endif |