diff --git a/CMakeLists.txt b/CMakeLists.txt index d7e18be..26cbf0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,6 +182,7 @@ set (SOURCES ${CMAKE_SOURCE_DIR}/src/ui/ui_utils.cpp ${CMAKE_SOURCE_DIR}/src/ui/util/hsv.cpp ${CMAKE_SOURCE_DIR}/src/ui/core/ui_context.cpp + ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_binding_button.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_button.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_icon_button.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_clickable.cpp diff --git a/src/ui/elements/ui_binding_button.cpp b/src/ui/elements/ui_binding_button.cpp new file mode 100644 index 0000000..a81fc44 --- /dev/null +++ b/src/ui/elements/ui_binding_button.cpp @@ -0,0 +1,179 @@ +#include "ui_binding_button.h" +#include "ui_theme.h" +#include + +namespace recompui { + static const float padding = 8.0f; + static const float height = 56.0f - (theme::border::width * 2.0f); + + BindingButton::BindingButton(Element *parent, const std::string &mapped_binding) : Element(parent, Events(EventType::Click, EventType::Hover, EventType::Enable, EventType::Focus), "button") { + this->mapped_binding = mapped_binding; + + enable_focus(); + + set_display(Display::Flex); + set_position(Position::Relative); + + set_flex_grow(1.0f); + set_flex_shrink(1.0f); + set_flex_basis(100.0f, recompui::Unit::Percent); + + set_align_items(AlignItems::Center); + set_justify_content(JustifyContent::Center); + + set_width(100.0f, recompui::Unit::Percent); + set_height(height); + set_padding(padding); + + set_border_width(theme::border::width); + set_border_radius(theme::border::radius_sm); + set_border_color(theme::color::WhiteA5); + set_background_color(theme::color::WhiteA5); + set_color(theme::color::TextDim); + + set_cursor(Cursor::Pointer); + + focus_style.set_border_color(theme::color::White); + focus_style.set_background_color(theme::color::WhiteA30); + focus_style.set_color(theme::color::TextActive); + hover_style.set_border_color(theme::color::WhiteA80); + hover_style.set_background_color(theme::color::WhiteA20); + hover_style.set_color(theme::color::Text); + + disabled_style.set_color(theme::color::TextDim); + disabled_style.set_opacity(0.5f); + disabled_style.set_cursor(Cursor::None); + + add_style(&hover_style, hover_state); + add_style(&focus_style, focus_state); + add_style(&disabled_style, disabled_state); + add_style(&hover_disabled_style, { hover_state, disabled_state }); + + ContextId context = get_current_context(); + + bound_text_el = context.create_element(this, 0, "div", true); + bound_text_el->set_text(mapped_binding); + apply_binding_style(); + + recording_parent = context.create_element(this); + recording_circle = context.create_element(recording_parent); + recording_edge = context.create_element(recording_parent); + recording_svg = context.create_element(recording_edge, "icons/RecordBorder.svg"); + apply_recording_style(); + } + + void BindingButton::apply_recording_style() { + recording_parent->set_display(Display::Flex); + recording_parent->set_position(Position::Absolute); + + recording_parent->set_top(0.0f); + recording_parent->set_left(0.0f); + recording_parent->set_right(0.0f); + recording_parent->set_bottom(0.0f); + + recording_parent->set_align_items(AlignItems::Center); + recording_parent->set_justify_content(JustifyContent::Center); + recording_parent->set_opacity(0); + + const float circle_size = 24; + recording_circle->set_width(circle_size); + recording_circle->set_height(circle_size); + recording_circle->set_border_radius(circle_size * 0.5f); + recording_circle->set_background_color(theme::color::Danger); + + const float edge_size = 36; + recording_edge->set_position(Position::Absolute); + recording_edge->set_top(50.0f, recompui::Unit::Percent); + recording_edge->set_left(50.0f, recompui::Unit::Percent); + recording_edge->set_width(edge_size); + recording_edge->set_height(edge_size); + recording_edge->set_translate_2D(-50.0f, -50.0f, recompui::Unit::Percent); + + recording_svg->set_width(edge_size); + recording_svg->set_height(edge_size); + recording_svg->set_image_color(theme::color::Danger); + } + + void BindingButton::apply_binding_style() { + bound_text_el->set_font_family("promptfont"); + bound_text_el->set_font_size(40.0f); + bound_text_el->set_font_style(FontStyle::Normal); + bound_text_el->set_font_weight(400); + bound_text_el->set_line_height(40.0f); + bound_text_el->set_opacity(1); + } + + void BindingButton::add_pressed_callback(std::function callback) { + pressed_callbacks.push_back(std::move(callback)); + } + + void BindingButton::set_binding(const std::string &binding) { + this->mapped_binding = binding; + bound_text_el->set_text(mapped_binding); + } + + void BindingButton::set_is_binding(bool is_binding) { + this->is_binding = is_binding; + + if (is_binding) { + bound_text_el->set_opacity(0); + recording_parent->set_opacity(1); + queue_update(); + } else { + bound_text_el->set_opacity(1); + recording_parent->set_opacity(0); + } + } + + void BindingButton::process_event(const Event &e) { + switch (e.type) { + case EventType::Click: + if (is_enabled()) { + for (const auto &function : pressed_callbacks) { + function(); + } + set_is_binding(!is_binding); + } + break; + case EventType::Hover: + set_style_enabled(hover_state, std::get(e.variant).active && is_enabled()); + break; + case EventType::Enable: + { + bool enable_active = std::get(e.variant).active; + set_style_enabled(disabled_state, !enable_active); + if (enable_active) { + set_cursor(Cursor::Pointer); + set_focusable(true); + } + else { + set_cursor(Cursor::None); + set_focusable(false); + } + } + break; + case EventType::Focus: + set_style_enabled(focus_state, std::get(e.variant).active); + break; + case EventType::Update: + { + if (is_binding == false) { + break; + } + queue_update(); + std::chrono::high_resolution_clock::duration since_start = ultramodern::time_since_start(); + auto millis = std::chrono::duration_cast(since_start).count(); + const float loop_length_seconds = 1.5f; + float t = static_cast(millis) / (loop_length_seconds * 1000.0f); + float sine_time = sinf(t * 2.0f * 3.14159f); + float scale = 1.0f + ((sine_time * 0.15f / 2.0f) - 0.15f); + recording_circle->set_scale_2D(scale, scale); + } + break; + default: + assert(false && "Unknown event type."); + break; + } + } + +} // namespace recompui diff --git a/src/ui/elements/ui_binding_button.h b/src/ui/elements/ui_binding_button.h new file mode 100644 index 0000000..9178e59 --- /dev/null +++ b/src/ui/elements/ui_binding_button.h @@ -0,0 +1,42 @@ +#pragma once + +#include "ui_element.h" +#include "ui_svg.h" + +namespace recompui { + class BindingButton : public Element { + protected: + bool is_binding = false; + std::string mapped_binding; // promptfont representation of the binding. + + Element *bound_text_el; + Element *recording_parent; + Element *recording_circle; + Element *recording_edge; + Svg *recording_svg; + + Style binding_style; + Style hover_style; + Style focus_style; + Style disabled_style; + Style hover_disabled_style; + std::list> pressed_callbacks; + + // Element overrides. + virtual void process_event(const Event &e) override; + std::string_view get_type_name() override { return "BindingButton"; } + public: + BindingButton(Element *parent, const std::string &mapped_binding); + void add_pressed_callback(std::function callback); + void set_binding(const std::string &binding); + void set_is_binding(bool is_binding); + Style* get_hover_style() { return &hover_style; } + Style* get_focus_style() { return &focus_style; } + Style* get_disabled_style() { return &disabled_style; } + Style* get_hover_disabled_style() { return &hover_disabled_style; } + private: + void apply_recording_style(); + void apply_binding_style(); + }; + +} // namespace recompui