1 #include "option_popup.h"
2
3 #include "assets/assets.h"
4 #include "core/image_group.h"
5 #include "graphics/generic_button.h"
6 #include "graphics/graphics.h"
7 #include "graphics/image.h"
8 #include "graphics/lang_text.h"
9 #include "graphics/panel.h"
10 #include "graphics/screen.h"
11 #include "graphics/scrollbar.h"
12 #include "graphics/text.h"
13 #include "graphics/window.h"
14 #include "input/input.h"
15 #include "translation/translation.h"
16
17 #define CANCEL_BUTTON 0
18 #define CONFIRM_BUTTON 1
19 #define MAX_OPTIONS 3
20 #define START_Y_OFFSET 88
21
22 static const int Y_OFFSET_PER_OPTION[2] = { 112, 144 };
23 static int border_image_ids[2];
24
25 static void on_scroll(void);
26
27 static void button_select_option(int option, int param2);
28
29 static generic_button buttons[] = {
30 {40, 0, 180, 20, button_select_option, button_none, CANCEL_BUTTON, 0},
31 {260, 0, 180, 20, button_select_option, button_none, CONFIRM_BUTTON, 0},
32 {20, 0, 0, 0, button_select_option, button_none, 2, 0},
33 {20, 0, 0, 0, button_select_option, button_none, 3, 0},
34 {20, 0, 0, 0, button_select_option, button_none, 4, 0}
35 };
36
37 static scrollbar_type scrollbar = { 420, START_Y_OFFSET + 40, 0, on_scroll, 4 };
38
39 static struct {
40 int title;
41 int subtitle;
42 option_menu_item *options;
43 int num_options;
44 int width_blocks;
45 int height_blocks;
46 void (*close_func)(int selection);
47 int focus_button_id;
48 int original_option;
49 int selected_option;
50 int price;
51 int visible_options;
52 int scroll_position;
53 int height;
54 option_menu_row_size row_size;
55 } data;
56
init(int title,int subtitle,option_menu_item * options,int num_options,void (* close_func)(int selection),int original_option,int price,option_menu_row_size row_size)57 static int init(int title, int subtitle, option_menu_item *options, int num_options,
58 void (*close_func)(int selection), int original_option, int price, option_menu_row_size row_size)
59 {
60 if (window_is(WINDOW_POPUP_DIALOG)) {
61 // don't show popup over popup
62 return 0;
63 }
64 data.num_options = num_options <= MAX_OPTIONS ? num_options : MAX_OPTIONS;
65 data.close_func = close_func;
66 data.title = title;
67 data.subtitle = subtitle;
68 data.width_blocks = 30;
69 data.original_option = original_option;
70 data.selected_option = data.original_option;
71 data.price = price;
72 data.options = options;
73 data.height = 0;
74 data.row_size = row_size;
75
76 for (int i = 0; i < MAX_OPTIONS; i++) {
77 buttons[i + 2].y = 40 + START_Y_OFFSET + i * Y_OFFSET_PER_OPTION[data.row_size];
78 buttons[i + 2].height = Y_OFFSET_PER_OPTION[data.row_size] - 16;
79 }
80
81 if (!border_image_ids[0]) {
82 border_image_ids[0] = assets_get_image_id("UI_Elements", "Policy Selection Borders");
83 border_image_ids[1] = assets_get_image_id("UI_Elements", "Monument Mod Selection Borders");
84 }
85 return 1;
86 }
87
calculate_visible_options(void)88 static void calculate_visible_options(void)
89 {
90 if (data.height == screen_height()) {
91 return;
92 }
93 data.height = screen_height();
94 data.visible_options = (data.height - 160) / Y_OFFSET_PER_OPTION[data.row_size];
95 if (data.visible_options > data.num_options) {
96 data.visible_options = data.num_options;
97 }
98 data.height_blocks = 10 + data.visible_options * (data.row_size == OPTION_MENU_SMALL_ROW ? 7 : 9);
99 buttons[0].y = buttons[1].y = START_Y_OFFSET + 42 + Y_OFFSET_PER_OPTION[data.row_size] * data.visible_options;
100
101 buttons[2].width = buttons[3].width = buttons[4].width = data.num_options == data.visible_options ? 430 : 400;
102
103 scrollbar.height = Y_OFFSET_PER_OPTION[data.row_size] * data.visible_options;
104 scrollbar_init(&scrollbar, 0, data.num_options - data.visible_options);
105 if (data.selected_option > data.visible_options) {
106 scrollbar.scroll_position = data.selected_option - data.visible_options;
107 }
108 }
109
draw_apply_button(int x,int y,int box_width)110 static void draw_apply_button(int x, int y, int box_width)
111 {
112 font_t font = data.selected_option != data.original_option ? FONT_NORMAL_BLACK : FONT_NORMAL_PLAIN;
113 color_t color = data.selected_option != data.original_option ? 0 : COLOR_FONT_LIGHT_GRAY;
114 if (!data.price) {
115 text_draw_centered(translation_for(TR_OPTION_MENU_APPLY), x, y, box_width, font, color);
116 } else {
117 text_draw_with_money(translation_for(TR_OPTION_MENU_APPLY), data.price, " (", ")",
118 x, y, box_width, font, color);
119 }
120 }
121
draw_background(void)122 static void draw_background(void)
123 {
124 window_draw_underlying_window();
125
126 calculate_visible_options();
127 graphics_in_dialog_with_size(16 * data.width_blocks, 16 * data.height_blocks);
128 outer_panel_draw(0, 0, data.width_blocks, data.height_blocks);
129
130 text_draw_centered(translation_for(data.title), 0, 20, 480, FONT_LARGE_BLACK, 0);
131 text_draw_multiline(translation_for(data.subtitle), 20, 60, 440, FONT_NORMAL_BLACK, 0);
132 if (data.price) {
133 text_draw_with_money(translation_for(TR_OPTION_MENU_COST), data.price, " ", ".",
134 20, 110, 0, FONT_NORMAL_BLACK, 0);
135 }
136
137 int y_offset = START_Y_OFFSET;
138
139 for (int i = 0; i < data.visible_options; i++) {
140 int text_width = data.num_options == data.visible_options ? 448 : 400;
141 int text_x = 20;
142
143 if (data.options[i + scrollbar.scroll_position].image_id) {
144 image_draw(data.options[i + scrollbar.scroll_position].image_id, text_x, y_offset + 42);
145 int offset = data.row_size == OPTION_MENU_SMALL_ROW ? 128 : 160;
146 text_x += offset;
147 text_width -= offset;
148 }
149 if (data.selected_option == i + scrollbar.scroll_position + 1) {
150 inner_panel_draw(text_x - 6, y_offset + 44,
151 text_width / 16,
152 Y_OFFSET_PER_OPTION[data.row_size] / 16 - 1);
153 }
154 text_draw_multiline(translation_for(data.options[i + scrollbar.scroll_position].header),
155 text_x, y_offset + 49, text_width - 4,
156 data.selected_option == i + scrollbar.scroll_position + 1 ? FONT_NORMAL_WHITE : FONT_NORMAL_BLACK, 0);
157 text_draw_multiline(translation_for(data.options[i + scrollbar.scroll_position].desc),
158 text_x, y_offset + 69, text_width - 4,
159 data.selected_option == i + scrollbar.scroll_position + 1 ? FONT_NORMAL_WHITE : FONT_NORMAL_BLACK, 0);
160
161
162 y_offset += Y_OFFSET_PER_OPTION[data.row_size];
163 }
164
165 lang_text_draw_centered(13, 4, 40, buttons[0].y + 4,
166 180, FONT_NORMAL_BLACK);
167 draw_apply_button(260, buttons[1].y + 4, 180);
168
169 graphics_reset_dialog();
170 }
171
draw_option_image_border(int x,int y,int focused)172 static void draw_option_image_border(int x, int y, int focused)
173 {
174 if (data.row_size == OPTION_MENU_SMALL_ROW) {
175 image_draw(border_image_ids[0] + focused, x, y);
176 image_draw(border_image_ids[0] + 2 + focused, x + 105, y + 5);
177 image_draw(border_image_ids[0] + 4 + focused, x, y + 90);
178 image_draw(border_image_ids[0] + 6 + focused, x, y + 5);
179 } else {
180 image_draw(border_image_ids[1] + focused, x, y);
181 image_draw(border_image_ids[1] + 2 + focused, x + 146, y + 5);
182 image_draw(border_image_ids[1] + 4 + focused, x, y + 126);
183 image_draw(border_image_ids[1] + 6 + focused, x, y + 5);
184 }
185 }
186
draw_foreground(void)187 static void draw_foreground(void)
188 {
189 graphics_in_dialog_with_size(16 * data.width_blocks, 16 * data.height_blocks);
190 for (int i = 0; i < data.visible_options; i++) {
191 if (data.options[i + scrollbar.scroll_position].image_id) {
192 draw_option_image_border(20, buttons[i + 2].y + 2, data.focus_button_id == i + 3);
193 }
194 }
195
196 button_border_draw(40, buttons[0].y, 180, 20, data.focus_button_id == 1);
197
198 button_border_draw(260, buttons[1].y, 180, 20,
199 data.focus_button_id == 2 && data.selected_option != data.original_option);
200
201 if (data.num_options > data.visible_options) {
202 inner_panel_draw(scrollbar.x + 4, scrollbar.y + 32, 2, scrollbar.height / 16 - 4);
203 scrollbar_draw(&scrollbar);
204 }
205
206 graphics_reset_dialog();
207 }
208
handle_input(const mouse * m,const hotkeys * h)209 static void handle_input(const mouse *m, const hotkeys *h)
210 {
211 if (input_go_back_requested(m, h)) {
212 data.close_func(0);
213 window_go_back();
214 }
215 const mouse *m_dialog = mouse_in_dialog_with_size(m, data.width_blocks * 16, data.height_blocks * 16);
216 if (scrollbar_handle_mouse(&scrollbar, m_dialog)) {
217 return;
218 }
219 if (generic_buttons_handle_mouse(m_dialog, 0, 0, buttons, data.visible_options + 2, &data.focus_button_id)) {
220 return;
221 }
222 }
223
on_scroll(void)224 static void on_scroll(void)
225 {
226 window_invalidate();
227 }
228
button_select_option(int option,int param2)229 static void button_select_option(int option, int param2)
230 {
231 switch (option) {
232 case CANCEL_BUTTON:
233 data.close_func(0);
234 window_go_back();
235 break;
236 case CONFIRM_BUTTON:
237 if (data.selected_option != data.original_option) {
238 data.close_func(data.selected_option);
239 window_go_back();
240 }
241 break;
242 default:
243 data.selected_option = option + scrollbar.scroll_position - CONFIRM_BUTTON;
244 window_request_refresh();
245 break;
246 }
247 }
248
window_option_popup_show(int title,int subtitle,option_menu_item * options,int num_options,void (* close_func)(int selection),int current_option,int price,option_menu_row_size row_size)249 void window_option_popup_show(int title, int subtitle, option_menu_item *options, int num_options,
250 void (*close_func)(int selection), int current_option, int price, option_menu_row_size row_size)
251 {
252 if (init(title, subtitle, options, num_options, close_func, current_option, price, row_size)) {
253 window_type window = {
254 WINDOW_POPUP_DIALOG,
255 draw_background,
256 draw_foreground,
257 handle_input
258 };
259 window_show(&window);
260 }
261 }
262