diff --git a/CMakeLists.txt b/CMakeLists.txt index 37e088f..58c5387 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,8 @@ set (SOURCES ${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp ${CMAKE_SOURCE_DIR}/src/ui/ui_rml_hacks.cpp ${CMAKE_SOURCE_DIR}/src/ui/ui_elements.cpp + ${CMAKE_SOURCE_DIR}/src/ui/ui_mod_details_panel.cpp + ${CMAKE_SOURCE_DIR}/src/ui/ui_mod_menu.cpp ${CMAKE_SOURCE_DIR}/src/ui/util/hsv.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ElementConfigGroup.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ElementConfigOption.cpp @@ -171,14 +173,13 @@ set (SOURCES ${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeRadioTabs.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeRange.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeTextField.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ElementModMenu.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/presets.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_button.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_container.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_element.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_image.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_label.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_mod_details_panel.cpp + ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_scroll_container.cpp ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp ${CMAKE_SOURCE_DIR}/rsp/aspMain.cpp diff --git a/src/ui/elements/ElementModMenu.cpp b/src/ui/elements/ElementModMenu.cpp deleted file mode 100644 index 217f6a4..0000000 --- a/src/ui/elements/ElementModMenu.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "ElementModMenu.h" -#include "presets.h" -#include "librecomp/mods.hpp" - -#include - -#define MOD_MENU_BEM "mod-menu" - -namespace recompui { - -static const std::string cls_base = BLOCK(MOD_MENU_BEM); -static const std::string cls_list = EL(MOD_MENU_BEM, "list"); -static const std::string cls_list_scroll = EL(MOD_MENU_BEM, "list-scroll"); -static const std::string cls_list_entry = EL(MOD_MENU_BEM, "list-entry"); -static const std::string cls_list_entry_thumbnail = EL(MOD_MENU_BEM, "list-entry-thumbnail"); -static const std::string cls_list_entry_body = EL(MOD_MENU_BEM, "list-entry-body"); -static const std::string cls_list_entry_name = EL(MOD_MENU_BEM, "list-entry-name"); -static const std::string cls_list_entry_description = EL(MOD_MENU_BEM, "list-entry-description"); - -void ElementModMenu::ProcessEvent(Rml::Event& event) { - Rml::Element* event_element = event.GetCurrentElement(); - Rml::EventId event_id = event.GetId(); - switch (event_id) { - // Click event handlers. - case Rml::EventId::Click: - // Refresh - if (event_element == refresh_button) { - RefreshMods(); - } - break; - case Rml::EventId::Focus: - { - size_t mod_index; - Rml::Variant *val = event_element->GetAttribute("mod_index"); - if (val->GetInto(mod_index) && mod_index < mod_details.size()) { - mod_details_panel->set_mod_details(mod_details[mod_index]); - } - if (active_list_entry_el != nullptr) { - active_list_entry_el->RemoveAttribute("is_selected"); - } - event_element->SetAttribute("is_selected", true); - active_list_entry_el = event_element; - } - default: - break; - } -} - -Rml::ElementPtr ElementModMenu::CreateModListEntry(const recomp::mods::ModDetails& details, size_t index) { - Rml::ElementDocument *doc = GetOwnerDocument(); - - Rml::ElementPtr mod_el = doc->CreateElement("div"); - mod_el->SetClass(cls_list_entry, true); - mod_el->SetAttribute("mod_index", index); - { - Rml::Element* thumbnail_el = add_div_with_class(doc, mod_el.get(), cls_list_entry_thumbnail); - Rml::Element *body_el = add_div_with_class(doc, mod_el.get(), cls_list_entry_body); - { - Rml::Element *name_el = add_div_with_class(doc, body_el, cls_list_entry_name); - name_el->SetInnerRML(details.mod_id); - - Rml::Element *description_el = add_div_with_class(doc, body_el, cls_list_entry_description); - description_el->SetInnerRML("Short description of mod here."); - } // body_el - } // mod_el - - return mod_el; -} - -void ElementModMenu::CreateModList() { - Rml::ElementDocument *doc = GetOwnerDocument(); - - // Clear the contents of the list scroll. - list_el_scroll->SetInnerRML(""); - Rml::Element* prev_el = refresh_button; - active_list_entry_el = nullptr; - - bool first = true; - - // Create the child elements for the list scroll. - for (size_t mod_index = 0; mod_index < mod_details.size(); mod_index++) { - const recomp::mods::ModDetails& details = mod_details[mod_index]; - Rml::Element *mod_el = list_el_scroll->AppendChild(CreateModListEntry(details, mod_index)); - mod_el->SetAttribute("mod_index", mod_index); - mod_el->AddEventListener(Rml::EventId::Focus, this, false); - mod_el->SetId("mod-list-entry-" + std::to_string(mod_index)); - - mod_el->SetProperty("nav-up", "#" + prev_el->GetId()); - prev_el->SetProperty("nav-down", "#" + mod_el->GetId()); - - if (first) { - active_list_entry_el = mod_el; - } - first = false; - - prev_el = mod_el; - } - - active_list_entry_el->SetAttribute("is_selected", true); - - DirtyLayout(); -} - -void ElementModMenu::RefreshMods() { - recomp::mods::scan_mods(); - mod_details = recomp::mods::get_mod_details(game_mod_id); - - mod_details_panel->set_mod_details(mod_details[0]); - - CreateModList(); -} - -ElementModMenu::ElementModMenu(const Rml::String& tag) : Rml::Element(tag) { - game_mod_id = "mm"; - SetAttribute("recomp-store-element", true); - Rml::ElementDocument *doc = GetOwnerDocument(); - SetClass(cls_base, true); - - Rml::Element *body_el = add_div_with_class(doc, this, "config__hz-wrapper"); - { - list_el = add_div_with_class(doc, body_el, cls_list); - { - list_el_scroll = add_div_with_class(doc, list_el, cls_list_scroll); - } // list_el - - recompui::Element body_el_compat(body_el); - mod_details_panel = std::make_unique(&body_el_compat); - } // body_el - - Rml::Element *footer_el = add_div_with_class(doc, this, "config__footer"); - { - refresh_button = add_button(doc, footer_el, "Refresh", ButtonVariant::Primary); - refresh_button->AddEventListener(Rml::EventId::Click, this, false); - refresh_button->SetId("refresh-button"); - } // footer_el - - RefreshMods(); -} - -ElementModMenu::~ElementModMenu() { -} - - -} // namespace Rml diff --git a/src/ui/elements/ElementModMenu.h b/src/ui/elements/ElementModMenu.h deleted file mode 100644 index fa3b8fb..0000000 --- a/src/ui/elements/ElementModMenu.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef RECOMPUI_ELEMENT_MOD_MENU_H -#define RECOMPUI_ELEMENT_MOD_MENU_H - -#include "common.h" -#include "librecomp/mods.hpp" -#include "ui_mod_details_panel.h" - -namespace recompui { - -class ElementModMenu : public Rml::Element, public Rml::EventListener { -public: - ElementModMenu(const Rml::String& tag); - virtual ~ElementModMenu(); - void ProcessEvent(Rml::Event& event) final; -private: - void RefreshMods(); - void CreateModList(); - Rml::ElementPtr CreateModListEntry(const recomp::mods::ModDetails& details, size_t index); - Rml::Element *refresh_button; - Rml::Element *list_el; // The root mod list element. - Rml::Element *list_el_scroll; // The scroll within the root mod list element. - std::unique_ptr mod_details_panel; - Rml::Element *active_list_entry_el = nullptr; - std::vector mod_details{}; - std::string game_mod_id; -}; - -} // namespace recompui -#endif diff --git a/src/ui/elements/ui_element.cpp b/src/ui/elements/ui_element.cpp index ba57b75..d20e755 100644 --- a/src/ui/elements/ui_element.cpp +++ b/src/ui/elements/ui_element.cpp @@ -17,6 +17,24 @@ static Rml::Unit to_rml(Unit unit) { } } +static Rml::Style::AlignItems to_rml(AlignItems align_items) { + switch (align_items) { + case AlignItems::FlexStart: + return Rml::Style::AlignItems::FlexStart; + case AlignItems::FlexEnd: + return Rml::Style::AlignItems::FlexEnd; + case AlignItems::Center: + return Rml::Style::AlignItems::Center; + case AlignItems::Baseline: + return Rml::Style::AlignItems::Baseline; + case AlignItems::Stretch: + return Rml::Style::AlignItems::Stretch; + default: + assert(false && "Unknown align items."); + return Rml::Style::AlignItems::FlexStart; + } +} + static Rml::Style::Overflow to_rml(Overflow overflow) { switch (overflow) { case Overflow::Visible: @@ -33,18 +51,32 @@ static Rml::Style::Overflow to_rml(Overflow overflow) { } } +static Rml::Style::TextAlign to_rml(TextAlign text_align) { + switch (text_align) { + case TextAlign::Left: + return Rml::Style::TextAlign::Left; + case TextAlign::Right: + return Rml::Style::TextAlign::Right; + case TextAlign::Center: + return Rml::Style::TextAlign::Center; + case TextAlign::Justify: + return Rml::Style::TextAlign::Justify; + default: + assert(false && "Unknown text align."); + return Rml::Style::TextAlign::Left; + } +} + Element::Element(Rml::Element *base) { assert(base != nullptr); this->base = base; - this->parent = nullptr; this->owner = false; } Element::Element(Element *parent, uint32_t events_enabled, Rml::String base_class) { assert(parent != nullptr); - this->parent = parent; this->owner = true; base = parent->base->AppendChild(parent->base->GetOwnerDocument()->CreateElement(base_class)); @@ -169,6 +201,22 @@ void Element::set_height_auto() { set_property(Rml::PropertyId::Height, Rml::Property(Rml::Style::FlexBasis::Type::Auto, Rml::Unit::KEYWORD)); } +void Element::set_min_width(float width, Unit unit, Animation animation) { + set_property(Rml::PropertyId::MinWidth, Rml::Property(width, to_rml(unit)), animation); +} + +void Element::set_min_height(float height, Unit unit, Animation animation) { + set_property(Rml::PropertyId::MinHeight, Rml::Property(height, to_rml(unit)), animation); +} + +void Element::set_max_width(float width, Unit unit, Animation animation) { + set_property(Rml::PropertyId::MaxWidth, Rml::Property(width, to_rml(unit)), animation); +} + +void Element::set_max_height(float height, Unit unit, Animation animation) { + set_property(Rml::PropertyId::MaxHeight, Rml::Property(height, to_rml(unit)), animation); +} + void Element::set_padding(float padding, Unit unit, Animation animation) { set_property(Rml::PropertyId::PaddingLeft, Rml::Property(padding, to_rml(unit)), animation); set_property(Rml::PropertyId::PaddingTop, Rml::Property(padding, to_rml(unit)), animation); @@ -223,6 +271,22 @@ void Element::set_border_width(float width, Unit unit, Animation animation) { set_property(Rml::PropertyId::BorderRightWidth, property, animation); } +void Element::set_border_left_width(float width, Unit unit, Animation animation) { + set_property(Rml::PropertyId::BorderLeftWidth, Rml::Property(width, to_rml(unit)), animation); +} + +void Element::set_border_top_width(float width, Unit unit, Animation animation) { + set_property(Rml::PropertyId::BorderTopWidth, Rml::Property(width, to_rml(unit)), animation); +} + +void Element::set_border_right_width(float width, Unit unit, Animation animation) { + set_property(Rml::PropertyId::BorderRightWidth, Rml::Property(width, to_rml(unit)), animation); +} + +void Element::set_border_bottom_width(float width, Unit unit, Animation animation) { + set_property(Rml::PropertyId::BorderBottomWidth, Rml::Property(width, to_rml(unit)), animation); +} + void Element::set_border_radius(float radius, Unit unit, Animation animation) { Rml::Property property(radius, to_rml(unit)); set_property(Rml::PropertyId::BorderTopLeftRadius, property, animation); @@ -260,6 +324,26 @@ void Element::set_border_color(const Color &color, Animation animation) { set_property(Rml::PropertyId::BorderRightColor, property, animation); } +void Element::set_border_left_color(const Color &color, Animation animation) { + Rml::Property property(Rml::Colourb(color.r, color.g, color.b, color.a), Rml::Unit::COLOUR); + set_property(Rml::PropertyId::BorderLeftColor, property, animation); +} + +void Element::set_border_top_color(const Color &color, Animation animation) { + Rml::Property property(Rml::Colourb(color.r, color.g, color.b, color.a), Rml::Unit::COLOUR); + set_property(Rml::PropertyId::BorderTopColor, property, animation); +} + +void Element::set_border_right_color(const Color &color, Animation animation) { + Rml::Property property(Rml::Colourb(color.r, color.g, color.b, color.a), Rml::Unit::COLOUR); + set_property(Rml::PropertyId::BorderRightColor, property, animation); +} + +void Element::set_border_bottom_color(const Color &color, Animation animation) { + Rml::Property property(Rml::Colourb(color.r, color.g, color.b, color.a), Rml::Unit::COLOUR); + set_property(Rml::PropertyId::BorderBottomColor, property, animation); +} + void Element::set_color(const Color &color, Animation animation) { Rml::Property property(Rml::Colourb(color.r, color.g, color.b, color.a), Rml::Unit::COLOUR); set_property(Rml::PropertyId::Color, property, animation); @@ -365,6 +449,10 @@ void Element::set_flex_direction(FlexDirection flex_direction) { } } +void Element::set_align_items(AlignItems align_items) { + set_property(Rml::PropertyId::AlignItems, to_rml(align_items)); +} + void Element::set_overflow(Overflow overflow) { set_property(Rml::PropertyId::OverflowX, to_rml(overflow)); set_property(Rml::PropertyId::OverflowY, to_rml(overflow)); @@ -412,4 +500,8 @@ void Element::set_text(const std::string &text) { base->SetInnerRML(text); } +void Element::set_text_align(TextAlign text_align) { + set_property(Rml::PropertyId::TextAlign, to_rml(text_align)); +} + }; \ No newline at end of file diff --git a/src/ui/elements/ui_element.h b/src/ui/elements/ui_element.h index 296fdd6..e477ad1 100644 --- a/src/ui/elements/ui_element.h +++ b/src/ui/elements/ui_element.h @@ -103,6 +103,14 @@ enum class FlexDirection { Column }; +enum class AlignItems { + FlexStart, + FlexEnd, + Center, + Baseline, + Stretch +}; + enum class Overflow { Visible, Hidden, @@ -126,6 +134,13 @@ enum class FontStyle { Italic }; +enum class TextAlign { + Left, + Right, + Center, + Justify +}; + struct Animation { AnimationType type = AnimationType::None; float duration = 0.0f; @@ -146,7 +161,6 @@ private: // Rml::EventListener overrides. virtual void ProcessEvent(Rml::Event &event) override; protected: - Element *parent; Rml::Element *base; bool owner; @@ -167,6 +181,10 @@ public: void set_width_auto(); void set_height(float height, Unit unit = Unit::Dp, Animation animation = Animation()); void set_height_auto(); + void set_min_width(float width, Unit unit = Unit::Dp, Animation animation = Animation()); + void set_min_height(float height, Unit unit = Unit::Dp, Animation animation = Animation()); + void set_max_width(float width, Unit unit = Unit::Dp, Animation animation = Animation()); + void set_max_height(float height, Unit unit = Unit::Dp, Animation animation = Animation()); void set_padding(float padding, Unit unit = Unit::Dp, Animation animation = Animation()); void set_padding_left(float padding, Unit unit = Unit::Dp, Animation animation = Animation()); void set_padding_top(float padding, Unit unit = Unit::Dp, Animation animation = Animation()); @@ -178,6 +196,10 @@ public: void set_margin_right(float margin, Unit unit = Unit::Dp, Animation animation = Animation()); void set_margin_bottom(float margin, Unit unit = Unit::Dp, Animation animation = Animation()); void set_border_width(float width, Unit unit = Unit::Dp, Animation animation = Animation()); + void set_border_left_width(float width, Unit unit = Unit::Dp, Animation animation = Animation()); + void set_border_top_width(float width, Unit unit = Unit::Dp, Animation animation = Animation()); + void set_border_right_width(float width, Unit unit = Unit::Dp, Animation animation = Animation()); + void set_border_bottom_width(float width, Unit unit = Unit::Dp, Animation animation = Animation()); void set_border_radius(float radius, Unit unit = Unit::Dp, Animation animation = Animation()); void set_border_top_left_radius(float radius, Unit unit = Unit::Dp, Animation animation = Animation()); void set_border_top_right_radius(float radius, Unit unit = Unit::Dp, Animation animation = Animation()); @@ -185,6 +207,10 @@ public: void set_border_bottom_right_radius(float radius, Unit unit = Unit::Dp, Animation animation = Animation()); void set_background_color(const Color &color, Animation animation = Animation()); void set_border_color(const Color &color, Animation animation = Animation()); + void set_border_left_color(const Color &color, Animation animation = Animation()); + void set_border_top_color(const Color &color, Animation animation = Animation()); + void set_border_right_color(const Color &color, Animation animation = Animation()); + void set_border_bottom_color(const Color &color, Animation animation = Animation()); void set_color(const Color &color, Animation animation = Animation()); void set_cursor(Cursor cursor); void set_opacity(float opacity, Animation animation = Animation()); @@ -197,6 +223,7 @@ public: void set_flex(float grow, float shrink, Animation animation = Animation()); void set_flex(float grow, float shrink, float basis_percentage, Animation animation = Animation()); void set_flex_direction(FlexDirection flex_direction); + void set_align_items(AlignItems align_items); void set_overflow(Overflow overflow); void set_overflow_x(Overflow overflow); void set_overflow_y(Overflow overflow); @@ -206,6 +233,7 @@ public: void set_font_style(FontStyle style); void set_font_weight(uint32_t weight, Animation animation = Animation()); void set_text(const std::string &text); + void set_text_align(TextAlign text_align); }; } // namespace recompui \ No newline at end of file diff --git a/src/ui/elements/ui_label.cpp b/src/ui/elements/ui_label.cpp index 8d8453c..455d636 100644 --- a/src/ui/elements/ui_label.cpp +++ b/src/ui/elements/ui_label.cpp @@ -6,20 +6,27 @@ namespace recompui { Label::Label(LabelStyle label_style, Element *parent) : Element(parent) { switch (label_style) { + case LabelStyle::Small: + set_font_size(20.0f); + set_letter_spacing(0.0f); + set_line_height(20.0f); + set_font_weight(400); + break; case LabelStyle::Normal: set_font_size(28.0f); set_letter_spacing(3.08f); set_line_height(28.0f); + set_font_weight(700); break; case LabelStyle::Large: set_font_size(36.0f); set_letter_spacing(2.52f); set_line_height(36.0f); + set_font_weight(700); break; } set_font_style(FontStyle::Normal); - set_font_weight(700); } Label::Label(const std::string &text, LabelStyle label_style, Element *parent) : Label(label_style, parent) { diff --git a/src/ui/elements/ui_label.h b/src/ui/elements/ui_label.h index 72a761d..bbfeb18 100644 --- a/src/ui/elements/ui_label.h +++ b/src/ui/elements/ui_label.h @@ -5,6 +5,7 @@ namespace recompui { enum class LabelStyle { + Small, Normal, Large }; diff --git a/src/ui/elements/ui_scroll_container.cpp b/src/ui/elements/ui_scroll_container.cpp new file mode 100644 index 0000000..bbe41c9 --- /dev/null +++ b/src/ui/elements/ui_scroll_container.cpp @@ -0,0 +1,27 @@ +#include "ui_scroll_container.h" + +#include + +namespace recompui { + + ScrollContainer::ScrollContainer(ScrollDirection direction, Element *parent) : Element(parent) { + set_flex(1.0f, 1.0f, 100.0f); + set_width(100.0f, Unit::Percent); + set_height(100.0f, Unit::Percent); + + switch (direction) { + case ScrollDirection::Horizontal: + set_max_width(100.0f, Unit::Percent); + set_overflow_x(Overflow::Auto); + break; + case ScrollDirection::Vertical: + set_max_height(100.0f, Unit::Percent); + set_overflow_y(Overflow::Auto); + break; + default: + assert(false && "Unknown scroll direction."); + break; + } + } + +}; \ No newline at end of file diff --git a/src/ui/elements/ui_scroll_container.h b/src/ui/elements/ui_scroll_container.h new file mode 100644 index 0000000..538af01 --- /dev/null +++ b/src/ui/elements/ui_scroll_container.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ui_element.h" + +namespace recompui { + + enum class ScrollDirection { + Horizontal, + Vertical + }; + + class ScrollContainer : public Element { + public: + ScrollContainer(ScrollDirection direction, Element *parent); + }; + +} // namespace recompui diff --git a/src/ui/ui_elements.h b/src/ui/ui_elements.h index 8ec01cf..854a0ff 100644 --- a/src/ui/ui_elements.h +++ b/src/ui/ui_elements.h @@ -14,7 +14,7 @@ #include "elements/ElementOptionTypeRange.h" #include "elements/ElementOptionTypeTextField.h" #include "elements/ElementDescription.h" -#include "elements/ElementModMenu.h" +#include "ui_mod_menu.h" namespace recompui { void register_custom_elements(); diff --git a/src/ui/elements/ui_mod_details_panel.cpp b/src/ui/ui_mod_details_panel.cpp similarity index 99% rename from src/ui/elements/ui_mod_details_panel.cpp rename to src/ui/ui_mod_details_panel.cpp index bac5af7..be7537a 100644 --- a/src/ui/elements/ui_mod_details_panel.cpp +++ b/src/ui/ui_mod_details_panel.cpp @@ -1,6 +1,5 @@ #include "ui_mod_details_panel.h" -#include "presets.h" #include "librecomp/mods.hpp" namespace recompui { diff --git a/src/ui/elements/ui_mod_details_panel.h b/src/ui/ui_mod_details_panel.h similarity index 87% rename from src/ui/elements/ui_mod_details_panel.h rename to src/ui/ui_mod_details_panel.h index 90517da..446fdd4 100644 --- a/src/ui/elements/ui_mod_details_panel.h +++ b/src/ui/ui_mod_details_panel.h @@ -1,13 +1,12 @@ #ifndef RECOMPUI_ELEMENT_MOD_DETAILS_PANEL_H #define RECOMPUI_ELEMENT_MOD_DETAILS_PANEL_H -#include "common.h" #include "librecomp/mods.hpp" -#include "ui_button.h" -#include "ui_container.h" -#include "ui_image.h" -#include "ui_label.h" -#include "ui_toggle.h" +#include "elements/ui_button.h" +#include "elements/ui_container.h" +#include "elements/ui_image.h" +#include "elements/ui_label.h" +#include "elements/ui_toggle.h" namespace recompui { diff --git a/src/ui/ui_mod_menu.cpp b/src/ui/ui_mod_menu.cpp new file mode 100644 index 0000000..7b78b09 --- /dev/null +++ b/src/ui/ui_mod_menu.cpp @@ -0,0 +1,161 @@ +#include "ui_mod_menu.h" + +#include "elements/presets.h" +#include "librecomp/mods.hpp" + +#include + +// TODO: +// - Set up navigation. +// - Add hover and active state for mod entries. + +namespace recompui { + +ModEntry::ModEntry(Element *parent, const recomp::mods::ModDetails &details, uint32_t mod_index, ModMenu *mod_menu) : Element(parent, Events(EventType::Click, EventType::Hover, EventType::Focus)) { + assert(mod_menu != nullptr); + + this->mod_index = mod_index; + this->mod_menu = mod_menu; + + set_display(Display::Flex); + set_flex_direction(FlexDirection::Row); + set_width(100.0f, Unit::Percent); + set_height_auto(); + set_padding_top(4.0f); + set_padding_right(8.0f); + set_padding_bottom(4.0f); + set_padding_left(8.0f); + set_border_width(1.1f); + set_border_color(Color{ 242, 242, 242, 204 }); + set_background_color(Color{ 242, 242, 242, 12 }); + set_cursor(Cursor::Pointer); + + { + thumbnail_image = std::make_unique(this); + thumbnail_image->set_width(100.0f); + thumbnail_image->set_height(100.0f); + thumbnail_image->set_min_width(100.0f); + thumbnail_image->set_min_height(100.0f); + thumbnail_image->set_background_color(Color{ 190, 184, 219, 25 }); + + body_container = std::make_unique(FlexDirection::Column, JustifyContent::FlexStart, this); + body_container->set_width_auto(); + body_container->set_height(100.0f); + body_container->set_margin_left(16.0f); + body_container->set_overflow(Overflow::Hidden); + + { + name_label = std::make_unique