summaryrefslogtreecommitdiff
path: root/g_src/ViewBase.h
blob: 315abf310d56ae9dfc171f354ca3e97a779fe56e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#ifndef VIEWBASE_H
#define VIEWBASE_H

#include <set>
#include <map>
#include <string>

#include "keybindings.h"
#include "graphics.h"

enum InterfaceBreakdownTypes
{
	INTERFACE_BREAKDOWN_NONE,
	INTERFACE_BREAKDOWN_QUIT,
	INTERFACE_BREAKDOWN_STOPSCREEN,
	INTERFACE_BREAKDOWN_TOFIRST,
	INTERFACE_BREAKDOWNNUM
};

class viewscreenst
{
	public:
		viewscreenst *child;
		viewscreenst *parent;
		char breakdownlevel;

		char option_key_pressed;
		virtual void feed(std::set<InterfaceKey> &events){}
		virtual void logic(){}
		virtual void render(){}
                virtual void resize(int w, int h){}

		virtual void help();
		virtual char movies_okay(){return 1;}
		virtual char is_option_screen(){return 0;}
		virtual char is_save_screen(){return 0;}
		viewscreenst()
			{
			child=0;
			parent=0;
			breakdownlevel=INTERFACE_BREAKDOWN_NONE;
			option_key_pressed=0;
			}
		virtual ~viewscreenst(){}

		virtual bool key_conflict(InterfaceKey test_key);
};

namespace widgets {

  using namespace std;
  
  template <typename T>
  class menu {
    typedef map<int,pair<string, T> > dict;
    dict lines;
    int selection;
    int last_displayheight;
    bool bleached;
    map<int, pair<int,int> > colors;
    
    // Given 'total' lines, with 'sel' selected, and 'space' to draw in,
    // returns the first line that should be drawn.
    int first_line(int total, int sel, int space) {
      // There is no doubt some clever math to do this, but I'm tired and don't care.
      for (int start = 0;; start += space / 2) {
        if (start + space/2 >= sel) return start;
        if (start + space >= total) return start;
      }
    }
    pair<string,T> mp(string s, T t) { return make_pair(s,t); }

    // Scrolls N lines up/down; positive = down
    void scroll(int n) {
      typename dict::iterator it = lines.find(selection);
      for (int i = 0; i < abs(n); i++) {
        if (n < 0 && it == lines.begin()) { // We've hit the top
          if (i) break;
          else {
            it = --(lines.end());
            break;
          }
        }
        if (n < 0) --it; else ++it; // Scroll one line
        if (it == lines.end()) { // We've hit the bottom
          if (i) {
            --it;
            break;
          }
          else {
            it = lines.begin();
            break;
          }
        }
        // If we hit neither the top nor bottom, loop.
      }

      selection = it->first;
    }
    
  public:
    menu() { clear(); }
    int size() { return lines.size(); }
    // Adds a line just past the last taken position
    void add(string text, T token) {
      if (!lines.size()) {
        lines[0] = mp(text,token);
      } else {
        typename dict::iterator it = --(lines.end());
        lines[it->first + 1] = mp(text,token);
      }
    }
    // (Re)sets the text of the given line
    void set(int line, string text, T token) {
      lines[line] = mp(text,token);
    }
    // Set the color of a line
    void set_color(int line, int fg, int bg) {
      colors[line] = make_pair(fg,bg);
    }
    // Handles (page) up/down
    void feed(std::set<InterfaceKey> &input) {
      if (!lines.size()) return;
      if (input.count(INTERFACEKEY_STANDARDSCROLL_UP)) scroll(-1);
      if (input.count(INTERFACEKEY_STANDARDSCROLL_DOWN)) scroll(1);
      if (input.count(INTERFACEKEY_STANDARDSCROLL_PAGEUP)) scroll(-(last_displayheight / 2));
      if (input.count(INTERFACEKEY_STANDARDSCROLL_PAGEDOWN)) scroll(last_displayheight / 2);        
    }
    void render(int x1, int x2, int y1, int y2) {
      gps.erasescreen_rect(x1,x2,y1,y2);
      int h = y2 - y1 + 1,
        w = x2 - x1 + 1,
        x = x1, y = y1;
      last_displayheight = h;
      if (!lines.size()) return;
      int total = (--lines.end())->first + 1;
      int first = first_line(total, selection, h);
      typename dict::iterator it = lines.lower_bound(first);
      for (; it != lines.end() && it->first - first < h; ++it) {
        gps.locate(it->first - first + y, x);
        map<int,pair<int,int> >::iterator color = colors.find(it->first - first);
        int fg = 7, bg = 0;
        if (color != colors.end()) {
          fg = color->second.first;
          bg = color->second.second;
        }
        gps.changecolor(fg, bg, it->first == selection && !bleached);
        gps.addst(it->second.first.substr(0, w));
      }
    }
    // Read out the current selection
    T get_selection() { return lines[selection].second; }
    int get_pos() { return selection; }
    // Set the position by line
    void set_pos(int pos) {
      if (pos < size())
        selection = pos;
    }
    // Delete the currently selected line
    void del_selection() {
      typename dict::iterator it = lines.find(selection);
      typename dict::iterator newsel = it;
      ++newsel;
      if (newsel == lines.end()) {
        newsel = it;
        --newsel;
      }
      lines.erase(it);
      if (lines.size()) selection = newsel->first;
    }
    // If true, don't draw a highlight
    void bleach(bool b) { bleached = b; }
    // Reset the menu
    void clear() {
      selection = 0;
      lines.clear();
      last_displayheight = 10;
      bleached = false;
      colors.clear();
    }
  };

  class textbox {
    string text;
    bool keep;
  public:
    textbox() { textbox("", false); }
    textbox(string initializer, bool keep) { this->keep = keep; text = initializer; }
    string get_text() { return text; }
    // Only cares about INTERFACEKEY_STRING events
    void feed(std::set<InterfaceKey> &input);
    void render(int x1, int x2, int y1, int y2);
  };

}

#endif