]> git.localhorst.tv Git - l2e.git/blobdiff - src/graphics/Menu.h
removed useless comments
[l2e.git] / src / graphics / Menu.h
index 083e741864b552b161173f5b117ed09c9d4f8f34..e288f2d6b946844dc1766652d67728ea6d9b0aea 100644 (file)
-/*
- * 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 "../geometry/Vector.h"
+#include "Sprite.h"
+#include "../math/Vector.h"
 
+#include <algorithm>
 #include <vector>
 #include <SDL.h>
 
 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;
+       int thirdColumnHack;
+       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), thirdColumnHack(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 T>
-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<int> position) const;
+       void Draw(SDL_Surface *dest, const math::Vector<int> &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<Entry> 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<class T>
-Menu<T>::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<T>::Menu()
+: MenuProperties()
 , selected(0)
-, topRow(0) {
+, secondarySelection(0)
+, topRow(0)
+, state(STATE_ACTIVE) {
 
 }
 
+template<class T>
+Menu<T>::Menu(const MenuProperties &p)
+: MenuProperties(p)
+, selected(0)
+, secondarySelection(0)
+, topRow(0)
+, state(STATE_ACTIVE) {
+
+}
+
+
+template<class T>
+int Menu<T>::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<class T>
 int Menu<T>::Width() const {
@@ -110,30 +183,46 @@ int Menu<T>::Height() const {
 
 template<class T>
 void Menu<T>::NextItem() {
-       SelectIndex(selected + 1);
+       int index(selected + 1);
+       if (wrapX && index % cols == 0) {
+               index -= cols;
+       }
+       SelectIndex(index);
 }
 
 template<class T>
 void Menu<T>::PreviousItem() {
-       SelectIndex(selected - 1);
+       int index(selected - 1);
+       if (wrapX && selected % cols == 0) {
+               index += cols;
+       }
+       SelectIndex(index);
 }
 
 template<class T>
 void Menu<T>::NextRow() {
-       SelectIndex(selected + cols);
+       int index(selected + cols);
+       if (wrapY && index >= int(entries.size())) {
+               index -= entries.size();
+       }
+       SelectIndex(index);
 }
 
 template<class T>
 void Menu<T>::PreviousRow() {
-       SelectIndex(selected - cols);
+       int index(selected - cols);
+       if (wrapY && index < 0) {
+               index += entries.size();
+       }
+       SelectIndex(index);
 }
 
 template<class T>
 void Menu<T>::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,17 +230,74 @@ void Menu<T>::SelectIndex(int index) {
 
 
 template<class T>
-void Menu<T>::Draw(SDL_Surface *dest, geometry::Point<int> position) const {
+void Menu<T>::Draw(SDL_Surface *dest, const math::Vector<int> &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<int> offset((i % cols) * (ColWidth() + colGap), (i / cols) * RowHeight());
-               font->DrawString(entries[start + i].title, dest, position + offset, charsPerEntry);
+               if (!entries[start + i].title) continue;
+               math::Vector<int> iconOffset(
+                               (i % cols) * (ColWidth() + colGap),
+                               (i / cols) * RowHeight());
+
+               // This fixes the position of the third column of the inventory and capsule menus.
+               if (thirdColumnHack && i % cols == 2) {
+                       iconOffset += math::Vector<int>(font->CharWidth() * thirdColumnHack, 0);
+               }
+
+               if (entries[start + i].icon) {
+                       entries[start + i].icon->Draw(dest, position + iconOffset);
+               }
+               math::Vector<int> 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 += math::Vector<int>(charsPerEntry * usedFont->CharWidth(), 0);
+
+               if (charsPerAdditionalText) {
+                       textOffset += math::Vector<int>(additionalTextGap, 0);
+                       if (entries[start + i].additionalText) {
+                               usedFont->DrawString(entries[start + i].additionalText, dest, position + textOffset, charsPerAdditionalText);
+                       }
+                       textOffset += math::Vector<int>(charsPerAdditionalText * usedFont->CharWidth(), 0);
+               }
+
+               if (charsPerNumber) {
+                       usedFont->DrawChar(delimiter, dest, position + textOffset);
+                       textOffset += math::Vector<int>(usedFont->CharWidth(), 0);
+                       usedFont->DrawNumber(entries[start + i].number, dest, position + textOffset, charsPerNumber);
+               }
+       }
+       math::Vector<int> cursorOffset(
+                       (selected % cols) * (ColWidth() + colGap) - cursor->Width(),
+                       ((selected - start) / cols) * RowHeight());
+       // This fixes the position of the third column of the inventory and capsule menus.
+       if (thirdColumnHack && selected % cols == 2) {
+               cursorOffset += math::Vector<int>(font->CharWidth() * thirdColumnHack, 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
+                                       - math::Vector<int>(selectedCursor->Width(), 0));
+                       if (secondarySelection >= start && secondarySelection <= end) {
+                               math::Vector<int> secondaryOffset(
+                                               (secondarySelection % cols) * (ColWidth() + colGap) - cursor->Width(),
+                                               ((secondarySelection - start) / cols) * RowHeight());
+                               selectedCursor->Draw(dest, position + secondaryOffset);
+                       }
+                       break;
        }
 }
 
 }
 
-#endif /* GRAPHICS_MENU_H_ */
+#endif