X-Git-Url: http://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fgraphics%2FMenu.h;h=886e2fd07af5a7bacb4d0c04566bc9824719231f;hb=cc3d698b8c1ad09d7a3f9e3f28bc84e0ac1735ea;hp=083e741864b552b161173f5b117ed09c9d4f8f34;hpb=4470cf4b44128f3509bfab43483844d19f563a77;p=l2e.git diff --git a/src/graphics/Menu.h b/src/graphics/Menu.h index 083e741..886e2fd 100644 --- a/src/graphics/Menu.h +++ b/src/graphics/Menu.h @@ -1,61 +1,110 @@ -/* - * Menu.h - * - * Created on: Aug 8, 2012 - * Author: holy - */ - #ifndef GRAPHICS_MENU_H_ #define GRAPHICS_MENU_H_ #include "Font.h" -#include "../geometry/operators.h" -#include "../geometry/Point.h" +#include "fwd.h" +#include "Sprite.h" #include "../geometry/Vector.h" +#include #include #include namespace graphics { -class Sprite; +struct MenuProperties { + static const int TYPE_ID = 407; + + const Font *font; + const Font *disabledFont; + const Sprite *cursor; + const Sprite *selectedCursor; + int charsPerEntry; + int rows; + int rowGap; + int iconSpace; + int cols; + int colGap; + int charsPerNumber; + int charsPerAdditionalText; + int additionalTextGap; + char delimiter; + bool wrapX; + bool wrapY; + + MenuProperties() + : font(0), disabledFont(0), cursor(0), selectedCursor(0) + , charsPerEntry(0), rows(1), rowGap(0) + , iconSpace(0), cols(1), colGap(0) + , charsPerNumber(0), charsPerAdditionalText(0) + , additionalTextGap(0), delimiter(':') + , wrapX(false), wrapY(false) { } + + static void CreateTypeDescription(); + static void Construct(void *); + +}; -// TODO: alternate font for disabled entries -// TODO: sprite for the cursor -// TODO: animation when top row changes template -class Menu { +class Menu +: private MenuProperties { public: - explicit Menu(const Font *font = NULL, int charsPerEntry = 16, int rows = 1, int rowGap = 0, int cols = 1, int colGap = 0); + Menu(); + Menu(const MenuProperties &); public: + void SetInactive() { state = STATE_INACTIVE; } + void SetActive() { state = STATE_ACTIVE; } + void SetSelected() { state = STATE_SELECTED; } + void SetDualSelection() { state = STATE_DUAL; secondarySelection = selected; } + bool IsActive() const { return state == STATE_ACTIVE; } + bool HasSelected() const { return state == STATE_SELECTED; } + bool InDualMode() const { return state == STATE_DUAL; } + int Width() const; int Height() const; - int ColWidth() const { return font->CharWidth() * charsPerEntry; } + int ColWidth() const; int RowHeight() const { return font->CharHeight() + rowGap; } + int CharsPerEntry() const { return charsPerEntry; } T &Selected() { return entries[selected].value; } const T &Selected() const { return entries[selected].value; } const char *SelectedTitle() const { return entries[selected].title; } + int SelectedNumber() const { return entries[selected].number; } bool SelectedIsEnabled() const { return entries[selected].enabled; } + T &SecondarySelection() { return entries[secondarySelection].value; } + const T &SecondarySelection() const { return entries[secondarySelection].value; } + const char *SecondaryTitle() const { return entries[secondarySelection].title; } + int SecondaryNumber() const { return entries[secondarySelection].number; } + bool SecondaryIsEnabled() const { return entries[secondarySelection].enabled; } + + void SwapSelected() { SwapEntriesAt(selected, secondarySelection); } + void SwapEntriesAt(int lhs, int rhs) { std::swap(entries[lhs], entries[rhs]); } + void NextItem(); void PreviousItem(); void NextRow(); void PreviousRow(); void SelectIndex(int index); + int SelectedIndex() const { return selected; } + int SecondaryIndex() const { return secondarySelection; } + bool IsSelected(int index) const { return index == selected; } int EntryCount() const { return entries.size(); } T &ValueAt(int index) { return entries[index].value; } const T &ValueAt(int index) const { return entries[index].value; } - void Add(const char *title, const T &value, bool enabled = true, const Sprite *icon = 0) { entries.push_back(Entry(title, value, enabled, icon)); } + void Add(const char *title, const T &value, bool enabled = true, const Sprite *icon = 0, int number = 0, const char *additionalText = 0) { entries.push_back(Entry(title, value, enabled, icon, number, additionalText)); } + void AddEmptyEntry() { entries.push_back(Entry(0, T(), false)); } void Disable(int index) { entries[index].enabled = false; } void Enable(int index) { entries[index].enabled = true; } void Reserve(int n) { entries.reserve(n); } + void Clear() { entries.clear(); } + void ClearEntry(int at) { entries[at] = Entry(0, T(), false); } - void Draw(SDL_Surface *dest, geometry::Point position) const; + void Draw(SDL_Surface *dest, const geometry::Vector &position) const; private: int GetRow(int index) const { return index / cols; } @@ -63,39 +112,63 @@ private: private: struct Entry { - Entry(const char *title, const T &value, bool enabled = true, const Sprite *icon = 0) - : title(title), icon(icon), value(value), enabled(enabled) { } + Entry(const char *title, const T &value, bool enabled = true, const Sprite *icon = 0, int number = 0, const char *additionalText = 0) + : title(title), additionalText(additionalText), icon(icon), number(number), value(value), enabled(enabled) { } const char *title; + const char *additionalText; const Sprite *icon; + int number; T value; bool enabled; }; - const Font *font; std::vector entries; - int charsPerEntry; - int rows; - int rowGap; - int cols; - int colGap; int selected; + int secondarySelection; int topRow; + enum State { + STATE_INACTIVE, + STATE_ACTIVE, + STATE_SELECTED, + STATE_DUAL, + }; + State state; }; template -Menu::Menu(const Font *font, int charsPerEntry, int rows, int rowGap, int cols, int colGap) -: font(font) -, charsPerEntry(charsPerEntry) -, rows(rows) -, rowGap(rowGap) -, cols(cols) -, colGap(colGap) +Menu::Menu() +: MenuProperties() , selected(0) -, topRow(0) { +, secondarySelection(0) +, topRow(0) +, state(STATE_ACTIVE) { } +template +Menu::Menu(const MenuProperties &p) +: MenuProperties(p) +, selected(0) +, secondarySelection(0) +, topRow(0) +, state(STATE_ACTIVE) { + +} + + +template +int Menu::ColWidth() const { + int width(iconSpace); + width += font->CharWidth() * (charsPerEntry + charsPerNumber); + if (charsPerNumber) { + width += font->CharWidth(); + } + if (charsPerAdditionalText) { + width += additionalTextGap + charsPerAdditionalText * font->CharWidth(); + } + return width; +} template int Menu::Width() const { @@ -110,30 +183,46 @@ int Menu::Height() const { template void Menu::NextItem() { - SelectIndex(selected + 1); + int index(selected + 1); + if (wrapX && index % cols == 0) { + index -= cols; + } + SelectIndex(index); } template void Menu::PreviousItem() { - SelectIndex(selected - 1); + int index(selected - 1); + if (wrapX && selected % cols == 0) { + index += cols; + } + SelectIndex(index); } template void Menu::NextRow() { - SelectIndex(selected + cols); + int index(selected + cols); + if (wrapY && index >= int(entries.size())) { + index -= entries.size(); + } + SelectIndex(index); } template void Menu::PreviousRow() { - SelectIndex(selected - cols); + int index(selected - cols); + if (wrapY && index < 0) { + index += entries.size(); + } + SelectIndex(index); } template void Menu::SelectIndex(int index) { - if (index < 0 || entries.size() < index) return; + if (index < 0 || int(entries.size()) <= index) return; selected = index; - if (GetRow(selected) - rows > topRow) { - topRow = GetRow(selected) - rows; + if (topRow <= GetRow(selected) - rows) { + topRow = GetRow(selected) - rows + 1; } else if (GetRow(selected) < topRow) { topRow = GetRow(selected); } @@ -141,14 +230,73 @@ void Menu::SelectIndex(int index) { template -void Menu::Draw(SDL_Surface *dest, geometry::Point position) const { +void Menu::Draw(SDL_Surface *dest, const geometry::Vector &position) const { int start(topRow * cols); - int slots((topRow + rows) * cols); + int slots(rows * cols); int items(entries.size() - start); - int end(items < slots ? items : slots); + int end(start + (items < slots ? items : slots)); for (int i(0), count(end - start); i < count; ++i) { - geometry::Vector offset((i % cols) * (ColWidth() + colGap), (i / cols) * RowHeight()); - font->DrawString(entries[start + i].title, dest, position + offset, charsPerEntry); + if (!entries[start + i].title) continue; + geometry::Vector iconOffset( + (i % cols) * (ColWidth() + colGap), + (i / cols) * RowHeight()); + + // Third column hack! + // This fixes the position of the "DROP" item in the inventory menu. + if (i % cols == 2) { + iconOffset += geometry::Vector(font->CharWidth(), 0); + } + + if (entries[start + i].icon) { + entries[start + i].icon->Draw(dest, position + iconOffset); + } + geometry::Vector textOffset(iconOffset.X() + iconSpace, iconOffset.Y()); + const Font *usedFont(entries[start + i].enabled ? font : disabledFont); + usedFont->DrawString(entries[start + i].title, dest, position + textOffset, charsPerEntry); + + textOffset += geometry::Vector(charsPerEntry * usedFont->CharWidth(), 0); + + if (charsPerAdditionalText) { + textOffset += geometry::Vector(additionalTextGap, 0); + if (entries[start + i].additionalText) { + usedFont->DrawString(entries[start + i].additionalText, dest, position + textOffset, charsPerAdditionalText); + } + textOffset += geometry::Vector(charsPerAdditionalText * usedFont->CharWidth(), 0); + } + + if (charsPerNumber) { + usedFont->DrawChar(delimiter, dest, position + textOffset); + textOffset += geometry::Vector(usedFont->CharWidth(), 0); + usedFont->DrawNumber(entries[start + i].number, dest, position + textOffset, charsPerNumber); + } + } + geometry::Vector cursorOffset( + (selected % cols) * (ColWidth() + colGap) - cursor->Width(), + ((selected - start) / cols) * RowHeight()); + // Third column hack! + // This fixes the position of the "DROP" item in the inventory menu. + if (selected % cols == 2) { + cursorOffset += geometry::Vector(font->CharWidth(), 0); + } + switch (state) { + case STATE_INACTIVE: + break; + case STATE_ACTIVE: + cursor->Draw(dest, position + cursorOffset); + break; + case STATE_SELECTED: + selectedCursor->Draw(dest, position + cursorOffset); + break; + case STATE_DUAL: + cursor->Draw(dest, position + cursorOffset + - geometry::Vector(selectedCursor->Width(), 0)); + if (secondarySelection >= start && secondarySelection <= end) { + geometry::Vector secondaryOffset( + (secondarySelection % cols) * (ColWidth() + colGap) - cursor->Width(), + ((secondarySelection - start) / cols) * RowHeight()); + selectedCursor->Draw(dest, position + secondaryOffset); + } + break; } }