1 #ifndef GRAPHICS_MENU_H_
2 #define GRAPHICS_MENU_H_
12 #include "../math/Vector.h"
20 struct MenuProperties {
21 static const int TYPE_ID = 407;
24 const Font *disabledFont;
26 const Sprite *selectedCursor;
27 const Animation *cursorAnimation;
28 const Animation *selectedCursorAnimation;
36 int charsPerAdditionalText;
37 int additionalTextGap;
44 : font(0), disabledFont(0), cursor(0), selectedCursor(0)
45 , cursorAnimation(0), selectedCursorAnimation(0)
46 , charsPerEntry(0), rows(1), rowGap(0)
47 , iconSpace(0), cols(1), colGap(0)
48 , charsPerNumber(0), charsPerAdditionalText(0)
49 , additionalTextGap(0), thirdColumnHack(0), delimiter(':')
50 , wrapX(false), wrapY(false) { }
52 static void CreateTypeDescription();
53 static void Construct(void *);
59 : private MenuProperties {
63 Menu(const MenuProperties &);
66 void SetInactive() { state = STATE_INACTIVE; }
67 void SetActive() { state = STATE_ACTIVE; }
68 void SetSelected() { state = STATE_SELECTED; }
69 void SetDualSelection() { state = STATE_DUAL; secondarySelection = selected; }
70 bool IsActive() const { return state == STATE_ACTIVE; }
71 bool HasSelected() const { return state == STATE_SELECTED; }
72 bool InDualMode() const { return state == STATE_DUAL; }
77 int RowHeight() const { return font->CharHeight() + rowGap; }
78 int CharsPerEntry() const { return charsPerEntry; }
80 T &Selected() { return entries[selected].value; }
81 const T &Selected() const { return entries[selected].value; }
82 const char *SelectedTitle() const { return entries[selected].title; }
83 int SelectedNumber() const { return entries[selected].number; }
84 bool SelectedIsEnabled() const { return entries[selected].enabled; }
86 T &SecondarySelection() { return entries[secondarySelection].value; }
87 const T &SecondarySelection() const { return entries[secondarySelection].value; }
88 const char *SecondaryTitle() const { return entries[secondarySelection].title; }
89 int SecondaryNumber() const { return entries[secondarySelection].number; }
90 bool SecondaryIsEnabled() const { return entries[secondarySelection].enabled; }
92 void SwapSelected() { SwapEntriesAt(selected, secondarySelection); }
93 void SwapEntriesAt(int lhs, int rhs) { std::swap(entries[lhs], entries[rhs]); }
99 void SelectIndex(int index);
100 int SelectedIndex() const { return selected; }
101 int SecondaryIndex() const { return secondarySelection; }
102 bool IsSelected(int index) const { return index == selected; }
104 int EntryCount() const { return entries.size(); }
105 T &ValueAt(int index) { return entries[index].value; }
106 const T &ValueAt(int index) const { return entries[index].value; }
108 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)); }
109 void AddEmptyEntry() { entries.push_back(Entry(0, T(), false)); }
110 void Disable(int index) { entries[index].enabled = false; }
111 void Enable(int index) { entries[index].enabled = true; }
112 void Reserve(int n) { entries.reserve(n); }
113 void Clear() { entries.clear(); }
114 void ClearEntry(int at) { entries[at] = Entry(0, T(), false); }
116 void StartAnimation(app::Application &ctrl);
117 void StartAnimation(app::State &ctrl);
118 void StopAnimation();
120 void Draw(SDL_Surface *dest, const math::Vector<int> &position) const;
123 int GetRow(int index) const { return index / cols; }
124 int GetCol(int index) const { return index % cols; }
126 void DrawCursor(SDL_Surface *, const math::Vector<int> &) const;
127 void DrawSelectedCursor(SDL_Surface *, const math::Vector<int> &) const;
131 Entry(const char *title, const T &value, bool enabled = true, const Sprite *icon = 0, int number = 0, const char *additionalText = 0)
132 : title(title), additionalText(additionalText), icon(icon), number(number), value(value), enabled(enabled) { }
134 const char *additionalText;
140 AnimationRunner animation;
141 AnimationRunner selectedAnimation;
142 std::vector<Entry> entries;
144 int secondarySelection;
161 , secondarySelection(0)
163 , state(STATE_ACTIVE) {
168 Menu<T>::Menu(const MenuProperties &p)
170 , animation(cursorAnimation)
171 , selectedAnimation(selectedCursorAnimation)
173 , secondarySelection(0)
175 , state(STATE_ACTIVE) {
181 int Menu<T>::ColWidth() const {
182 int width(iconSpace);
183 width += font->CharWidth() * (charsPerEntry + charsPerNumber);
184 if (charsPerNumber) {
185 width += font->CharWidth();
187 if (charsPerAdditionalText) {
188 width += additionalTextGap + charsPerAdditionalText * font->CharWidth();
194 int Menu<T>::Width() const {
195 return cols * ColWidth() + (cols - 1) * colGap;
199 int Menu<T>::Height() const {
200 return rows * font->CharHeight() + (rows - 1) * rowGap;
205 void Menu<T>::NextItem() {
206 int index(selected + 1);
207 if (wrapX && index % cols == 0) {
214 void Menu<T>::PreviousItem() {
215 int index(selected - 1);
216 if (wrapX && selected % cols == 0) {
223 void Menu<T>::NextRow() {
224 int index(selected + cols);
225 if (wrapY && index >= int(entries.size())) {
226 index -= entries.size();
232 void Menu<T>::PreviousRow() {
233 int index(selected - cols);
234 if (wrapY && index < 0) {
235 index += entries.size();
241 void Menu<T>::SelectIndex(int index) {
242 if (index < 0 || int(entries.size()) <= index) return;
244 if (topRow <= GetRow(selected) - rows) {
245 topRow = GetRow(selected) - rows + 1;
246 } else if (GetRow(selected) < topRow) {
247 topRow = GetRow(selected);
253 void Menu<T>::StartAnimation(app::Application &ctrl) {
254 if (cursorAnimation) {
255 animation.Start(ctrl);
257 if (selectedCursorAnimation) {
258 selectedAnimation.Start(ctrl);
263 void Menu<T>::StartAnimation(app::State &ctrl) {
264 if (cursorAnimation) {
265 animation.Start(ctrl);
267 if (selectedCursorAnimation) {
268 selectedAnimation.Start(ctrl);
273 void Menu<T>::StopAnimation() {
275 selectedAnimation.Stop();
280 void Menu<T>::Draw(SDL_Surface *dest, const math::Vector<int> &position) const {
281 int start(topRow * cols);
282 int slots(rows * cols);
283 int items(entries.size() - start);
284 int end(start + (items < slots ? items : slots));
285 for (int i(0), count(end - start); i < count; ++i) {
286 if (!entries[start + i].title) continue;
287 math::Vector<int> iconOffset(
288 (i % cols) * (ColWidth() + colGap),
289 (i / cols) * RowHeight());
291 // This fixes the position of the third column of the inventory and capsule menus.
292 if (thirdColumnHack && i % cols == 2) {
293 iconOffset += math::Vector<int>(font->CharWidth() * thirdColumnHack, 0);
296 if (entries[start + i].icon) {
297 entries[start + i].icon->Draw(dest, position + iconOffset);
299 math::Vector<int> textOffset(iconOffset.X() + iconSpace, iconOffset.Y());
300 const Font *usedFont(entries[start + i].enabled ? font : disabledFont);
301 usedFont->DrawString(entries[start + i].title, dest, position + textOffset, charsPerEntry);
303 textOffset += math::Vector<int>(charsPerEntry * usedFont->CharWidth(), 0);
305 if (charsPerAdditionalText) {
306 textOffset += math::Vector<int>(additionalTextGap, 0);
307 if (entries[start + i].additionalText) {
308 usedFont->DrawString(entries[start + i].additionalText, dest, position + textOffset, charsPerAdditionalText);
310 textOffset += math::Vector<int>(charsPerAdditionalText * usedFont->CharWidth(), 0);
313 if (charsPerNumber) {
314 usedFont->DrawChar(delimiter, dest, position + textOffset);
315 textOffset += math::Vector<int>(usedFont->CharWidth(), 0);
316 usedFont->DrawNumber(entries[start + i].number, dest, position + textOffset, charsPerNumber);
319 math::Vector<int> cursorOffset(
320 (selected % cols) * (ColWidth() + colGap) - cursor->Width(),
321 ((selected - start) / cols) * RowHeight());
322 // This fixes the position of the third column of the inventory and capsule menus.
323 if (thirdColumnHack && selected % cols == 2) {
324 cursorOffset += math::Vector<int>(font->CharWidth() * thirdColumnHack, 0);
330 DrawCursor(dest, position + cursorOffset);
333 DrawSelectedCursor(dest, position + cursorOffset);
336 DrawCursor(dest, position + cursorOffset
337 - math::Vector<int>(selectedCursor->Width(), 0));
338 if (secondarySelection >= start && secondarySelection <= end) {
339 math::Vector<int> secondaryOffset(
340 (secondarySelection % cols) * (ColWidth() + colGap) - cursor->Width(),
341 ((secondarySelection - start) / cols) * RowHeight());
342 DrawSelectedCursor(dest, position + secondaryOffset);
349 void Menu<T>::DrawCursor(
351 const math::Vector<int> &position) const {
352 if (animation.Running()) {
353 animation.Draw(dest, position);
355 cursor->Draw(dest, position);
360 void Menu<T>::DrawSelectedCursor(
362 const math::Vector<int> &position) const {
363 if (selectedAnimation.Running()) {
364 selectedAnimation.Draw(dest, position);
366 selectedCursor->Draw(dest, position);