diff --git a/build/Xenia.Cpp.x64.Common.props b/build/Xenia.Cpp.x64.Common.props index 22f7a1306..cdf59d98d 100644 --- a/build/Xenia.Cpp.x64.Common.props +++ b/build/Xenia.Cpp.x64.Common.props @@ -9,7 +9,7 @@ true - $(SolutionDir)third_party\flatbuffers\include\;$(SolutionDir)third_party\gflags\src\;$(SolutionDir)src\;$(SolutionDir)third_party;$(SolutionDir) + $(SolutionDir)third_party\flatbuffers\include\;$(SolutionDir)third_party\turbobadger\src\;$(SolutionDir)third_party\gflags\src\;$(SolutionDir)src\;$(SolutionDir)third_party;$(SolutionDir) GLEW_STATIC=1;GLEW_MX=1;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32;_WIN64=1;_AMD64=1;MICROPROFILE_MAX_THREADS=128;CAPSTONE_X86_ATT_DISABLE;CAPSTONE_DIET_NO;CAPSTONE_X86_REDUCE_NO;CAPSTONE_HAS_X86;CAPSTONE_USE_SYS_DYN_MEM;XBYAK_NO_OP_NAMES;%(PreprocessorDefinitions) Level4 true diff --git a/src/xenia/debug/ui/application.cc b/src/xenia/debug/ui/application.cc index 096e81614..3e03aebd2 100644 --- a/src/xenia/debug/ui/application.cc +++ b/src/xenia/debug/ui/application.cc @@ -16,8 +16,9 @@ #include "xenia/debug/ui/main_window.h" #include "xenia/profiling.h" -#include "third_party/turbobadger/src/tb/tb_msg.h" -#include "third_party/turbobadger/src/tb/tb_system.h" +#include "third_party/turbobadger/src/tb/message_handler.h" +#include "third_party/turbobadger/src/tb/util/metrics.h" +#include "third_party/turbobadger/src/tb/util/timer.h" namespace xe { namespace debug { @@ -81,16 +82,16 @@ void Application::Quit() { // This doesn't really belong here (it belongs in tb_system_[linux/windows].cpp. // This is here since the proper implementations has not yet been done. -void tb::TBSystem::RescheduleTimer(uint64_t fire_time) { - if (fire_time == tb::kNotSoon) { +void tb::util::RescheduleTimer(uint64_t fire_time) { + if (fire_time == tb::MessageHandler::kNotSoon) { return; } - uint64_t now = tb::TBSystem::GetTimeMS(); + uint64_t now = tb::util::GetTimeMS(); uint64_t delay_millis = fire_time >= now ? fire_time - now : 0; xe::debug::ui::Application::current()->loop()->PostDelayed([]() { uint64_t next_fire_time = tb::MessageHandler::GetNextMessageFireTime(); - uint64_t now = tb::TBSystem::GetTimeMS(); + uint64_t now = tb::util::GetTimeMS(); if (now < next_fire_time) { // We timed out *before* we were supposed to (the OS is not playing nice). // Calling ProcessMessages now won't achieve a thing so force a reschedule @@ -104,6 +105,6 @@ void tb::TBSystem::RescheduleTimer(uint64_t fire_time) { // If we still have things to do (because we didn't process all messages, // or because there are new messages), we need to rescedule, so call // RescheduleTimer. - tb::TBSystem::RescheduleTimer(tb::MessageHandler::GetNextMessageFireTime()); + tb::util::RescheduleTimer(tb::MessageHandler::GetNextMessageFireTime()); }, delay_millis); } diff --git a/src/xenia/debug/ui/turbo_badger_control.cc b/src/xenia/debug/ui/turbo_badger_control.cc index c6ccacb3e..28e8ae07e 100644 --- a/src/xenia/debug/ui/turbo_badger_control.cc +++ b/src/xenia/debug/ui/turbo_badger_control.cc @@ -17,10 +17,16 @@ // TODO(benvanik): remove this. #include "xenia/debug/ui/application.h" -#include "third_party/turbobadger/src/tb/tb_core.h" -#include "third_party/turbobadger/src/tb/tb_font_renderer.h" +#include "third_party/turbobadger/src/tb/animation_manager.h" +#include "third_party/turbobadger/src/tb/parsing/parse_node.h" +#include "third_party/turbobadger/src/tb/resources/font_manager.h" +#include "third_party/turbobadger/src/tb/turbo_badger.h" +#include "third_party/turbobadger/src/tb/util/string.h" + #include "third_party/turbobadger/src/tb/tb_message_window.h" -#include "third_party/turbobadger/src/tb/tb_widget_animation.h" +#include "third_party/turbobadger/src/tb/tb_scroll_container.h" +#include "third_party/turbobadger/src/tb/tb_text_box.h" +#include "third_party/turbobadger/src/tb/tb_toggle_container.h" namespace xe { namespace debug { @@ -36,23 +42,24 @@ constexpr double kDoubleClickDistance = 5; constexpr int32_t kMouseWheelDetent = 120; -class RootWidget : public tb::Widget { +class RootElement : public tb::Element { public: - RootWidget(TurboBadgerControl* owner) : owner_(owner) {} + RootElement(TurboBadgerControl* owner) : owner_(owner) {} void OnInvalid() override { owner_->Invalidate(); } private: TurboBadgerControl* owner_ = nullptr; }; -bool TurboBadgerControl::InitializeTurboBadger(tb::Renderer* renderer) { +bool TurboBadgerControl::InitializeTurboBadger( + tb::graphics::Renderer* renderer) { static bool has_initialized = false; if (has_initialized) { return true; } has_initialized = true; - if (!tb::tb_core_init( + if (!tb::Initialize( renderer, "third_party/turbobadger/resources/language/lng_en.tb.txt")) { XELOGE("Failed to initialize turbobadger core"); @@ -61,7 +68,7 @@ bool TurboBadgerControl::InitializeTurboBadger(tb::Renderer* renderer) { // Load the default skin, and override skin that contains the graphics // specific to the demo. - if (!tb::g_tb_skin->Load( + if (!tb::resources::Skin::get()->Load( "third_party/turbobadger/resources/default_skin/skin.tb.txt", "third_party/turbobadger/Demo/demo01/skin/skin.tb.txt")) { XELOGE("Failed to load turbobadger skin"); @@ -81,87 +88,912 @@ bool TurboBadgerControl::InitializeTurboBadger(tb::Renderer* renderer) { void register_freetype_font_renderer(); register_freetype_font_renderer(); #endif + auto font_manager = tb::resources::FontManager::get(); // Add fonts we can use to the font manager. #if defined(TB_FONT_RENDERER_STB) || defined(TB_FONT_RENDERER_FREETYPE) - tb::g_font_manager->AddFontInfo("third_party/turbobadger/resources/vera.ttf", - "Default"); + font_manager->AddFontInfo("third_party/turbobadger/resources/vera.ttf", + "Default"); #endif #ifdef TB_FONT_RENDERER_TBBF - tb::g_font_manager->AddFontInfo( + font_manager->AddFontInfo( "third_party/turbobadger/resources/default_font/" "segoe_white_with_shadow.tb.txt", "Default"); #endif - // Set the default font description for widgets to one of the fonts we just + // Set the default font description for elements to one of the fonts we just // added. tb::FontDescription fd; - fd.SetID(TBIDC("Default")); - fd.SetSize(tb::g_tb_skin->GetDimensionConverter()->DpToPx(14)); - tb::g_font_manager->SetDefaultFontDescription(fd); + fd.set_id(TBIDC("Default")); + fd.SetSize(tb::resources::Skin::get()->GetDimensionConverter()->DpToPx(14)); + font_manager->SetDefaultFontDescription(fd); // Create the font now. - auto font = tb::g_font_manager->CreateFontFace( - tb::g_font_manager->GetDefaultFontDescription()); + auto font = + font_manager->CreateFontFace(font_manager->GetDefaultFontDescription()); return true; } -void TurboBadgerControl::ShutdownTurboBadger() { tb::tb_core_shutdown(); } +void TurboBadgerControl::ShutdownTurboBadger() { tb::Shutdown(); } TurboBadgerControl::TurboBadgerControl(xe::ui::Loop* loop) : super(loop) {} TurboBadgerControl::~TurboBadgerControl() = default; +using tb::parsing::ParseNode; + +class TextNode { + public: + TextNode(const char* name, + std::vector> properties = {}, + std::vector children = {}) { + node_ = ParseNode::Create(name); + for (auto& prop : properties) { + set(prop.first, prop.second); + } + for (auto& child : children) { + node_->Add(child.node_); + } + } + + TextNode& set(const char* key, int32_t value) { + return set(key, tb::Value(value)); + } + + TextNode& set(const char* key, float value) { + return set(key, tb::Value(value)); + } + + TextNode& set(const char* key, const char* value) { + return set(key, tb::Value(value)); + } + + TextNode& set(const char* key, const std::string& value) { + return set(key, tb::Value(value.c_str())); + } + + TextNode& set(const char* key, tb::Rect value) { + auto va = new tb::ValueArray(); + va->AddInteger(value.x); + va->AddInteger(value.y); + va->AddInteger(value.w); + va->AddInteger(value.h); + auto node = node_->GetNode(key, ParseNode::MissingPolicy::kCreate); + node->GetValue().set_array(va, tb::Value::Set::kTakeOwnership); + return *this; + } + + TextNode& set(const char* key, tb::Value& value) { + auto node = node_->GetNode(key, ParseNode::MissingPolicy::kCreate); + node->TakeValue(value); + return *this; + } + + TextNode& child_list(std::initializer_list children) { + for (auto& child : children) { + node_->Add(child.node_); + } + return *this; + } + + ParseNode* node_ = nullptr; +}; + +namespace node { +using tb::Align; +using tb::Axis; +using tb::EditType; +using ElementState = tb::Element::State; +using tb::Gravity; +using tb::LayoutDistribution; +using tb::LayoutDistributionPosition; +using tb::LayoutOrder; +using tb::LayoutOverflow; +using tb::LayoutPosition; +using tb::LayoutSize; +using tb::Rect; +using tb::ScrollMode; +using tb::TextAlign; +using tb::ToggleAction; +using tb::Visibility; + +std::string operator"" _px(uint64_t value) { + return std::to_string(value) + "px"; +} +std::string operator"" _dp(uint64_t value) { + return std::to_string(value) + "dp"; +} +std::string operator"" _mm(uint64_t value) { + return std::to_string(value) + "mm"; +} + +struct Dimension { + Dimension(int32_t value) : value(std::to_string(value) + "px") {} + Dimension(const char* value) : value(value) {} + Dimension(std::string value) : value(std::move(value)) {} + operator std::string() { return value; } + std::string value; +}; +struct Id { + Id(int32_t value) : is_int(true), int_value(value) {} + Id(const char* value) : str_value(value) {} + Id(std::string value) : str_value(std::move(value)) {} + void set(TextNode* node, const char* key) { + if (is_int) { + node->set(key, int_value); + } else { + node->set(key, str_value); + } + } + bool is_int = false; + int32_t int_value = 0; + std::string str_value; +}; + +template +struct ElementNode : public TextNode { + using R = T; + ElementNode(const char* name, + std::vector> properties = {}, + std::vector children = {}) + : TextNode(name, std::move(properties), std::move(children)) {} + // TBID + R& id(Id value) { + value.set(this, "id"); + return *reinterpret_cast(this); + } + // TBID + R& group_id(Id value) { + value.set(this, "group-id"); + return *reinterpret_cast(this); + } + // TBID + R& skin(Id value) { + value.set(this, "skin"); + return *reinterpret_cast(this); + } + // Number + R& data(int32_t value) { + set("data", value); + return *reinterpret_cast(this); + } + R& data(float value) { + set("data", value); + return *reinterpret_cast(this); + } + // + R& is_group_root(bool value) { + set("is-group-root", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& is_focusable(bool value) { + set("is-focusable", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& is_long_clickable(bool value) { + set("want-long-click", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& ignore_input(bool value) { + set("ignore-input", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& opacity(float value) { + set("opacity", value); + return *reinterpret_cast(this); + } + // + R& connection(std::string value) { + set("connection", value); + return *reinterpret_cast(this); + } + // + R& axis(Axis value) { + set("axis", tb::to_string(value)); + return *reinterpret_cast(this); + } + // + R& gravity(Gravity value) { + set("gravity", tb::to_string(value)); + return *reinterpret_cast(this); + } + // + R& visibility(Visibility value) { + set("visibility", tb::to_string(value)); + return *reinterpret_cast(this); + } + // + R& state(ElementState value) { + set("state", tb::to_string(value)); + return *reinterpret_cast(this); + } + // + R& is_enabled(bool value) { + set("state", + to_string(value ? ElementState::kNone : ElementState::kDisabled)); + return *reinterpret_cast(this); + } + // Rect + R& rect(Rect value) { + set("rect", std::move(value)); + return *reinterpret_cast(this); + } + // + R& width(Dimension value) { + set("lp>width", value); + return *reinterpret_cast(this); + } + // + R& min_width(Dimension value) { + set("lp>min-width", value); + return *reinterpret_cast(this); + } + // + R& max_width(Dimension value) { + set("lp>max-width", value); + return *reinterpret_cast(this); + } + // + R& preferred_width(Dimension value) { + set("lp>pref-width", value); + return *reinterpret_cast(this); + } + // + R& height(Dimension value) { + set("lp>height", value); + return *reinterpret_cast(this); + } + // + R& min_height(Dimension value) { + set("lp>min-height", value); + return *reinterpret_cast(this); + } + // + R& max_height(Dimension value) { + set("lp>max-height", value); + return *reinterpret_cast(this); + } + // + R& preferred_height(Dimension value) { + set("lp>pref-height", value); + return *reinterpret_cast(this); + } + // + R& tooltip(std::string value) { + set("tooltip", value); + return *reinterpret_cast(this); + } + // The Element will be focused automatically the first time its Window is + // activated. + R& autofocus(bool value) { + set("autofocus", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& font(const char* name, int32_t size_px) { + set("font>name", name); + set("font>size", size_px); + return *reinterpret_cast(this); + } + // + R& font_name(std::string value) { + set("font>name", value); + return *reinterpret_cast(this); + } + // + R& font_size(Dimension value) { + set("font>size", value); + return *reinterpret_cast(this); + } + + R& clone(TextNode node) { + node_->Add(CloneNode(node).node_); + return *reinterpret_cast(this); + } + R& child(TextNode node) { + node_->Add(node.node_); + return *reinterpret_cast(this); + } + R& children() { return *reinterpret_cast(this); } + template + R& children(TextNode child_node, C... other_children) { + child(child_node); + children(other_children...); + return *reinterpret_cast(this); + } +}; +struct ButtonNode : public ElementNode { + using R = ButtonNode; + ButtonNode(const char* text = nullptr) : ElementNode("Button") { + if (text) { + this->text(text); + } + } + // + R& text(std::string value) { + set("text", value); + return *reinterpret_cast(this); + } + // + R& toggle_mode(bool value) { + set("toggle-mode", value ? 1 : 0); + return *reinterpret_cast(this); + } +}; +struct SelectInlineNode : public ElementNode { + using R = SelectInlineNode; + SelectInlineNode() : ElementNode("SelectInline") {} + SelectInlineNode(int32_t default_value, int32_t min_value, int32_t max_value) + : ElementNode("SelectInline") { + value(default_value); + min(min_value); + max(max_value); + } + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& min(int32_t value) { + set("min", value); + return *reinterpret_cast(this); + } + // + R& max(int32_t value) { + set("max", value); + return *reinterpret_cast(this); + } +}; +struct LabelContainerNode : public ElementNode { + using R = LabelContainerNode; + LabelContainerNode(const char* text, std::vector children = {}) + : ElementNode("LabelContainer", {}, std::move(children)) { + if (text) { + this->text(text); + } + } + // + R& text(std::string value) { + set("text", value); + return *reinterpret_cast(this); + } +}; +struct TextBoxNode : public ElementNode { + using R = TextBoxNode; + TextBoxNode(const char* text = nullptr) : ElementNode("TextBox") { + if (text) { + this->text(text); + } + } + // + R& text(std::string value) { + set("text", value); + return *reinterpret_cast(this); + } + // + R& is_multiline(bool value) { + set("multiline", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& is_styling(bool value) { + set("styling", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& is_read_only(bool value) { + set("readonly", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& is_wrapping(bool value) { + set("wrap", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& adapt_to_content(bool value) { + set("adapt-to-content", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& virtual_width(Dimension value) { + set("virtual-width", value); + return *reinterpret_cast(this); + } + // + R& placeholder(std::string value) { + set("placeholder", value); + return *reinterpret_cast(this); + } + // + R& type(EditType value) { + set("type", tb::to_string(value)); + return *reinterpret_cast(this); + } +}; +struct LayoutNode : public ElementNode { + using R = LayoutNode; + LayoutNode(std::vector children = {}) + : ElementNode("Layout", {}, std::move(children)) {} + // + R& spacing(Dimension value) { + set("spacing", value); + return *reinterpret_cast(this); + } + // + R& size(LayoutSize value) { + set("size", tb::to_string(value)); + return *reinterpret_cast(this); + } + // + R& position(LayoutPosition value) { + set("position", tb::to_string(value)); + return *reinterpret_cast(this); + } + // + R& overflow(LayoutOverflow value) { + set("overflow", tb::to_string(value)); + return *reinterpret_cast(this); + } + // + R& distribution(LayoutDistribution value) { + set("distribution", tb::to_string(value)); + return *reinterpret_cast(this); + } + // + R& distribution_position(LayoutDistributionPosition value) { + set("distribution-position", tb::to_string(value)); + return *reinterpret_cast(this); + } +}; +struct ScrollContainerNode : public ElementNode { + using R = ScrollContainerNode; + ScrollContainerNode(std::vector children = {}) + : ElementNode("ScrollContainer", {}, std::move(children)) {} + // + R& adapt_content(bool value) { + set("adapt-content", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& adapt_to_content(bool value) { + set("adapt-to-content", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& scroll_mode(ScrollMode value) { + set("scroll-mode", tb::to_string(value)); + return *reinterpret_cast(this); + } +}; +struct TabContainerNode : public ElementNode { + using R = TabContainerNode; + TabContainerNode(std::vector children = {}) + : ElementNode("TabContainer", {}, std::move(children)) {} + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& align(Align value) { + set("align", tb::to_string(value)); + return *reinterpret_cast(this); + } + // content? + // root? + TabContainerNode& tab(TextNode tab_button, TextNode tab_content) { + auto tabs_node = node_->GetNode("tabs", ParseNode::MissingPolicy::kCreate); + tabs_node->Add(tab_button.node_); + node_->Add(tab_content.node_); + return *this; + } +}; +struct ScrollBarNode : public ElementNode { + using R = ScrollBarNode; + ScrollBarNode() : ElementNode("ScrollBar") {} + // + R& value(float value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& axis(Axis value) { + set("axis", tb::to_string(value)); + return *reinterpret_cast(this); + } +}; +struct SliderNode : public ElementNode { + using R = SliderNode; + SliderNode() : ElementNode("Slider") {} + // + R& value(float value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& min(float value) { + set("min", value); + return *reinterpret_cast(this); + } + // + R& max(float value) { + set("max", value); + return *reinterpret_cast(this); + } + // + R& axis(Axis value) { + set("axis", tb::to_string(value)); + return *reinterpret_cast(this); + } +}; +struct Item { + std::string id; + std::string text; + Item(std::string id, std::string text) : id(id), text(text) {} +}; +template +struct ItemListElementNode : public ElementNode { + ItemListElementNode(const char* name) : ElementNode(name) {} + T& item(std::string text) { + auto items_node = + node_->GetNode("items", ParseNode::MissingPolicy::kCreate); + auto node = ParseNode::Create("item"); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(tb::Value(text.c_str())); + node->Add(text_node); + items_node->Add(node); + return *reinterpret_cast(this); + } + T& item(std::string id, std::string text) { + auto items_node = + node_->GetNode("items", ParseNode::MissingPolicy::kCreate); + auto node = ParseNode::Create("item"); + auto id_node = ParseNode::Create("id"); + id_node->TakeValue(tb::Value(id.c_str())); + node->Add(id_node); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(tb::Value(text.c_str())); + node->Add(text_node); + items_node->Add(node); + return *reinterpret_cast(this); + } + T& item(int32_t id, std::string text) { + auto items_node = + node_->GetNode("items", ParseNode::MissingPolicy::kCreate); + auto node = ParseNode::Create("item"); + auto id_node = ParseNode::Create("id"); + id_node->TakeValue(tb::Value(id)); + node->Add(id_node); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(tb::Value(text.c_str())); + node->Add(text_node); + items_node->Add(node); + return *reinterpret_cast(this); + } + T& items(std::initializer_list items) { + auto items_node = + node_->GetNode("items", ParseNode::MissingPolicy::kCreate); + for (auto& item : items) { + auto node = ParseNode::Create("item"); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(tb::Value(item.c_str())); + node->Add(text_node); + items_node->Add(node); + } + return *reinterpret_cast(this); + } + T& items(std::initializer_list> items) { + auto items_node = + node_->GetNode("items", ParseNode::MissingPolicy::kCreate); + for (auto& item : items) { + auto node = ParseNode::Create("item"); + auto id_node = ParseNode::Create("id"); + id_node->TakeValue(tb::Value(item.first)); + node->Add(id_node); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(tb::Value(item.second.c_str())); + node->Add(text_node); + items_node->Add(node); + } + return *reinterpret_cast(this); + } + T& items(std::initializer_list items) { + auto items_node = + node_->GetNode("items", ParseNode::MissingPolicy::kCreate); + for (auto& item : items) { + auto node = ParseNode::Create("item"); + auto id_node = ParseNode::Create("id"); + id_node->TakeValue(tb::Value(item.id.c_str())); + node->Add(id_node); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(tb::Value(item.text.c_str())); + node->Add(text_node); + items_node->Add(node); + } + return *reinterpret_cast(this); + } +}; +struct SelectListNode : public ItemListElementNode { + using R = SelectListNode; + SelectListNode() : ItemListElementNode("SelectList") {} + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } +}; +struct SelectDropdownNode : public ItemListElementNode { + using R = SelectDropdownNode; + SelectDropdownNode() : ItemListElementNode("SelectDropdown") {} + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } +}; +struct CheckBoxNode : public ElementNode { + using R = CheckBoxNode; + CheckBoxNode() : ElementNode("CheckBox") {} + // + R& value(bool value) { + set("value", value ? 1 : 0); + return *reinterpret_cast(this); + } +}; +struct RadioButtonNode : public ElementNode { + using R = RadioButtonNode; + RadioButtonNode() : ElementNode("RadioButton") {} + // + R& value(bool value) { + set("value", value ? 1 : 0); + return *reinterpret_cast(this); + } +}; +struct LabelNode : public ElementNode { + using R = LabelNode; + LabelNode(const char* text = nullptr) : ElementNode("Label") { + if (text) { + this->text(text); + } + } + // + R& text(std::string value) { + set("text", value); + return *reinterpret_cast(this); + } + // + R& text_align(TextAlign value) { + set("text-align", tb::to_string(value)); + return *reinterpret_cast(this); + } +}; +struct SkinImageNode : public ElementNode { + using R = SkinImageNode; + SkinImageNode() : ElementNode("SkinImage") {} +}; +struct SeparatorNode : public ElementNode { + using R = SeparatorNode; + SeparatorNode() : ElementNode("Separator") {} +}; +struct ProgressSpinnerNode : public ElementNode { + using R = ProgressSpinnerNode; + ProgressSpinnerNode() : ElementNode("ProgressSpinner") {} + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } +}; +struct ContainerNode : public ElementNode { + using R = ContainerNode; + ContainerNode() : ElementNode("Container") {} +}; +struct SectionNode : public ElementNode { + using R = SectionNode; + SectionNode(const char* text = nullptr) : ElementNode("Section") { + if (text) { + this->text(text); + } + } + R& content(TextNode child) { return this->child(child); } + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& text(std::string value) { + set("text", value); + return *reinterpret_cast(this); + } +}; +struct ToggleContainerNode : public ElementNode { + using R = ToggleContainerNode; + ToggleContainerNode() : ElementNode("ToggleContainer") {} + // + R& value(bool value) { + set("value", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& toggle_action(ToggleAction value) { + set("toggle", tb::to_string(value)); + return *reinterpret_cast(this); + } + // + R& invert(bool value) { + set("invert", value ? 1 : 0); + return *reinterpret_cast(this); + } +}; +struct ImageElementNode : public ElementNode { + using R = ImageElementNode; + ImageElementNode(const char* filename = nullptr) + : ElementNode("ImageElement") { + if (filename) { + this->filename(filename); + } + } + // + R& filename(std::string value) { + set("filename", value); + return *reinterpret_cast(this); + } +}; +struct CloneNode : public TextNode { + using R = CloneNode; + CloneNode(TextNode& source) : TextNode(source.node_->GetName()) { + node_->GetValue().Copy(source.node_->GetValue()); + node_->CloneChildren(source.node_); + } +}; +} // namespace node +TextNode BuildSomeControl() { + using namespace node; + return LayoutNode() + .axis(Axis::kX) + .child(LabelNode("foo")) + .child(LabelNode("bar")); +} +TextNode BuildUI() { + using namespace node; + auto rep_tree = LayoutNode().axis(Axis::kX).child_list({ + LabelNode("item"), ButtonNode("button"), + }); + return LayoutNode() + .id("foo") + .position(LayoutPosition::kLeftTop) + .axis(Axis::kY) + .children( + TabContainerNode() + .gravity(Gravity::kAll) + .axis(Axis::kX) + .tab(ButtonNode("Foo"), CloneNode(rep_tree)) + .tab(ButtonNode("Foo0"), CloneNode(rep_tree)) + .tab(ButtonNode("Foo1"), LabelNode("bar1")), + SectionNode("controls:") + .content(LayoutNode() + .child(BuildSomeControl()) + .child(BuildSomeControl())), + LabelNode("distribution: preferred").width(32_dp).font_size(3_mm), + LayoutNode() + .distribution(LayoutDistribution::kPreferred) + .child(ButtonNode("tab 0")) + .child(TextBoxNode("foo").type(EditType::kPassword)) + .children(ToggleContainerNode() + .value(true) + .toggle_action(ToggleAction::kExpanded) + .child(TextBoxNode() + .placeholder("@search") + .gravity(Gravity::kLeftRight) + .type(EditType::kSearch)), + ButtonNode("fffoo").is_enabled(false)) + .child(ButtonNode("tab 0")) + .child(ButtonNode("tab 0")) + .clone(rep_tree) + .children(ButtonNode("tab 1"), ButtonNode("tab 2"), + ButtonNode("tab 3"), ButtonNode("tab 4")), + SelectInlineNode(4, 0, 40), SelectListNode().items({ + {1, "a"}, {2, "b"}, + }), + SelectListNode().item("a").item("id", "b").item(5, "c"), + SelectDropdownNode().value(1).items({ + Item("1", "a"), Item("2", "b"), + }), + SelectDropdownNode().value(1).items({ + {1, "a"}, {2, "b"}, + }), + SelectDropdownNode().value(1).items({ + "a", "b", "c", + })); +} bool TurboBadgerControl::Create() { if (!super::Create()) { return false; } // TODO(benvanik): setup renderer? - renderer_ = TBRendererGL4::Create(); + renderer_ = TBRendererGL4::Create(context()); if (!InitializeTurboBadger(renderer_.get())) { XELOGE("Unable to initialize turbobadger"); return false; } - tb::WidgetAnimationManager::Init(); + // TODO(benvanik): setup elements. + root_element_ = std::make_unique(this); + root_element_->SetSkinBg(TBIDC("background")); + root_element_->set_rect({0, 0, 1000, 1000}); - // TODO(benvanik): setup widgets. - root_widget_ = std::make_unique(this); - root_widget_->SetSkinBg(TBIDC("background")); - root_widget_->SetRect(tb::Rect(0, 0, 1000, 1000)); + // ParseNode node; + ////node.ReadData("Label: text: \"distribution: preferred\""); + // auto tf = ParseNode::Create("Label"); + // auto tt = ParseNode::Create("text"); + // tt->TakeValue(tb::Value("hello")); + // tf->Add(tt); + // node.Add(tf); + // tb::g_elements_reader->LoadNodeTree(root_element_.get(), &node); + /* + Layout: position: left top, axis: y + Label: text: "distribution: preferred" + Layout: distribution: preferred + Button: text: tab 1 + Button: text: tab 2 + Button: text: tab 3 + Button: text: tab 4 + TextBox: placeholder: @search, gravity: left right, type: "search" + Label: text: "distribution: available" + Layout: distribution: available + Button: text: tab 1 + Button: text: tab 2 + Button: text: tab 3 + Button: text: tab 4 + TextBox: placeholder: @search, gravity: left right, type: "search" + */ + + auto n = BuildUI(); + ParseNode r; + r.Add(n.node_); + root_element_.get()->LoadNodeTree(&r); + // n.node_->Clear(); + // delete n.node_; // Block animations during init. tb::AnimationBlocker anim_blocker; // TODO(benvanik): dummy UI. - auto message_window = new tb::MessageWindow(root_widget(), TBIDC("")); + auto message_window = new tb::MessageWindow(root_element(), TBIDC("")); message_window->Show("Title", "Hello!"); - // tb::ShowDebugInfoSettingsWindow(root_widget()); + // tb::ShowDebugInfoSettingsWindow(root_element()); return true; } void TurboBadgerControl::Destroy() { - tb::WidgetAnimationManager::Shutdown(); + tb::Shutdown(); super::Destroy(); } void TurboBadgerControl::OnLayout(xe::ui::UIEvent& e) { super::OnLayout(e); - if (!root_widget()) { + if (!root_element()) { return; } // TODO(benvanik): subregion? - root_widget()->SetRect(tb::Rect(0, 0, width(), height())); + root_element()->set_rect({0, 0, width(), height()}); } void TurboBadgerControl::OnPaint(xe::ui::UIEvent& e) { super::OnPaint(e); - if (!root_widget()) { + if (!root_element()) { return; } @@ -177,32 +1009,32 @@ void TurboBadgerControl::OnPaint(xe::ui::UIEvent& e) { // Update TB (run animations, handle deferred input, etc). tb::AnimationManager::Update(); - root_widget()->InvokeProcessStates(); - root_widget()->InvokeProcess(); + root_element()->InvokeProcessStates(); + root_element()->InvokeProcess(); renderer()->BeginPaint(width(), height()); // Render entire control hierarchy. - root_widget()->InvokePaint(tb::Widget::PaintProps()); + root_element()->InvokePaint(tb::Element::PaintProps()); // Render debug overlay. - root_widget()->GetFont()->DrawString( + root_element()->GetFont()->DrawString( 5, 5, tb::Color(255, 0, 0), - tb::format_string("Frame %lld", frame_count_)); + tb::util::format_string("Frame %lld", frame_count_)); if (kContinuousRepaint) { - root_widget()->GetFont()->DrawString(5, 20, tb::Color(255, 0, 0), - tb::format_string("FPS: %d", fps_)); + root_element()->GetFont()->DrawString( + 5, 20, tb::Color(255, 0, 0), tb::util::format_string("FPS: %d", fps_)); } renderer()->EndPaint(); // If animations are running, reinvalidate immediately. if (tb::AnimationManager::HasAnimationsRunning()) { - root_widget()->Invalidate(); + root_element()->Invalidate(); } if (kContinuousRepaint) { // Force an immediate repaint, always. - root_widget()->Invalidate(); + root_element()->Invalidate(); } } @@ -237,7 +1069,7 @@ tb::ModifierKeys TurboBadgerControl::GetModifierKeys() { } void TurboBadgerControl::OnKeyPress(xe::ui::KeyEvent& e, bool is_down) { - if (!root_widget()) { + if (!root_element()) { return; } auto special_key = tb::SpecialKey::kUndefined; @@ -321,10 +1153,10 @@ void TurboBadgerControl::OnKeyPress(xe::ui::KeyEvent& e, bool is_down) { special_key = tb::SpecialKey::kEsc; break; case 93: - if (!is_down && tb::Widget::focused_widget) { - tb::WidgetEvent ev(tb::EventType::kContextMenu); + if (!is_down && tb::Element::focused_element) { + tb::ElementEvent ev(tb::EventType::kContextMenu); ev.modifierkeys = GetModifierKeys(); - tb::Widget::focused_widget->InvokeEvent(ev); + tb::Element::focused_element->InvokeEvent(ev); e.set_handled(true); return; } @@ -345,7 +1177,7 @@ void TurboBadgerControl::OnKeyPress(xe::ui::KeyEvent& e, bool is_down) { } if (!CheckShortcutKey(e, special_key, is_down)) { - e.set_handled(root_widget()->InvokeKey( + e.set_handled(root_element()->InvokeKey( special_key != tb::SpecialKey::kUndefined ? e.key_code() : 0, special_key, GetModifierKeys(), is_down)); } @@ -355,7 +1187,7 @@ bool TurboBadgerControl::CheckShortcutKey(xe::ui::KeyEvent& e, tb::SpecialKey special_key, bool is_down) { bool shortcut_key = modifier_cntrl_pressed_; - if (!tb::Widget::focused_widget || !is_down || !shortcut_key) { + if (!tb::Element::focused_element || !is_down || !shortcut_key) { return false; } bool reverse_key = modifier_shift_pressed_; @@ -395,10 +1227,10 @@ bool TurboBadgerControl::CheckShortcutKey(xe::ui::KeyEvent& e, return false; } - tb::WidgetEvent ev(tb::EventType::kShortcut); + tb::ElementEvent ev(tb::EventType::kShortcut); ev.modifierkeys = GetModifierKeys(); ev.ref_id = id; - if (!tb::Widget::focused_widget->InvokeEvent(ev)) { + if (!tb::Element::focused_element->InvokeEvent(ev)) { return false; } e.set_handled(true); @@ -417,7 +1249,7 @@ void TurboBadgerControl::OnKeyUp(xe::ui::KeyEvent& e) { void TurboBadgerControl::OnMouseDown(xe::ui::MouseEvent& e) { super::OnMouseDown(e); - if (!root_widget()) { + if (!root_element()) { return; } // TODO(benvanik): more button types. @@ -440,37 +1272,37 @@ void TurboBadgerControl::OnMouseDown(xe::ui::MouseEvent& e) { last_click_y_ = e.y(); last_click_time_ = now; - e.set_handled(root_widget()->InvokePointerDown( + e.set_handled(root_element()->InvokePointerDown( e.x(), e.y(), last_click_counter_, GetModifierKeys(), kTouch)); } } void TurboBadgerControl::OnMouseMove(xe::ui::MouseEvent& e) { super::OnMouseMove(e); - if (!root_widget()) { + if (!root_element()) { return; } - root_widget()->InvokePointerMove(e.x(), e.y(), GetModifierKeys(), kTouch); + root_element()->InvokePointerMove(e.x(), e.y(), GetModifierKeys(), kTouch); e.set_handled(true); } void TurboBadgerControl::OnMouseUp(xe::ui::MouseEvent& e) { super::OnMouseUp(e); - if (!root_widget()) { + if (!root_element()) { return; } if (e.button() == xe::ui::MouseEvent::Button::kLeft) { - e.set_handled(root_widget()->InvokePointerUp(e.x(), e.y(), - GetModifierKeys(), kTouch)); + e.set_handled(root_element()->InvokePointerUp(e.x(), e.y(), + GetModifierKeys(), kTouch)); } else if (e.button() == xe::ui::MouseEvent::Button::kRight) { - root_widget()->InvokePointerMove(e.x(), e.y(), GetModifierKeys(), kTouch); - if (tb::Widget::hovered_widget) { + root_element()->InvokePointerMove(e.x(), e.y(), GetModifierKeys(), kTouch); + if (tb::Element::hovered_element) { int x = e.x(); int y = e.y(); - tb::Widget::hovered_widget->ConvertFromRoot(x, y); - tb::WidgetEvent ev(tb::EventType::kContextMenu, x, y, kTouch, - GetModifierKeys()); - tb::Widget::hovered_widget->InvokeEvent(ev); + tb::Element::hovered_element->ConvertFromRoot(x, y); + tb::ElementEvent ev(tb::EventType::kContextMenu, x, y, kTouch, + GetModifierKeys()); + tb::Element::hovered_element->InvokeEvent(ev); } e.set_handled(true); } @@ -478,10 +1310,10 @@ void TurboBadgerControl::OnMouseUp(xe::ui::MouseEvent& e) { void TurboBadgerControl::OnMouseWheel(xe::ui::MouseEvent& e) { super::OnMouseWheel(e); - if (!root_widget()) { + if (!root_element()) { return; } - e.set_handled(root_widget()->InvokeWheel( + e.set_handled(root_element()->InvokeWheel( e.x(), e.y(), e.dx(), -e.dy() / kMouseWheelDetent, GetModifierKeys())); } diff --git a/src/xenia/debug/ui/turbo_badger_control.h b/src/xenia/debug/ui/turbo_badger_control.h index fa17472b4..8d670b52a 100644 --- a/src/xenia/debug/ui/turbo_badger_control.h +++ b/src/xenia/debug/ui/turbo_badger_control.h @@ -15,8 +15,7 @@ #include "xenia/ui/gl/wgl_control.h" #include "xenia/ui/loop.h" -#include "third_party/turbobadger/src/tb/tb_core.h" -#include "third_party/turbobadger/src/tb/tb_widgets.h" +#include "third_party/turbobadger/src/tb/element.h" namespace xe { namespace debug { @@ -27,11 +26,11 @@ class TurboBadgerControl : public xe::ui::gl::WGLControl { TurboBadgerControl(xe::ui::Loop* loop); ~TurboBadgerControl() override; - static bool InitializeTurboBadger(tb::Renderer* renderer); + static bool InitializeTurboBadger(tb::graphics::Renderer* renderer); static void ShutdownTurboBadger(); - tb::Renderer* renderer() const { return renderer_.get(); } - tb::Widget* root_widget() const { return root_widget_.get(); } + tb::graphics::Renderer* renderer() const { return renderer_.get(); } + tb::Element* root_element() const { return root_element_.get(); } protected: using super = xe::ui::gl::WGLControl; @@ -58,8 +57,8 @@ class TurboBadgerControl : public xe::ui::gl::WGLControl { void OnMouseUp(xe::ui::MouseEvent& e) override; void OnMouseWheel(xe::ui::MouseEvent& e) override; - std::unique_ptr renderer_; - std::unique_ptr root_widget_; + std::unique_ptr renderer_; + std::unique_ptr root_element_; uint32_t frame_count_ = 0; uint32_t fps_ = 0; diff --git a/src/xenia/debug/ui/turbo_badger_renderer.cc b/src/xenia/debug/ui/turbo_badger_renderer.cc index 939f3b2f5..a40f9de39 100644 --- a/src/xenia/debug/ui/turbo_badger_renderer.cc +++ b/src/xenia/debug/ui/turbo_badger_renderer.cc @@ -12,9 +12,8 @@ #include "xenia/base/assert.h" #include "xenia/base/logging.h" -#include "third_party/turbobadger/src/tb/tb_types.h" -#include "third_party/turbobadger/src/tb/tb_bitmap_fragment.h" -#include "third_party/turbobadger/src/tb/tb_system.h" +#include "third_party/turbobadger/src/tb/graphics/bitmap_fragment.h" +#include "third_party/turbobadger/src/tb/util/math.h" namespace xe { namespace debug { @@ -22,9 +21,9 @@ namespace ui { using namespace tb; -class TBRendererGL4::TBBitmapGL4 : public tb::Bitmap { +class TBRendererGL4::TBBitmapGL4 : public tb::graphics::Bitmap { public: - TBBitmapGL4(TBRendererGL4* renderer); + TBBitmapGL4(xe::ui::gl::GLContext* context, TBRendererGL4* renderer); ~TBBitmapGL4(); bool Init(int width, int height, uint32_t* data); @@ -32,6 +31,7 @@ class TBRendererGL4::TBBitmapGL4 : public tb::Bitmap { int Height() override { return height_; } void SetData(uint32_t* data) override; + xe::ui::gl::GLContext* context_ = nullptr; TBRendererGL4* renderer_ = nullptr; int width_ = 0; int height_ = 0; @@ -39,19 +39,22 @@ class TBRendererGL4::TBBitmapGL4 : public tb::Bitmap { GLuint64 gpu_handle_ = 0; }; -TBRendererGL4::TBBitmapGL4::TBBitmapGL4(TBRendererGL4* renderer) - : renderer_(renderer) {} +TBRendererGL4::TBBitmapGL4::TBBitmapGL4(xe::ui::gl::GLContext* context, + TBRendererGL4* renderer) + : context_(context), renderer_(renderer) {} TBRendererGL4::TBBitmapGL4::~TBBitmapGL4() { - // Must flush and unbind before we delete the texture + xe::ui::gl::GLContextLock lock(context_); + + // Must flush and unbind before we delete the texture. renderer_->FlushBitmap(this); glMakeTextureHandleNonResidentARB(gpu_handle_); glDeleteTextures(1, &handle_); } bool TBRendererGL4::TBBitmapGL4::Init(int width, int height, uint32_t* data) { - assert(width == GetNearestPowerOfTwo(width)); - assert(height == GetNearestPowerOfTwo(height)); + assert(width == tb::util::GetNearestPowerOfTwo(width)); + assert(height == tb::util::GetNearestPowerOfTwo(height)); width_ = width; height_ = height; @@ -76,17 +79,21 @@ void TBRendererGL4::TBBitmapGL4::SetData(uint32_t* data) { GL_UNSIGNED_BYTE, data); } -TBRendererGL4::TBRendererGL4() - : vertex_buffer_(VERTEX_BATCH_SIZE * sizeof(Vertex)) {} +TBRendererGL4::TBRendererGL4(xe::ui::gl::GLContext* context) + : context_(context), + vertex_buffer_(graphics::BatchingRenderer::kVertexBatchSize * + sizeof(Vertex)) {} TBRendererGL4::~TBRendererGL4() { + xe::ui::gl::GLContextLock lock(context_); vertex_buffer_.Shutdown(); glDeleteVertexArrays(1, &vao_); glDeleteProgram(program_); } -std::unique_ptr TBRendererGL4::Create() { - auto renderer = std::make_unique(); +std::unique_ptr TBRendererGL4::Create( + xe::ui::gl::GLContext* context) { + auto renderer = std::make_unique(context); if (!renderer->Initialize()) { XELOGE("Failed to initialize TurboBadger GL4 renderer"); return nullptr; @@ -181,8 +188,9 @@ void main() { \n\ return true; } -Bitmap* TBRendererGL4::CreateBitmap(int width, int height, uint32_t* data) { - auto bitmap = std::make_unique(this); +graphics::Bitmap* TBRendererGL4::CreateBitmap(int width, int height, + uint32_t* data) { + auto bitmap = std::make_unique(context_, this); if (!bitmap->Init(width, height, data)) { return nullptr; } @@ -191,12 +199,12 @@ Bitmap* TBRendererGL4::CreateBitmap(int width, int height, uint32_t* data) { void TBRendererGL4::SetClipRect(const Rect& rect) { Flush(); - glScissor(m_clip_rect.x, m_screen_rect.h - (m_clip_rect.y + m_clip_rect.h), - m_clip_rect.w, m_clip_rect.h); + glScissor(clip_rect_.x, screen_rect_.h - (clip_rect_.y + clip_rect_.h), + clip_rect_.w, clip_rect_.h); } void TBRendererGL4::BeginPaint(int render_target_w, int render_target_h) { - RendererBatcher::BeginPaint(render_target_w, render_target_h); + tb::graphics::BatchingRenderer::BeginPaint(render_target_w, render_target_h); glEnablei(GL_BLEND, 0); glBlendFunci(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -230,7 +238,7 @@ void TBRendererGL4::BeginPaint(int render_target_w, int render_target_h) { } void TBRendererGL4::EndPaint() { - RendererBatcher::EndPaint(); + tb::graphics::BatchingRenderer::EndPaint(); Flush(); diff --git a/src/xenia/debug/ui/turbo_badger_renderer.h b/src/xenia/debug/ui/turbo_badger_renderer.h index 8be060ccd..48e874c1a 100644 --- a/src/xenia/debug/ui/turbo_badger_renderer.h +++ b/src/xenia/debug/ui/turbo_badger_renderer.h @@ -10,29 +10,31 @@ #ifndef XENIA_DEBUG_UI_TURBO_BADGER_RENDERER_H_ #define XENIA_DEBUG_UI_TURBO_BADGER_RENDERER_H_ +#include #include #include "xenia/ui/gl/circular_buffer.h" +#include "xenia/ui/gl/gl_context.h" #include "xenia/ui/gl/gl.h" -#include "third_party/turbobadger/src/tb/tb_renderer.h" -#include "third_party/turbobadger/src/tb/tb_renderer_batcher.h" +#include "third_party/turbobadger/src/tb/graphics/batching_renderer.h" namespace xe { namespace debug { namespace ui { -class TBRendererGL4 : public tb::RendererBatcher { +class TBRendererGL4 : public tb::graphics::BatchingRenderer { public: - TBRendererGL4(); + TBRendererGL4(xe::ui::gl::GLContext* context); ~TBRendererGL4() override; - static std::unique_ptr Create(); + static std::unique_ptr Create(xe::ui::gl::GLContext* context); void BeginPaint(int render_target_w, int render_target_h) override; void EndPaint() override; - tb::Bitmap* CreateBitmap(int width, int height, uint32_t* data) override; + tb::graphics::Bitmap* CreateBitmap(int width, int height, + uint32_t* data) override; void RenderBatch(Batch* batch) override; void SetClipRect(const tb::Rect& rect) override; @@ -43,6 +45,8 @@ class TBRendererGL4 : public tb::RendererBatcher { bool Initialize(); void Flush(); + xe::ui::gl::GLContext* context_ = nullptr; + GLuint program_ = 0; GLuint vao_ = 0; xe::ui::gl::CircularBuffer vertex_buffer_; diff --git a/third_party/turbobadger b/third_party/turbobadger index 37c7b5d81..5075e6995 160000 --- a/third_party/turbobadger +++ b/third_party/turbobadger @@ -1 +1 @@ -Subproject commit 37c7b5d818a2e7a38a555e326ceb6cd4daf63a17 +Subproject commit 5075e6995e7946c6d14cd86a813281f19f5853a9