Antkeeper  0.0.1
menu.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "game/menu.hpp"
21 #include <engine/scene/text.hpp>
25 #include <engine/config.hpp>
26 #include <algorithm>
27 #include <engine/math/vector.hpp>
28 
29 namespace menu {
30 
31 void init_menu_item_index(::game& ctx, hash::fnv1a32_t menu_name)
32 {
33  if (auto it = ctx.menu_item_indices.find(menu_name); it != ctx.menu_item_indices.end())
34  {
35  ctx.menu_item_index = &it->second;
36  }
37  else
38  {
39  ctx.menu_item_index = &ctx.menu_item_indices[menu_name];
40  *ctx.menu_item_index = 0;
41  }
42 }
43 
44 void update_text_font(::game& ctx)
45 {
46  for (auto [name, value]: ctx.menu_item_texts)
47  {
48  name->set_material(ctx.menu_font_material);
49  name->set_font(&ctx.menu_font);
50 
51  if (value)
52  {
53  value->set_material(ctx.menu_font_material);
54  value->set_font(&ctx.menu_font);
55  }
56  }
57 }
58 
59 void update_text_color(::game& ctx)
60 {
61  for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
62  {
63  auto [name, value] = ctx.menu_item_texts[i];
64 
65  const math::fvec4& color = (i == *ctx.menu_item_index) ? config::menu_active_color : config::menu_inactive_color;
66 
67  name->set_color(color);
68  if (value)
69  value->set_color(color);
70  }
71 }
72 
73 void align_text(::game& ctx, bool center, bool has_back, float anchor_y)
74 {
75 
76 
77  const math::fvec2 viewport_size = math::fvec2(ctx.window->get_viewport_size());
78  const math::fvec2 viewport_center = viewport_size * 0.5f;
79 
80  const float viewport_padding = viewport_size.y() * (1.0f / 9.0f);
81 
82  // Calculate menu width
83  float m_width = ctx.menu_font.get_glyph_metrics(U'M').width;
84  float column_spacing = m_width * 2.0f;
85  const float min_two_column_row_width = m_width * 18.0f;
86  float menu_width = 0.0f;
87 
88  for (auto [name, value]: ctx.menu_item_texts)
89  {
90  float row_width = 0.0f;
91 
92  // Add name width to row width
93  const auto& name_bounds = name->get_bounds();
94  row_width += name_bounds.max.x() - name_bounds.min.x();
95 
96  if (value)
97  {
98  // Add value width to row width
99  const auto& value_bounds = value->get_bounds();
100  row_width += value_bounds.max.x() - value_bounds.min.x();
101 
102  // Add column spacing to row width
103  row_width += column_spacing;
104 
105  row_width = std::max<float>(min_two_column_row_width, row_width);
106  }
107 
108  menu_width = std::max<float>(menu_width, row_width);
109  }
110 
111  // Align texts
112  float menu_height;
113  if (has_back)
114  menu_height = (ctx.menu_item_texts.size() - 1) * ctx.menu_font.get_font_metrics().linespace - ctx.menu_font.get_font_metrics().linegap;
115  else
116  menu_height = ctx.menu_item_texts.size() * ctx.menu_font.get_font_metrics().linespace - ctx.menu_font.get_font_metrics().linegap;
117 
118  float menu_x = viewport_center.x() - menu_width * 0.5f;
119  float menu_y = viewport_center.y() + anchor_y + menu_height * 0.5f - ctx.menu_font.get_font_metrics().size;
120 
121  for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
122  {
123  auto [name, value] = ctx.menu_item_texts[i];
124 
125  float x = menu_x;
126  float y = menu_y - ctx.menu_font.get_font_metrics().linespace * i;
127  if (has_back && i == ctx.menu_item_texts.size() - 1)
128  {
129  y = viewport_padding;// + ctx.menu_font.get_font_metrics().linespace;
130  }
131 
132  if (center || i == ctx.menu_item_texts.size() - 1)
133  {
134  const auto& name_bounds = name->get_bounds();
135  const float name_width = name_bounds.max.x() - name_bounds.min.x();
136  x = viewport_center.x() - name_width * 0.5f;
137  }
138 
139  name->set_translation({std::round(x), std::round(y), 0.0f});
140 
141  if (value)
142  {
143  const auto& value_bounds = value->get_bounds();
144  const float value_width = value_bounds.max.x() - value_bounds.min.x();
145 
146  if (center || i == ctx.menu_item_texts.size() - 1)
147  x = viewport_center.x() - value_width * 0.5f;
148  else
149  x = menu_x + menu_width - value_width;
150 
151  value->set_translation({std::round(x), std::round(y), 0.0f});
152  }
153  }
154 }
155 
156 void refresh_text(::game& ctx)
157 {
158  for (auto [name, value]: ctx.menu_item_texts)
159  {
160  name->refresh();
161  if (value)
162  value->refresh();
163  }
164 }
165 
166 void add_text_to_ui(::game& ctx)
167 {
168  for (auto [name, value]: ctx.menu_item_texts)
169  {
170  ctx.ui_scene->add_object(*name);
171  if (value)
172  ctx.ui_scene->add_object(*value);
173  }
174 }
175 
177 {
178  for (auto [name, value]: ctx.menu_item_texts)
179  {
180  ctx.ui_scene->remove_object(*name);
181  if (value)
182  ctx.ui_scene->remove_object(*value);
183  }
184 }
185 
186 void delete_text(::game& ctx)
187 {
188  ctx.menu_item_texts.clear();
189 }
190 
192 {
193  ctx.animator->remove_animation(ctx.menu_fade_animation.get());
194  ctx.menu_fade_animation.reset();
195 }
196 
197 void clear_callbacks(::game& ctx)
198 {
199  // Clear menu item callbacks
200  ctx.menu_left_callbacks.clear();
201  ctx.menu_right_callbacks.clear();
202  ctx.menu_select_callbacks.clear();
203  ctx.menu_back_callback = nullptr;
204 }
205 
206 void setup_animations(::game& ctx)
207 {
208  ctx.menu_fade_animation = std::make_unique<animation<float>>();
209  animation_channel<float>* opacity_channel = ctx.menu_fade_animation->add_channel(0);
210 
211  ctx.menu_fade_animation->set_frame_callback
212  (
213  [&ctx](int channel, const float& opacity)
214  {
215  for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
216  {
217  auto [name, value] = ctx.menu_item_texts[i];
218 
219  math::fvec4 color = (i == *ctx.menu_item_index) ? config::menu_active_color : config::menu_inactive_color;
220  color[3] = color[3] * opacity;
221 
222  if (name)
223  name->set_color(color);
224  if (value)
225  value->set_color(color);
226  }
227  }
228  );
229 
230  ctx.animator->add_animation(ctx.menu_fade_animation.get());
231 }
232 
233 void fade_in(::game& ctx, const std::function<void()>& end_callback)
234 {
235  ctx.menu_fade_animation->set_interpolator(ease<float>::out_cubic);
236  animation_channel<float>* opacity_channel = ctx.menu_fade_animation->get_channel(0);
237  opacity_channel->remove_keyframes();
238  opacity_channel->insert_keyframe({0.0f, 0.0f});
239  opacity_channel->insert_keyframe({config::menu_fade_in_duration, 1.0f});
240  ctx.menu_fade_animation->set_end_callback(end_callback);
241 
242  for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
243  {
244  auto [name, value] = ctx.menu_item_texts[i];
245 
246  math::fvec4 color = (i == *ctx.menu_item_index) ? config::menu_active_color : config::menu_inactive_color;
247  color[3] = 0.0f;
248 
249  if (name)
250  {
251  name->set_color(color);
252  }
253  if (value)
254  {
255  value->set_color(color);
256  }
257  }
258 
259  ctx.menu_fade_animation->stop();
260  ctx.menu_fade_animation->play();
261 }
262 
263 void fade_out(::game& ctx, const std::function<void()>& end_callback)
264 {
265  ctx.menu_fade_animation->set_interpolator(ease<float>::out_cubic);
266  animation_channel<float>* opacity_channel = ctx.menu_fade_animation->get_channel(0);
267  opacity_channel->remove_keyframes();
268  opacity_channel->insert_keyframe({0.0f, 1.0f});
269  opacity_channel->insert_keyframe({config::menu_fade_out_duration, 0.0f});
270  ctx.menu_fade_animation->set_end_callback(end_callback);
271 
272  ctx.menu_fade_animation->stop();
273  ctx.menu_fade_animation->play();
274 }
275 
276 void fade_in_bg(::game& ctx)
277 {
278  ctx.menu_bg_fade_out_animation->stop();
279  ctx.menu_bg_fade_in_animation->stop();
280  ctx.menu_bg_fade_in_animation->play();
281 }
282 
283 void fade_out_bg(::game& ctx)
284 {
285  ctx.menu_bg_fade_in_animation->stop();
286  ctx.menu_bg_fade_out_animation->stop();
287  ctx.menu_bg_fade_out_animation->play();
288 }
289 
290 } // namespace menu
Single channel in a keyframe animation.
void remove_keyframes(float start, float end)
Removes all keyframes on [start, end).
void insert_keyframe(const keyframe &k)
Adds a keyframe to the animation.
Definition: game.hpp:121
std::unique_ptr< scene::collection > ui_scene
Definition: game.hpp:333
std::unique_ptr< animation< float > > menu_bg_fade_in_animation
Definition: game.hpp:338
std::vector< std::function< void()> > menu_left_callbacks
Definition: game.hpp:348
std::vector< std::function< void()> > menu_right_callbacks
Definition: game.hpp:349
std::unique_ptr< animator > animator
Definition: game.hpp:368
std::vector< std::function< void()> > menu_select_callbacks
Definition: game.hpp:347
type::bitmap_font menu_font
Definition: game.hpp:188
std::vector< std::tuple< scene::text *, scene::text * > > menu_item_texts
Definition: game.hpp:351
std::unique_ptr< animation< float > > menu_bg_fade_out_animation
Definition: game.hpp:339
std::shared_ptr< app::window > window
Definition: game.hpp:166
std::unordered_map< hash::fnv1a32_t, int > menu_item_indices
Definition: game.hpp:352
std::shared_ptr< render::material > menu_font_material
Definition: game.hpp:191
std::unique_ptr< animation< float > > menu_fade_animation
Definition: game.hpp:337
std::function< void()> menu_back_callback
Definition: game.hpp:350
int * menu_item_index
Definition: game.hpp:353
virtual const glyph_metrics & get_glyph_metrics(char32_t code) const
Returns metrics describing a glyph.
const font_metrics & get_font_metrics() const
Returns metrics describing the font.
Definition: font.hpp:109
Color science.
Definition: aces.hpp:27
constexpr vector< T, N > round(const vector< T, N > &x)
Performs a element-wise round operation.
Definition: vector.hpp:1489
Definition: menu.cpp:29
void fade_in_bg(::game &ctx)
Definition: menu.cpp:276
void init_menu_item_index(::game &ctx, hash::fnv1a32_t menu_name)
Definition: menu.cpp:31
void update_text_color(::game &ctx)
Definition: menu.cpp:59
void clear_callbacks(::game &ctx)
Definition: menu.cpp:197
void delete_animations(::game &ctx)
Definition: menu.cpp:191
void align_text(::game &ctx, bool center, bool has_back, float anchor_y)
Definition: menu.cpp:73
void fade_out_bg(::game &ctx)
Definition: menu.cpp:283
void setup_animations(::game &ctx)
Definition: menu.cpp:206
void refresh_text(::game &ctx)
Definition: menu.cpp:156
void add_text_to_ui(::game &ctx)
Definition: menu.cpp:166
void fade_out(::game &ctx, const std::function< void()> &end_callback)
Definition: menu.cpp:263
void update_text_font(::game &ctx)
Definition: menu.cpp:44
void fade_in(::game &ctx, const std::function< void()> &end_callback)
Definition: menu.cpp:233
void remove_text_from_ui(::game &ctx)
Definition: menu.cpp:176
void delete_text(::game &ctx)
Definition: menu.cpp:186
Container for templated easing functions.
Definition: ease.hpp:71
32-bit FNV-1a hash value.
Definition: fnv1a.hpp:117
n-dimensional vector.
Definition: vector.hpp:44
constexpr element_type & x() noexcept
Returns a reference to the first element.
Definition: vector.hpp:164
constexpr element_type & y() noexcept
Returns a reference to the second element.
Definition: vector.hpp:180
float linespace
Baseline-to-baseline distance, computed as ascent - descent + linegap.
float linegap
Distance that must be placed between two lines of text.
float size
Vertical size of the font.
float width
Horizontal extent of the glyph.