30 #ifdef E3D_TARGET_WINDOWS
33 #ifdef E3D_BACKEND_OPENGL
36 #elif defined E3D_BACKEND_GLES
37 # include <GLES3/gl3.h>
39 #include <glm/vec2.hpp>
41 #include <tbb/blocked_range.h>
42 #include <tbb/concurrent_vector.h>
43 #include <tbb/parallel_for.h>
44 #include <tbb/combinable.h>
46 #include "eng3d/ui/ui.hpp"
47 #include "eng3d/ui/label.hpp"
48 #include "eng3d/ui/window.hpp"
49 #include "eng3d/ui/text.hpp"
50 #include "eng3d/ui/button.hpp"
51 #include "eng3d/ui/tooltip.hpp"
52 #include "eng3d/ui/widget.hpp"
53 #include "eng3d/ui/slider.hpp"
54 #include "eng3d/ui/input.hpp"
55 #include "eng3d/ui/scrollbar.hpp"
56 #include "eng3d/texture.hpp"
57 #include "eng3d/rectangle.hpp"
58 #include "eng3d/state.hpp"
59 #include "eng3d/utils.hpp"
60 #include "eng3d/primitive.hpp"
61 #include "eng3d/log.hpp"
70 CXX_THROW(std::runtime_error,
"UI context already constructed");
75 CXX_THROW(std::runtime_error,
"Can't open font");
76 widgets.reserve(8192);
89 obj_shader = std::make_unique<Eng3D::OpenGL::Program>();
91 obj_shader->attach_shader(*s.builtin_shaders[
"vs_2d"]);
92 obj_shader->attach_shader(*s.builtin_shaders[
"fs_2d"]);
110 if(std::find_if(widgets.cbegin(), widgets.cend(), [widget](
const auto& e) { return e.get() == widget; }) != widgets.cend())
112 widgets.push_back(std::unique_ptr<UI::Widget>(widget));
116 auto it = std::find_if(widgets.begin(), widgets.end(), [widget](
const auto& e) { return e.get() == widget; });
123 for(
auto& widget : widgets) {
124 if(widget.get() == this->tooltip_widget || !widget->managed)
continue;
127 this->use_tooltip(
nullptr, { 0, 0 });
130 void UI::Context::clear_dead_recursive(
UI::Widget& w) {
131 bool changed =
false;
132 for(
size_t i = 0; i < w.
children.size(); i++) {
137 }
else if(w.
children[i]->dead_child) {
138 this->clear_dead_recursive(*w.
children[i].get());
142 if(changed) w.need_recalc =
true;
147 for(
size_t i = 0; i < widgets.size(); i++) {
148 if(widgets[i]->dead) {
149 widgets.erase(widgets.begin() + i);
151 }
else if(widgets[i]->dead_child) {
152 this->clear_dead_recursive(*widgets[i].get());
153 widgets[i]->dead_child =
false;
157 if(this->tooltip_widget)
158 this->clear_dead_recursive(*this->tooltip_widget);
166 if(eval == widget.is_eval)
return;
169 auto it = std::find_if(this->no_eval_widgets.begin(), this->no_eval_widgets.end(), [&widget](
const auto& e) { return e.get() == &widget; });
170 assert(it != this->no_eval_widgets.end());
171 this->widgets.push_back(std::move(*it));
172 this->no_eval_widgets.erase(it);
175 auto it = std::find_if(this->widgets.begin(), this->widgets.end(), [&widget](
const auto& e) { return e.get() == &widget; });
176 assert(it != this->widgets.end());
177 this->no_eval_widgets.push_back(std::move(*it));
178 this->widgets.erase(it);
180 widget.is_eval = eval;
184 std::scoped_lock lock(prompt_queue_mutex);
185 this->prompt_queue.emplace_back(title, text);
188 glm::ivec2 UI::Context::get_pos(
Widget& w, glm::ivec2 offset) {
189 glm::ivec2 pos{ w.
x, w.
y };
190 glm::ivec2 screen_size{ width, height };
191 glm::ivec2 parent_size{ 0, 0 };
198 pos += parent_size / 2;
202 pos.y += parent_size.y / 2;
206 pos.y += parent_size.y / 2;
207 pos.x += parent_size.x;
214 pos.x += parent_size.x / 2;
218 pos.x += parent_size.x;
222 pos.y += parent_size.y;
226 pos.y += parent_size.y;
227 pos.x += parent_size.x / 2;
234 pos += screen_size / 2;
237 pos.y += screen_size.y / 2;
240 pos.y += screen_size.y / 2;
241 pos.x += screen_size.x;
246 pos.x += screen_size.x / 2;
249 pos.x += screen_size.x;
252 pos.y += screen_size.y;
255 pos.y += screen_size.y;
256 pos.x += screen_size.x / 2;
266 this->width = _width;
267 this->height = _height;
268 glViewport(0, 0, this->width, this->height);
270 this->projection = glm::ortho(0.f,
static_cast<float>(this->width),
static_cast<float>(this->height), 0.f, 0.f, 1.f);
271 this->view = glm::mat4(1.f);
272 this->model =
glm::translate(glm::mat4(1.f), glm::vec3(0.f, 0.f, 0.f));
276 this->cursor_pos = pos;
279 void UI::Context::render_recursive(
Widget& w,
Eng3D::Rect viewport, glm::ivec2 offset) {
287 w.recalc_child_pos();
288 w.need_recalc =
false;
298 offset = this->get_pos(w, offset);
304 viewport = local_viewport;
306 local_viewport.
offset(-offset);
307 if(local_viewport.width() > 0 && local_viewport.height() > 0) {
308 obj_shader->set_uniform(
"model",
glm::translate(model, glm::vec3(offset, 0.f)));
309 piechart_shader->set_uniform(
"model",
glm::translate(model, glm::vec3(offset, 0.f)));
314 if((viewport.
size().x <= 0 || viewport.
size().y <= 0) && !child->is_float)
317 this->render_recursive(*child, viewport, offset);
324 this->model =
glm::translate(glm::mat4(1.f), glm::vec3(0.f, 0.f, 0.f));
326 glActiveTexture(GL_TEXTURE0);
328 this->obj_shader->use();
329 this->obj_shader->set_uniform(
"projection", this->projection);
330 this->obj_shader->set_uniform(
"view", this->view);
331 this->obj_shader->set_uniform(
"model", this->model);
334 for(
auto& widget : this->widgets)
335 this->render_recursive(*widget.get(), viewport, glm::vec2(0.f));
336 if(tooltip_widget !=
nullptr)
337 this->render_recursive(*tooltip_widget, viewport, glm::vec2(0.f));
340 obj_shader->set_uniform(
"diffuse_color", glm::vec4(1.f));
341 obj_shader->set_texture(0,
"diffuse_map", *cursor_tex);
342 obj_shader->set_uniform(
"model",
glm::translate(glm::mat4(1.f), glm::vec3(cursor_pos, 0.f)));
348 void UI::Context::clear_hover_recursive(
Widget& w) {
351 clear_hover_recursive(*child);
354 static inline bool is_inside_transparent(
const UI::Widget& w, glm::ivec2 mouse_pos, glm::ivec2 offset) {
357 glm::ivec2 tex_pos = ((mouse_pos - offset) * tex_size) / glm::ivec2(w.
width, w.
height);
358 const Eng3D::Rect tex_rect{ glm::ivec2(0), tex_size };
359 if(tex_rect.contains(mouse_pos)) {
360 const uint32_t argb = w.
current_texture->get_pixel(tex_pos.x, tex_pos.y).get_value();
361 if(((argb >> 24) & 0xff) == 0)
return true;
367 bool UI::Context::check_hover_recursive(
UI::Widget& w, glm::ivec2 mouse_pos, glm::ivec2 offset) {
368 offset = this->get_pos(w, offset);
375 if(!r.contains(mouse_pos)) {
378 if(is_inside_transparent(w, mouse_pos, offset))
390 this->tooltip_widget = w.
tooltip;
395 consumed_hover |= check_hover_recursive(*child, mouse_pos, offset);
397 return consumed_hover;
416 bool is_hover =
false;
417 tooltip_widget =
nullptr;
418 for(
const auto& widget :
reverse(widgets)) {
419 is_hover |= check_hover_recursive(*widget, mouse_pos, glm::ivec2(0));
420 if(is_hover)
return is_hover;
427 glm::ivec2 mouse_pos,
433 offset = this->get_pos(w, offset);
446 if(!r.contains(mouse_pos)) {
449 if(is_inside_transparent(w, mouse_pos, offset))
455 auto new_click_state = check_click_recursive(*child, mouse_pos, offset, click_state, clickable, mouse_pressed);
456 if (click_state < new_click_state)
457 click_state = new_click_state;
474 wc->
set_value((
static_cast<float>(std::abs(mouse_pos.x - offset.x)) /
static_cast<float>(wc->width)) * wc->max);
477 mouse_pressed_widget = &w;
481 on_drag(mouse_pos, mouse_pos);
487 if (mouse_pressed_widget == &w)
499 this->start_drag_mouse_position = mouse_pos;
500 #ifdef E3D_TARGET_WINDOWS
501 SetCapture(GetActiveWindow());
505 int click_wind_index = -1;
507 bool is_click =
false;
508 for(
int i = widgets.size() - 1; i >= 0; i--) {
509 click_state = check_click_recursive(*widgets[i].get(), mouse_pos, glm::ivec2(0), click_state,
true,
true);
514 click_wind_index = i;
519 if(click_wind_index != -1) {
521 auto& window = *widgets[click_wind_index].get();
523 auto it = widgets.begin() + click_wind_index;
524 std::rotate(it, it + 1, widgets.end());
534 #ifdef E3D_TARGET_WINDOWS
541 bool is_click =
false;
542 for(
auto& widget :
reverse(widgets)) {
543 click_state = check_click_recursive(*widget, mouse_pos, glm::ivec2(0), click_state,
true,
false);
550 mouse_pressed_widget =
nullptr;
557 on_drag(start_drag_mouse_position, mouse_pos);
562 auto& c_widget =
static_cast<UI::Input&
>(widget);
563 if(c_widget.is_selected) c_widget.
on_textinput(c_widget, _input);
567 for(
const auto& children : widget.
children)
574 for(
const auto& widget : widgets)
581 this->tooltip_widget = tooltip;
582 if(this->tooltip_widget !=
nullptr)
583 this->tooltip_widget->
set_pos(pos.x, pos.y, tooltip->
width, tooltip->
height, width, height);
586 bool UI::Context::check_wheel_recursive(
UI::Widget& w, glm::ivec2 mouse_pos, glm::ivec2 offset,
int y) {
587 offset = get_pos(w, offset);
596 if(is_inside_transparent(w, mouse_pos, offset))
608 bool scrolled =
false;
612 scrolled = check_wheel_recursive(*children, mouse_pos, offset, y);
618 if(scrollbar !=
nullptr)
626 for(
auto& widget :
reverse(widgets))
627 if(check_wheel_recursive(*widget, mouse_pos, glm::ivec2(0), y))
636 for(
auto& widget :
reverse(widgets))
637 do_tick_recursive(*widget);
640 int UI::Context::do_tick_recursive(
Widget& w) {
643 do_tick_recursive(*child);
std::shared_ptr< Eng3D::IO::Asset::Base > get_unique(const Eng3D::IO::Path &path)
Obtaining an unique asset means the "first-found" policy applies.
Eng3D::TrueType::Manager ttf_man
Eng3D::TextureManager tex_man
Eng3D::IO::PackageManager package_man
std::shared_ptr< Eng3D::Texture > load(const std::string &path, TextureOptions options=default_options)
Finds a texture in the list of a texture manager if the texture is already in the list we load the sa...
std::shared_ptr< Eng3D::TrueType::Font > load(std::shared_ptr< Eng3D::IO::Asset::Base > asset)
The UI context that handles all the ui widgets.
std::shared_ptr< Eng3D::Texture > cursor_tex
std::shared_ptr< Eng3D::Texture > button_border
void set_eval(UI::Widget &widget, bool eval)
Moves a widget from evaluable to non-evaluable making a widget non-evaluable has side effects,...
std::shared_ptr< Eng3D::Texture > foreground
std::shared_ptr< Eng3D::TrueType::Font > default_font
std::shared_ptr< Eng3D::Texture > tooltip_tex
void render_all()
Render all widgets.
void use_tooltip(Tooltip *tooltip, glm::ivec2 pos)
void do_tick()
Will call on_tick on all widgets.
std::shared_ptr< Eng3D::Texture > button
bool check_mouse_released(glm::ivec2 mouse_pos)
Release the dragging of the widget.
std::unique_ptr< Eng3D::OpenGL::Program > obj_shader
std::shared_ptr< Eng3D::Texture > background
void remove_widget(UI::Widget *widget)
void prompt(const std::string &title, const std::string &text)
std::shared_ptr< Eng3D::Texture > piechart_overlay
void resize(const int width, const int height)
bool check_text_input(const char *input)
Will give keyboard input to Input Widget if one is selected.
void check_drag(glm::ivec2 mouse_pos)
Check for on_drag events, will move Window widgets with is_pinned = false.
bool check_wheel(glm::ivec2 mouse_pos, int y)
Check if the mouse is above a widget and scroll widget.
std::unique_ptr< Eng3D::OpenGL::Program > piechart_shader
std::shared_ptr< Eng3D::Texture > window_top
void add_widget(UI::Widget *widget)
bool check_hover(glm::ivec2 mouse_pos)
Check for on_hover events If the mouse is above a widget call the widgets on_hover or show its toolti...
bool check_click(glm::ivec2 mouse_pos)
Check for on_click events. Check if the mouse is above a widget and call the widgets on_click if poss...
void clear()
Removes all widgets.
void clear_dead()
Removes all widgets that have been killed.
std::shared_ptr< Eng3D::Texture > border_tex
void set_cursor_pos(glm::ivec2 pos)
void set_value(const float _value)
std::string translate(const std::string_view str)
constexpr glm::vec2 size() const
Obtains the current size of the rectangle.
constexpr Rectangle intersection(const Rectangle &rect) const
Obtains the intersection rectangle from two other rectangles R1 and R2.
constexpr void offset(glm::vec2 offset)
Offset the rectangle by the given parameter.
constexpr bool contains(glm::vec2 pos) const
Checks if the point is contains the point.
bool check_text_input_recursive(UI::Widget &widget, const char *_input)
Range< It > reverse(ORange &&originalRange)
#define CXX_THROW(class,...)