+/*
+ * 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 <vector>
+#include <SDL.h>
+
+namespace graphics {
+
+class Sprite;
+
+template<class T>
+class Menu {
+
+public:
+ Menu(const Font *font, int charsPerEntry, int rows, int rowGap = 0, int cols = 1, int colGap = 0);
+
+public:
+ int Width() const;
+ int Height() const;
+ int ColWidth() const { return font->CharWidth() * charsPerEntry; }
+ int RowHeight() const { return font->CharHeight() + rowGap; }
+
+ T &Selected() { return entries[selected].value; }
+ const T &Selected() const { return entries[selected].value; }
+ const char *SelectedTitle() const { return entries[selected].title; }
+
+ void NextItem();
+ void PreviousItem();
+ void NextRow();
+ void PreviousRow();
+ void SelectIndex(int index);
+
+ void Add(const char *title, const T &value, const Sprite *icon = 0) { entries.push_back(Entry(title, value, icon)); }
+ void Reserve(int n) { entries.reserve(n); }
+
+ void Draw(SDL_Surface *dest, geometry::Point<int> position) const;
+
+private:
+ int GetRow(int index) const { return index / cols; }
+ int GetCol(int index) const { return index % cols; }
+
+private:
+ struct Entry {
+ Entry(const char *title, const T &value, const Sprite *icon = 0)
+ : title(title), icon(icon), value(value) { }
+ const char *title;
+ const Sprite *icon;
+ T value;
+ };
+ const Font *font;
+ std::vector<Entry> entries;
+ int charsPerEntry;
+ int rows;
+ int rowGap;
+ int cols;
+ int colGap;
+ int selected;
+ int topRow;
+
+};
+
+
+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)
+, selected(0)
+, topRow(0) {
+
+}
+
+
+template<class T>
+int Menu<T>::Width() const {
+ return cols * ColWidth() + (cols - 1) * colGap;
+}
+
+template<class T>
+int Menu<T>::Height() const {
+ return rows * font->CharHeight() + (rows - 1) * rowGap;
+}
+
+
+template<class T>
+void Menu<T>::NextItem() {
+ SelectIndex(selected + 1);
+}
+
+template<class T>
+void Menu<T>::PreviousItem() {
+ SelectIndex(selected - 1);
+}
+
+template<class T>
+void Menu<T>::NextRow() {
+ SelectIndex(selected + cols);
+}
+
+template<class T>
+void Menu<T>::PreviousRow() {
+ SelectIndex(selected - cols);
+}
+
+template<class T>
+void Menu<T>::SelectIndex(int index) {
+ if (index < 0 || entries.size() < index) return;
+ selected = index;
+ if (GetRow(selected) - rows > topRow) {
+ topRow = GetRow(selected) - rows;
+ } else if (GetRow(selected) < topRow) {
+ topRow = GetRow(selected);
+ }
+}
+
+
+template<class T>
+void Menu<T>::Draw(SDL_Surface *dest, geometry::Point<int> position) const {
+ int start(topRow * cols);
+ int slots((topRow + rows) * cols);
+ int items(entries.size() - start);
+ int end(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);
+ }
+}
+
+}
+
+#endif /* GRAPHICS_MENU_H_ */