infinipaint/src/GUIStuff/GUIManager.hpp
2026-05-07 21:48:47 +03:00

173 lines
6.9 KiB
C++

#pragma once
#include <include/core/SkCanvas.h>
#include <Eigen/Dense>
#include "GUIManagerID.hpp"
#include <clay.h>
#include "Elements/Element.hpp"
#include "GUIFloatAnimation.hpp"
using namespace Eigen;
namespace GUIStuff {
class GUIManager {
public:
struct ElementContainer {
std::unique_ptr<Element> elem;
bool isUsedThisFrame;
};
GUIManager();
void draw(SkCanvas* c, bool skiaAA);
void draw_force(SkCanvas* canvas, bool skiaAA);
void set_to_layout();
void layout_if_necessary();
GUIFloatAnimation* float_animation(const char* animationID, const GUIFloatAnimationData& animation);
void update();
void update_window(const Vector2f& windowRect, const SCollision::AABB<float>& safeWindowRect, float guiScaleMultiplier);
UpdateInputData io;
void in_dynamic_area(const std::function<void()>& f);
bool is_dynamic_area();
void new_id(const char* id, const std::function<void()>& f);
void new_id(int64_t id, const std::function<void()>& f);
void set_z_index(int16_t z, const std::function<void()>& f);
void set_z_index_keep_clipping_region(int16_t z, const std::function<void()>& f);
int16_t get_z_index();
void set_post_callback_func(const std::function<void()>& f);
void set_post_callback_func_high_priority(const std::function<void()>& f);
void run_post_callback_func();
void invalidate_draw_element(Element* element, const BorderData& extraPadding = {});
void invalidate_draw_in_area(const SCollision::AABB<float>& bb);
bool last_interaction_is_touch();
template <typename ElementType, typename... Args> ElementType* element(const char* id, const Args&... a) {
push_id(id);
ElementType* elem = insert_element<ElementType>();
Element* oldParent = parentElement;
elem->parent = parentElement;
parentElement = elem;
elem->set_parent_clipping_region(clippingRegion);
Clay_ElementId clayId = strArena.elem_id_from_id_stack(idStack);
elem->layout(clayId, a...);
elem->set_bounding_box_from_elem_data(Clay_GetElementData(clayId)); // Setting bounding box after layout ensures that the element will have its bounding box set if it's set to be drawn.
parentElement = oldParent;
pop_id();
return elem;
}
template <typename ElementType, typename... Args> ElementType* clipping_element(const char* id, const Args&... a) {
push_id(id);
ElementType* elem = insert_element<ElementType>();
Element* oldParent = parentElement;
elem->parent = parentElement;
parentElement = elem;
elem->set_parent_clipping_region(clippingRegion);
auto oldClippingRegion = clippingRegion;
if(elem->get_bb().has_value()) {
if(clippingRegion.has_value())
clippingRegion = clippingRegion.value().get_intersection_between_aabbs(elem->get_bb().value());
else
clippingRegion = elem->get_bb();
}
Clay_ElementId clayId = strArena.elem_id_from_id_stack(idStack);
elem->layout(clayId, a...);
elem->set_bounding_box_from_elem_data(Clay_GetElementData(clayId));
clippingRegion = oldClippingRegion;
parentElement = oldParent;
pop_id();
return elem;
}
void deselect_all();
DefaultStringArena strArena;
void input_paste_callback(const CustomEvents::PasteEvent& paste);
void input_text_key_callback(const InputManager::KeyCallbackArgs& key);
void input_text_callback(const InputManager::TextCallbackArgs& text);
void input_key_callback(const InputManager::KeyCallbackArgs& key);
void input_mouse_button_callback(InputManager::MouseButtonCallbackArgs button);
void input_mouse_motion_callback(InputManager::MouseMotionCallbackArgs motion);
void input_mouse_wheel_callback(InputManager::MouseWheelCallbackArgs wheel);
void input_finger_touch_callback(InputManager::FingerTouchCallbackArgs touch);
void input_finger_motion_callback(InputManager::FingerMotionCallbackArgs motion);
std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
bool cursor_obstructed() const;
private:
std::unordered_map<GUIManagerIDStack, GUIFloatAnimation> animations;
void layout();
void layout_begin();
void layout_end();
void single_layout_run();
void mouse_callback(const Vector2f& mousePos, const std::function<void(ElementContainer*)>& f);
GUIManagerIDStack idStack;
std::vector<ElementContainer*> orderedElements;
std::unordered_map<GUIManagerIDStack, ElementContainer> elements;
template <typename NewElement> NewElement* insert_element() {
auto [it, inserted] = elements.emplace(idStack, ElementContainer());
auto& container = it->second;
if(inserted)
container = {std::make_unique<NewElement>(*this)};
container.elem->zIndex = zIndex;
orderedElements.emplace_back(&container);
container.isUsedThisFrame = true;
return static_cast<NewElement*>(it->second.elem.get());
}
static void clay_error_handler(Clay_ErrorData errorData);
static Clay_Dimensions clay_skia_measure_text(Clay_StringSlice str, Clay_TextElementConfig* config, void* userData);
void push_id(int64_t id);
void push_id(const char* id);
void pop_id();
int16_t zIndex = 0;
Element* parentElement = nullptr;
bool dynamicArea = false;
std::optional<SCollision::AABB<float>> clippingRegion;
std::function<void()> postCallbackFunc;
bool postCallbackFuncIsHighPriority;
Clay_Context* clayInstance;
Clay_Arena clayArena;
Clay_RenderCommandArray renderCommands;
std::unordered_map<uint32_t, Clay_RenderCommand> oldRenderCommandMap;
std::optional<SCollision::AABB<float>> invalidDrawBB;
SCollision::AABB<float> get_invalid_draw_bb(SCollision::AABB<float> bb, const BorderData& extraPadding = {});
SCollision::AABB<float> get_invalid_draw_bb_from_command(const Clay_RenderCommand* command);
void update_invalidated_draw_area_from_layout();
void calculate_new_clip_rect(std::vector<SCollision::AABB<float>>& clipRectStack, std::optional<SCollision::AABB<float>>& clipRect, bool& clipNoDraw);
void clip_rect_transform(SkCanvas* canvas, std::vector<SCollision::AABB<float>>& clipRectStack, std::optional<SCollision::AABB<float>>& clipRect, bool& clipNoDraw);
bool lastInteractionIsTouch;
bool setToLayout;
bool setToUpdateInvalidateDrawAreaFromLayout;
bool cursorObstructed;
};
}