1 /*
2 * This file is part of EasyRPG Player.
3 *
4 * EasyRPG Player is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * EasyRPG Player is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 // Headers
19 #include "window_selectable.h"
20 #include "game_system.h"
21 #include "input.h"
22 #include "util_macro.h"
23 #include "bitmap.h"
24
25 constexpr int arrow_animation_frames = 20;
26
27 // Constructor
Window_Selectable(int ix,int iy,int iwidth,int iheight)28 Window_Selectable::Window_Selectable(int ix, int iy, int iwidth, int iheight) :
29 Window_Base(ix, iy, iwidth, iheight) { }
30
CreateContents()31 void Window_Selectable::CreateContents() {
32 SetContents(Bitmap::Create(width - 16, max(height - border_y * 2, GetRowMax() * menu_item_height)));
33 }
34
35 // Properties
36
GetIndex() const37 int Window_Selectable::GetIndex() const {
38 return index;
39 }
SetIndex(int nindex)40 void Window_Selectable::SetIndex(int nindex) {
41 index = min(nindex, item_max - 1);
42 if (active && help_window != NULL) {
43 UpdateHelp();
44 }
45 UpdateCursorRect();
46 }
GetRowMax() const47 int Window_Selectable::GetRowMax() const {
48 return (item_max + column_max - 1) / column_max;
49 }
GetTopRow() const50 int Window_Selectable::GetTopRow() const {
51 return oy / menu_item_height;
52 }
SetTopRow(int row)53 void Window_Selectable::SetTopRow(int row) {
54 if (row < 0) row = 0;
55 if (row > GetRowMax() - 1) row = GetRowMax() - 1;
56 SetOy(row * menu_item_height);
57 }
GetPageRowMax() const58 int Window_Selectable::GetPageRowMax() const {
59 return (height - border_y * 2) / menu_item_height;
60 }
GetPageItemMax()61 int Window_Selectable::GetPageItemMax() {
62 return GetPageRowMax() * column_max;
63 }
64
GetItemRect(int index)65 Rect Window_Selectable::GetItemRect(int index) {
66 Rect rect = Rect();
67 rect.width = (width / column_max - 16);
68 rect.x = (index % column_max * (rect.width + 16));
69 rect.height = menu_item_height - 4;
70 rect.y = index / column_max * menu_item_height + menu_item_height / 8;
71 return rect;
72 }
73
GetHelpWindow()74 Window_Help* Window_Selectable::GetHelpWindow() {
75 return help_window;
76 }
77
SetHelpWindow(Window_Help * nhelp_window)78 void Window_Selectable::SetHelpWindow(Window_Help* nhelp_window) {
79 help_window = nhelp_window;
80 if (active && help_window != NULL) {
81 UpdateHelp();
82 }
83 }
84
UpdateHelp()85 void Window_Selectable::UpdateHelp() {
86 if (UpdateHelpFn && help_window != nullptr) {
87 UpdateHelpFn(*help_window, index);
88 }
89 }
90
91 // Update Cursor Rect
UpdateCursorRect()92 void Window_Selectable::UpdateCursorRect() {
93 int cursor_width = 0;
94 int x = 0;
95 if (index < 0) {
96 SetCursorRect(Rect());
97 return;
98 }
99 int row = index / column_max;
100 if (row < GetTopRow()) {
101 SetTopRow(row);
102 } else if (row > GetTopRow() + (GetPageRowMax() - 1)) {
103 SetTopRow(row - (GetPageRowMax() - 1));
104 }
105
106 cursor_width = (width / column_max - 16) + 8;
107 x = (index % column_max * (cursor_width + 8)) - 4;
108
109 int y = index / column_max * menu_item_height - oy;
110 SetCursorRect(Rect(x, y, cursor_width, menu_item_height));
111 }
112
UpdateArrows()113 void Window_Selectable::UpdateArrows() {
114 bool show_up_arrow = (GetTopRow() > 0);
115 bool show_down_arrow = (GetTopRow() < (GetRowMax() - GetPageRowMax()));
116
117 if (show_up_arrow || show_down_arrow) {
118 arrow_frame = (arrow_frame + 1) % (arrow_animation_frames * 2);
119 }
120 bool arrow_visible = (arrow_frame < arrow_animation_frames);
121 SetUpArrow(show_up_arrow && arrow_visible);
122 SetDownArrow(show_down_arrow && arrow_visible);
123 }
124
125 // Update
Update()126 void Window_Selectable::Update() {
127 Window_Base::Update();
128 if (active && item_max > 0 && index >= 0) {
129 if (scroll_dir != 0) {
130 scroll_progress++;
131 SetOy(GetOy() + (menu_item_height * scroll_progress / 4 - menu_item_height * (scroll_progress - 1) / 4) * scroll_dir);
132 UpdateArrows();
133 if (scroll_progress < 4) {
134 return;
135 } else {
136 scroll_dir = 0;
137 scroll_progress = 0;
138 if (active && help_window != NULL) {
139 UpdateHelp();
140 }
141 UpdateCursorRect();
142 }
143 }
144
145 int old_index = index;
146
147 auto move_down = [&]() {
148 if (index < item_max - column_max || column_max == 1 ) {
149 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
150 index = (index + column_max) % item_max;
151 }
152 };
153 if (Input::IsTriggered(Input::DOWN) || Input::IsTriggered(Input::SCROLL_DOWN)) {
154 move_down();
155 } else if (Input::IsRepeated(Input::DOWN)) {
156 if (endless_scrolling || (index + column_max) % item_max > index) {
157 move_down();
158 }
159 }
160
161 auto move_up = [&]() {
162 if (index >= column_max || column_max == 1) {
163 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
164 index = (index - column_max + item_max) % item_max;
165 }
166 };
167 if (Input::IsTriggered(Input::UP) || Input::IsTriggered(Input::SCROLL_UP)) {
168 move_up();
169 } else if (Input::IsRepeated(Input::UP)) {
170 if (endless_scrolling || (index - column_max + item_max) % item_max < index) {
171 move_up();
172 }
173 }
174
175 // page up/down is limited to selectables with one column
176 if (column_max == 1) {
177 if (Input::IsRepeated(Input::PAGE_DOWN) && index < item_max - 1) {
178 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
179 int new_pos = index + GetPageRowMax();
180 index = (new_pos <= item_max - 1) ? new_pos : item_max - 1;
181 }
182 if (Input::IsRepeated(Input::PAGE_UP) && index > 0) {
183 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
184 int new_pos = index - GetPageRowMax();
185 index = (new_pos >= 0) ? new_pos : 0;
186 }
187 }
188 if (Input::IsRepeated(Input::RIGHT)) {
189 if (column_max >= 2 && index < item_max - 1) {
190 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
191 index += 1;
192 }
193 }
194 if (Input::IsRepeated(Input::LEFT)) {
195 if (column_max >= 2 && index > 0) {
196 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
197 index -= 1;
198 }
199 }
200
201 if (std::abs(index - old_index) <= column_max) {
202 int row = index / column_max;
203 if (row < GetTopRow() && old_index < item_max - 1) {
204 scroll_dir = -1;
205 return;
206 } else if (row > GetTopRow() + (GetPageRowMax() - 1) && old_index > 0) {
207 scroll_dir = 1;
208 return;
209 }
210 }
211 }
212 if (active && help_window != NULL) {
213 UpdateHelp();
214 }
215 UpdateCursorRect();
216 UpdateArrows();
217 }
218
219 // Set endless scrolling state
SetEndlessScrolling(bool state)220 void Window_Selectable::SetEndlessScrolling(bool state) {
221 endless_scrolling = state;
222 }
223
224 // Set menu item height
SetMenuItemHeight(int height)225 void Window_Selectable::SetMenuItemHeight(int height) {
226 menu_item_height = height;
227 }
228