]> git.localhorst.tv Git - l2e.git/blob - src/graphics/Menu.h
b581fe25283ffcee693407cd523e3f72d7bee704
[l2e.git] / src / graphics / Menu.h
1 /*
2  * Menu.h
3  *
4  *  Created on: Aug 8, 2012
5  *      Author: holy
6  */
7
8 #ifndef GRAPHICS_MENU_H_
9 #define GRAPHICS_MENU_H_
10
11 #include "Font.h"
12 #include "fwd.h"
13 #include "Sprite.h"
14 #include "../geometry/Vector.h"
15
16 #include <vector>
17 #include <SDL.h>
18
19 namespace graphics {
20
21 struct MenuProperties {
22         const Font *font;
23         const Font *disabledFont;
24         const Sprite *cursor;
25         int charsPerEntry;
26         int rows;
27         int rowGap;
28         int iconSpace;
29         int cols;
30         int colGap;
31         int charsPerNumber;
32         int charsPerAdditionalText;
33         int additionalTextGap;
34         char delimiter;
35
36         MenuProperties()
37         : font(0), disabledFont(0), cursor(0)
38         , charsPerEntry(0), rows(0), rowGap(0)
39         , iconSpace(0), cols(0), colGap(0)
40         , charsPerNumber(0), charsPerAdditionalText(0)
41         , additionalTextGap(0), delimiter(':') { }
42
43         MenuProperties(const Font *font, const Font *disabledFont, const Sprite *cursor, int charsPerEntry, int rows, int rowGap, int iconSpace, int cols, int colGap, int charsPerNumber, char delimiter, int charsPerAdditionalText, int additionalTextGap)
44         : font(font), disabledFont(disabledFont), cursor(cursor), charsPerEntry(charsPerEntry), rows(rows), rowGap(rowGap), iconSpace(iconSpace), cols(cols), colGap(colGap), charsPerNumber(charsPerNumber), charsPerAdditionalText(charsPerAdditionalText), additionalTextGap(additionalTextGap), delimiter(delimiter) { }
45
46         static void CreateTypeDescription();
47         static void Construct(void *);
48
49 };
50
51 template<class T>
52 class Menu
53 : private MenuProperties {
54
55 public:
56         Menu();
57         Menu(const MenuProperties &);
58         Menu(const Font *font, const Font *disabledFont, const Sprite *cursor, int charsPerEntry, int rows, int rowGap = 0, int iconSpace = 0, int cols = 1, int colGap = 0, int charsPerNumber = 0, char delimiter = ':', int charsPerAdditionalText = 0, int additionalTextGap = 0);
59
60 public:
61         int Width() const;
62         int Height() const;
63         int ColWidth() const;
64         int RowHeight() const { return font->CharHeight() + rowGap; }
65         int CharsPerEntry() const { return charsPerEntry; }
66
67         T &Selected() { return entries[selected].value; }
68         const T &Selected() const { return entries[selected].value; }
69         const char *SelectedTitle() const { return entries[selected].title; }
70         int SelectedNumber() const { return entries[selected].number; }
71         bool SelectedIsEnabled() const { return entries[selected].enabled; }
72
73         void NextItem();
74         void PreviousItem();
75         void NextRow();
76         void PreviousRow();
77         void SelectIndex(int index);
78         int SelectedIndex() const { return selected; }
79         bool IsSelected(int index) const { return index == selected; }
80
81         int EntryCount() const { return entries.size(); }
82         T &ValueAt(int index) { return entries[index].value; }
83         const T &ValueAt(int index) const { return entries[index].value; }
84
85         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)); }
86         void AddEmptyEntry() { entries.push_back(Entry(0, T(), false)); }
87         void Disable(int index) { entries[index].enabled = false; }
88         void Enable(int index) { entries[index].enabled = true; }
89         void Reserve(int n) { entries.reserve(n); }
90         void Clear() { entries.clear(); }
91
92         void Draw(SDL_Surface *dest, const geometry::Vector<int> &position) const;
93
94 private:
95         int GetRow(int index) const { return index / cols; }
96         int GetCol(int index) const { return index % cols; }
97
98 private:
99         struct Entry {
100                 Entry(const char *title, const T &value, bool enabled = true, const Sprite *icon = 0, int number = 0, const char *additionalText = 0)
101                 : title(title), additionalText(additionalText), icon(icon), number(number), value(value), enabled(enabled) { }
102                 const char *title;
103                 const char *additionalText;
104                 const Sprite *icon;
105                 int number;
106                 T value;
107                 bool enabled;
108         };
109         std::vector<Entry> entries;
110         int selected;
111         int topRow;
112
113 };
114
115
116 template<class T>
117 Menu<T>::Menu()
118 : MenuProperties()
119 , selected(0)
120 , topRow(0) {
121
122 }
123
124 template<class T>
125 Menu<T>::Menu(const MenuProperties &p)
126 : MenuProperties(p)
127 , selected(0)
128 , topRow(0) {
129
130 }
131
132 template<class T>
133 Menu<T>::Menu(const Font *font, const Font *disabledFont, const Sprite *cursor, int charsPerEntry, int rows, int rowGap, int iconSpace, int cols, int colGap, int charsPerNumber, char delimiter, int charsPerAdditionalText, int additionalTextGap)
134 : MenuProperties(
135                 font, disabledFont ? disabledFont : font,
136                 cursor, charsPerEntry,
137                 rows, rowGap, iconSpace,
138                 cols, colGap, charsPerNumber,
139                 delimiter,
140                 charsPerAdditionalText,
141                 additionalTextGap)
142 , selected(0)
143 , topRow(0) {
144
145 }
146
147
148 template<class T>
149 int Menu<T>::ColWidth() const {
150         int width(iconSpace);
151         width += font->CharWidth() * (charsPerEntry + charsPerNumber);
152         if (charsPerNumber) {
153                 width += font->CharWidth();
154         }
155         if (charsPerAdditionalText) {
156                 width += additionalTextGap + charsPerAdditionalText * font->CharWidth();
157         }
158         return width;
159 }
160
161 template<class T>
162 int Menu<T>::Width() const {
163         return cols * ColWidth() + (cols - 1) * colGap;
164 }
165
166 template<class T>
167 int Menu<T>::Height() const {
168         return rows * font->CharHeight() + (rows - 1) * rowGap;
169 }
170
171
172 template<class T>
173 void Menu<T>::NextItem() {
174         SelectIndex(selected + 1);
175 }
176
177 template<class T>
178 void Menu<T>::PreviousItem() {
179         SelectIndex(selected - 1);
180 }
181
182 template<class T>
183 void Menu<T>::NextRow() {
184         SelectIndex(selected + cols);
185 }
186
187 template<class T>
188 void Menu<T>::PreviousRow() {
189         SelectIndex(selected - cols);
190 }
191
192 template<class T>
193 void Menu<T>::SelectIndex(int index) {
194         if (index < 0 || int(entries.size()) <= index) return;
195         selected = index;
196         if (topRow <= GetRow(selected) - rows) {
197                 topRow = GetRow(selected) - rows + 1;
198         } else if (GetRow(selected) < topRow) {
199                 topRow = GetRow(selected);
200         }
201 }
202
203
204 template<class T>
205 void Menu<T>::Draw(SDL_Surface *dest, const geometry::Vector<int> &position) const {
206         int start(topRow * cols);
207         int slots(rows * cols);
208         int items(entries.size() - start);
209         int end(start + (items < slots ? items : slots));
210         for (int i(0), count(end - start); i < count; ++i) {
211                 if (!entries[start + i].title) continue;
212                 geometry::Vector<int> iconOffset(
213                                 (i % cols) * (ColWidth() + colGap),
214                                 (i / cols) * RowHeight());
215                 if (entries[start + i].icon) {
216                         entries[start + i].icon->Draw(dest, position + iconOffset);
217                 }
218                 geometry::Vector<int> textOffset(iconOffset.X() + iconSpace, iconOffset.Y());
219                 const Font *usedFont(entries[start + i].enabled ? font : disabledFont);
220                 usedFont->DrawString(entries[start + i].title, dest, position + textOffset, charsPerEntry);
221
222                 textOffset += geometry::Vector<int>(charsPerEntry * usedFont->CharWidth(), 0);
223
224                 if (charsPerAdditionalText) {
225                         textOffset += geometry::Vector<int>(additionalTextGap, 0);
226                         if (entries[start + i].additionalText) {
227                                 usedFont->DrawString(entries[start + i].additionalText, dest, position + textOffset, charsPerAdditionalText);
228                         }
229                         textOffset += geometry::Vector<int>(charsPerAdditionalText * usedFont->CharWidth(), 0);
230                 }
231
232                 if (charsPerNumber) {
233                         usedFont->DrawChar(delimiter, dest, position + textOffset);
234                         textOffset += geometry::Vector<int>(usedFont->CharWidth(), 0);
235                         usedFont->DrawNumber(entries[start + i].number, dest, position + textOffset);
236                 }
237         }
238         geometry::Vector<int> cursorOffset(
239                         (selected % cols) * (ColWidth() + colGap) - cursor->Width(),
240                         ((selected - start) / cols) * RowHeight());
241         cursor->Draw(dest, position + cursorOffset);
242 }
243
244 }
245
246 #endif /* GRAPHICS_MENU_H_ */