diff --git a/.gitmodules b/.gitmodules
index 51b659e4d..aa8fa6ebd 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -25,6 +25,6 @@
[submodule "third_party/libav-xma-bin"]
path = third_party/libav-xma-bin
url = https://github.com/xenia-project/libav-xma-bin.git
-[submodule "third_party/turbobadger"]
- path = third_party/turbobadger
- url = https://github.com/xenia-project/turbobadger.git
+[submodule "third_party/elemental-forms"]
+ path = third_party/elemental-forms
+ url = https://github.com/xenia-project/elemental-forms.git
diff --git a/build/Xenia.Cpp.x64.Common.props b/build/Xenia.Cpp.x64.Common.props
index cdf59d98d..0124f7ef7 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\turbobadger\src\;$(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)third_party\elemental-forms\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/libxenia.vcxproj b/libxenia.vcxproj
index b2dfafd35..7c620cc22 100644
--- a/libxenia.vcxproj
+++ b/libxenia.vcxproj
@@ -166,11 +166,13 @@
+
+
@@ -394,6 +396,7 @@
+
@@ -401,6 +404,7 @@
+
@@ -556,7 +560,7 @@
true
- libxenia-base.lib;libavcodec.a;libavutil.a;libgflags.lib;libglew.lib
+ libelemental.lib;libxenia-base.lib;libavcodec.a;libavutil.a;libgflags.lib;libglew.lib
$(SolutionDir)third_party\libav-xma-bin\lib\Debug;%(AdditionalLibraryDirectories)
@@ -573,7 +577,7 @@
true
- libxenia-base.lib;libavcodec.a;libavutil.a;libgflags.lib;libglew.lib
+ libelemental.lib;libxenia-base.lib;libavcodec.a;libavutil.a;libgflags.lib;libglew.lib
@@ -592,7 +596,7 @@
true
- libxenia-base.lib;libavcodec.a;libavutil.a;libgflags.lib;libglew.lib
+ libelemental.lib;libxenia-base.lib;libavcodec.a;libavutil.a;libgflags.lib;libglew.lib
diff --git a/libxenia.vcxproj.filters b/libxenia.vcxproj.filters
index d15ccd065..c71877768 100644
--- a/libxenia.vcxproj.filters
+++ b/libxenia.vcxproj.filters
@@ -730,6 +730,12 @@
src\xenia\ui\gl
+
+ src\xenia\ui
+
+
+ src\xenia\ui\gl
+
@@ -1374,6 +1380,12 @@
src\xenia\ui\gl
+
+ src\xenia\ui
+
+
+ src\xenia\ui\gl
+
diff --git a/src/xenia/debug/ui/application.cc b/src/xenia/debug/ui/application.cc
index 3e03aebd2..8a0928ffb 100644
--- a/src/xenia/debug/ui/application.cc
+++ b/src/xenia/debug/ui/application.cc
@@ -9,6 +9,9 @@
#include "xenia/debug/ui/application.h"
+#include "el/message_handler.h"
+#include "el/util/metrics.h"
+#include "el/util/timer.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/platform.h"
@@ -16,10 +19,6 @@
#include "xenia/debug/ui/main_window.h"
#include "xenia/profiling.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 {
namespace ui {
@@ -82,16 +81,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::util::RescheduleTimer(uint64_t fire_time) {
- if (fire_time == tb::MessageHandler::kNotSoon) {
+void el::util::RescheduleTimer(uint64_t fire_time) {
+ if (fire_time == el::MessageHandler::kNotSoon) {
return;
}
- uint64_t now = tb::util::GetTimeMS();
+ uint64_t now = el::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::util::GetTimeMS();
+ uint64_t next_fire_time = el::MessageHandler::GetNextMessageFireTime();
+ uint64_t now = el::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
@@ -100,11 +99,11 @@ void tb::util::RescheduleTimer(uint64_t fire_time) {
return;
}
- tb::MessageHandler::ProcessMessages();
+ el::MessageHandler::ProcessMessages();
// 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::util::RescheduleTimer(tb::MessageHandler::GetNextMessageFireTime());
+ el::util::RescheduleTimer(el::MessageHandler::GetNextMessageFireTime());
}, delay_millis);
}
diff --git a/src/xenia/debug/ui/main_window.cc b/src/xenia/debug/ui/main_window.cc
index 147deca70..1e4890a01 100644
--- a/src/xenia/debug/ui/main_window.cc
+++ b/src/xenia/debug/ui/main_window.cc
@@ -13,7 +13,9 @@
#include "xenia/base/logging.h"
#include "xenia/base/platform.h"
#include "xenia/base/threading.h"
-#include "xenia/debug/ui/turbo_badger_control.h"
+
+// TODO(benvanik): platform based.
+#include "xenia/ui/gl/wgl_elemental_control.h"
namespace xe {
namespace debug {
@@ -89,7 +91,7 @@ bool MainWindow::Initialize() {
// Setup the GL control that actually does the drawing.
// We run here in the loop and only touch it (and its context) on this
// thread. That means some sync-fu when we want to swap.
- control_ = std::make_unique(loop());
+ control_ = std::make_unique(loop());
AddChild(control_.get());
Resize(1440, 1200);
diff --git a/src/xenia/debug/ui/main_window.h b/src/xenia/debug/ui/main_window.h
index cd82e495e..2aded1926 100644
--- a/src/xenia/debug/ui/main_window.h
+++ b/src/xenia/debug/ui/main_window.h
@@ -13,6 +13,7 @@
#include
#include "xenia/debug/ui/application.h"
+#include "xenia/ui/elemental_control.h"
#include "xenia/ui/platform.h"
#include "xenia/ui/window.h"
@@ -20,8 +21,6 @@ namespace xe {
namespace debug {
namespace ui {
-class TurboBadgerControl;
-
class MainWindow : public xe::ui::PlatformWindow {
public:
MainWindow(Application* app);
@@ -37,7 +36,7 @@ class MainWindow : public xe::ui::PlatformWindow {
Application* app_ = nullptr;
xe::ui::PlatformMenu main_menu_;
- std::unique_ptr control_;
+ std::unique_ptr control_;
};
} // namespace ui
diff --git a/src/xenia/debug/ui/turbo_badger_control.cc b/src/xenia/debug/ui/turbo_badger_control.cc
deleted file mode 100644
index 28e8ae07e..000000000
--- a/src/xenia/debug/ui/turbo_badger_control.cc
+++ /dev/null
@@ -1,1322 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2015 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#include "xenia/debug/ui/turbo_badger_control.h"
-
-#include "xenia/base/assert.h"
-#include "xenia/base/clock.h"
-#include "xenia/base/logging.h"
-#include "xenia/debug/ui/turbo_badger_renderer.h"
-
-// TODO(benvanik): remove this.
-#include "xenia/debug/ui/application.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_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 {
-namespace ui {
-
-constexpr bool kContinuousRepaint = false;
-
-// Enables long press behaviors (context menu, etc).
-constexpr bool kTouch = false;
-
-constexpr uint64_t kDoubleClickDelayMillis = 600;
-constexpr double kDoubleClickDistance = 5;
-
-constexpr int32_t kMouseWheelDetent = 120;
-
-class RootElement : public tb::Element {
- public:
- RootElement(TurboBadgerControl* owner) : owner_(owner) {}
- void OnInvalid() override { owner_->Invalidate(); }
-
- private:
- TurboBadgerControl* owner_ = nullptr;
-};
-
-bool TurboBadgerControl::InitializeTurboBadger(
- tb::graphics::Renderer* renderer) {
- static bool has_initialized = false;
- if (has_initialized) {
- return true;
- }
- has_initialized = true;
-
- if (!tb::Initialize(
- renderer,
- "third_party/turbobadger/resources/language/lng_en.tb.txt")) {
- XELOGE("Failed to initialize turbobadger core");
- return false;
- }
-
- // Load the default skin, and override skin that contains the graphics
- // specific to the demo.
- 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");
- return false;
- }
-
-// Register font renderers.
-#ifdef TB_FONT_RENDERER_TBBF
- void register_tbbf_font_renderer();
- register_tbbf_font_renderer();
-#endif
-#ifdef TB_FONT_RENDERER_STB
- void register_stb_font_renderer();
- register_stb_font_renderer();
-#endif
-#ifdef TB_FONT_RENDERER_FREETYPE
- 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)
- font_manager->AddFontInfo("third_party/turbobadger/resources/vera.ttf",
- "Default");
-#endif
-#ifdef TB_FONT_RENDERER_TBBF
- font_manager->AddFontInfo(
- "third_party/turbobadger/resources/default_font/"
- "segoe_white_with_shadow.tb.txt",
- "Default");
-#endif
-
- // Set the default font description for elements to one of the fonts we just
- // added.
- tb::FontDescription 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 =
- font_manager->CreateFontFace(font_manager->GetDefaultFontDescription());
- return true;
-}
-
-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(context());
-
- if (!InitializeTurboBadger(renderer_.get())) {
- XELOGE("Unable to initialize turbobadger");
- return false;
- }
-
- // TODO(benvanik): setup elements.
- root_element_ = std::make_unique(this);
- root_element_->SetSkinBg(TBIDC("background"));
- root_element_->set_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_element(), TBIDC(""));
- message_window->Show("Title", "Hello!");
-
- // tb::ShowDebugInfoSettingsWindow(root_element());
-
- return true;
-}
-
-void TurboBadgerControl::Destroy() {
- tb::Shutdown();
- super::Destroy();
-}
-
-void TurboBadgerControl::OnLayout(xe::ui::UIEvent& e) {
- super::OnLayout(e);
- if (!root_element()) {
- return;
- }
- // TODO(benvanik): subregion?
- root_element()->set_rect({0, 0, width(), height()});
-}
-
-void TurboBadgerControl::OnPaint(xe::ui::UIEvent& e) {
- super::OnPaint(e);
- if (!root_element()) {
- return;
- }
-
- ++frame_count_;
- ++fps_frame_count_;
- uint64_t now_ns = xe::Clock::QueryHostSystemTime();
- if (now_ns > fps_update_time_ + 1000 * 10000) {
- fps_ = uint32_t(fps_frame_count_ /
- (double(now_ns - fps_update_time_) / 10000000.0));
- fps_update_time_ = now_ns;
- fps_frame_count_ = 0;
- }
-
- // Update TB (run animations, handle deferred input, etc).
- tb::AnimationManager::Update();
- root_element()->InvokeProcessStates();
- root_element()->InvokeProcess();
-
- renderer()->BeginPaint(width(), height());
-
- // Render entire control hierarchy.
- root_element()->InvokePaint(tb::Element::PaintProps());
-
- // Render debug overlay.
- root_element()->GetFont()->DrawString(
- 5, 5, tb::Color(255, 0, 0),
- tb::util::format_string("Frame %lld", frame_count_));
- if (kContinuousRepaint) {
- 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_element()->Invalidate();
- }
- if (kContinuousRepaint) {
- // Force an immediate repaint, always.
- root_element()->Invalidate();
- }
-}
-
-void TurboBadgerControl::OnGotFocus(xe::ui::UIEvent& e) {
- super::OnGotFocus(e);
-}
-
-void TurboBadgerControl::OnLostFocus(xe::ui::UIEvent& e) {
- super::OnLostFocus(e);
- modifier_shift_pressed_ = false;
- modifier_cntrl_pressed_ = false;
- modifier_alt_pressed_ = false;
- modifier_super_pressed_ = false;
- last_click_time_ = 0;
-}
-
-tb::ModifierKeys TurboBadgerControl::GetModifierKeys() {
- auto modifiers = tb::ModifierKeys::kNone;
- if (modifier_shift_pressed_) {
- modifiers |= tb::ModifierKeys::kShift;
- }
- if (modifier_cntrl_pressed_) {
- modifiers |= tb::ModifierKeys::kCtrl;
- }
- if (modifier_alt_pressed_) {
- modifiers |= tb::ModifierKeys::kAlt;
- }
- if (modifier_super_pressed_) {
- modifiers |= tb::ModifierKeys::kSuper;
- }
- return modifiers;
-}
-
-void TurboBadgerControl::OnKeyPress(xe::ui::KeyEvent& e, bool is_down) {
- if (!root_element()) {
- return;
- }
- auto special_key = tb::SpecialKey::kUndefined;
- switch (e.key_code()) {
- case 38:
- special_key = tb::SpecialKey::kUp;
- break;
- case 39:
- special_key = tb::SpecialKey::kRight;
- break;
- case 40:
- special_key = tb::SpecialKey::kDown;
- break;
- case 37:
- special_key = tb::SpecialKey::kLeft;
- break;
- case 112:
- special_key = tb::SpecialKey::kF1;
- break;
- case 113:
- special_key = tb::SpecialKey::kF2;
- break;
- case 114:
- special_key = tb::SpecialKey::kF3;
- break;
- case 115:
- special_key = tb::SpecialKey::kF4;
- break;
- case 116:
- special_key = tb::SpecialKey::kF5;
- break;
- case 117:
- special_key = tb::SpecialKey::kF6;
- break;
- case 118:
- special_key = tb::SpecialKey::kF7;
- break;
- case 119:
- special_key = tb::SpecialKey::kF8;
- break;
- case 120:
- special_key = tb::SpecialKey::kF9;
- break;
- case 121:
- special_key = tb::SpecialKey::kF10;
- break;
- case 122:
- special_key = tb::SpecialKey::kF11;
- break;
- case 123:
- special_key = tb::SpecialKey::kF12;
- break;
- case 33:
- special_key = tb::SpecialKey::kPageUp;
- break;
- case 34:
- special_key = tb::SpecialKey::kPageDown;
- break;
- case 36:
- special_key = tb::SpecialKey::kHome;
- break;
- case 35:
- special_key = tb::SpecialKey::kEnd;
- break;
- case 45:
- special_key = tb::SpecialKey::kInsert;
- break;
- case 9:
- special_key = tb::SpecialKey::kTab;
- break;
- case 46:
- special_key = tb::SpecialKey::kDelete;
- break;
- case 8:
- special_key = tb::SpecialKey::kBackspace;
- break;
- case 13:
- special_key = tb::SpecialKey::kEnter;
- break;
- case 27:
- special_key = tb::SpecialKey::kEsc;
- break;
- case 93:
- if (!is_down && tb::Element::focused_element) {
- tb::ElementEvent ev(tb::EventType::kContextMenu);
- ev.modifierkeys = GetModifierKeys();
- tb::Element::focused_element->InvokeEvent(ev);
- e.set_handled(true);
- return;
- }
- break;
- case 16:
- modifier_shift_pressed_ = is_down;
- break;
- case 17:
- modifier_cntrl_pressed_ = is_down;
- break;
- // case xx:
- // // alt ??
- // modifier_alt_pressed_ = is_down;
- // break;
- case 91:
- modifier_super_pressed_ = is_down;
- break;
- }
-
- if (!CheckShortcutKey(e, special_key, is_down)) {
- e.set_handled(root_element()->InvokeKey(
- special_key != tb::SpecialKey::kUndefined ? e.key_code() : 0,
- special_key, GetModifierKeys(), is_down));
- }
-}
-
-bool TurboBadgerControl::CheckShortcutKey(xe::ui::KeyEvent& e,
- tb::SpecialKey special_key,
- bool is_down) {
- bool shortcut_key = modifier_cntrl_pressed_;
- if (!tb::Element::focused_element || !is_down || !shortcut_key) {
- return false;
- }
- bool reverse_key = modifier_shift_pressed_;
- int upper_key = e.key_code();
- if (upper_key >= 'a' && upper_key <= 'z') {
- upper_key += 'A' - 'a';
- }
- tb::TBID id;
- if (upper_key == 'X') {
- id = TBIDC("cut");
- } else if (upper_key == 'C' || special_key == tb::SpecialKey::kInsert) {
- id = TBIDC("copy");
- } else if (upper_key == 'V' ||
- (special_key == tb::SpecialKey::kInsert && reverse_key)) {
- id = TBIDC("paste");
- } else if (upper_key == 'A') {
- id = TBIDC("selectall");
- } else if (upper_key == 'Z' || upper_key == 'Y') {
- bool undo = upper_key == 'Z';
- if (reverse_key) {
- undo = !undo;
- }
- id = undo ? TBIDC("undo") : TBIDC("redo");
- } else if (upper_key == 'N') {
- id = TBIDC("new");
- } else if (upper_key == 'O') {
- id = TBIDC("open");
- } else if (upper_key == 'S') {
- id = TBIDC("save");
- } else if (upper_key == 'W') {
- id = TBIDC("close");
- } else if (special_key == tb::SpecialKey::kPageUp) {
- id = TBIDC("prev_doc");
- } else if (special_key == tb::SpecialKey::kPageDown) {
- id = TBIDC("next_doc");
- } else {
- return false;
- }
-
- tb::ElementEvent ev(tb::EventType::kShortcut);
- ev.modifierkeys = GetModifierKeys();
- ev.ref_id = id;
- if (!tb::Element::focused_element->InvokeEvent(ev)) {
- return false;
- }
- e.set_handled(true);
- return true;
-}
-
-void TurboBadgerControl::OnKeyDown(xe::ui::KeyEvent& e) {
- super::OnKeyDown(e);
- OnKeyPress(e, true);
-}
-
-void TurboBadgerControl::OnKeyUp(xe::ui::KeyEvent& e) {
- super::OnKeyUp(e);
- OnKeyPress(e, false);
-}
-
-void TurboBadgerControl::OnMouseDown(xe::ui::MouseEvent& e) {
- super::OnMouseDown(e);
- if (!root_element()) {
- return;
- }
- // TODO(benvanik): more button types.
- if (e.button() == xe::ui::MouseEvent::Button::kLeft) {
- // Simulated click count support.
- // TODO(benvanik): move into Control?
- uint64_t now = xe::Clock::QueryHostUptimeMillis();
- if (now < last_click_time_ + kDoubleClickDelayMillis) {
- double distance_moved = std::sqrt(std::pow(e.x() - last_click_x_, 2.0) +
- std::pow(e.y() - last_click_y_, 2.0));
- if (distance_moved < kDoubleClickDistance) {
- ++last_click_counter_;
- } else {
- last_click_counter_ = 1;
- }
- } else {
- last_click_counter_ = 1;
- }
- last_click_x_ = e.x();
- last_click_y_ = e.y();
- last_click_time_ = now;
-
- 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_element()) {
- return;
- }
- 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_element()) {
- return;
- }
- if (e.button() == xe::ui::MouseEvent::Button::kLeft) {
- e.set_handled(root_element()->InvokePointerUp(e.x(), e.y(),
- GetModifierKeys(), kTouch));
- } else if (e.button() == xe::ui::MouseEvent::Button::kRight) {
- root_element()->InvokePointerMove(e.x(), e.y(), GetModifierKeys(), kTouch);
- if (tb::Element::hovered_element) {
- int x = e.x();
- int y = e.y();
- 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);
- }
-}
-
-void TurboBadgerControl::OnMouseWheel(xe::ui::MouseEvent& e) {
- super::OnMouseWheel(e);
- if (!root_element()) {
- return;
- }
- e.set_handled(root_element()->InvokeWheel(
- e.x(), e.y(), e.dx(), -e.dy() / kMouseWheelDetent, GetModifierKeys()));
-}
-
-} // namespace ui
-} // namespace debug
-} // namespace xe
diff --git a/src/xenia/debug/ui/turbo_badger_control.h b/src/xenia/debug/ui/turbo_badger_control.h
deleted file mode 100644
index 8d670b52a..000000000
--- a/src/xenia/debug/ui/turbo_badger_control.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2015 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#ifndef XENIA_DEBUG_UI_TURBO_BADGER_CONTROL_H_
-#define XENIA_DEBUG_UI_TURBO_BADGER_CONTROL_H_
-
-#include
-
-#include "xenia/ui/gl/wgl_control.h"
-#include "xenia/ui/loop.h"
-
-#include "third_party/turbobadger/src/tb/element.h"
-
-namespace xe {
-namespace debug {
-namespace ui {
-
-class TurboBadgerControl : public xe::ui::gl::WGLControl {
- public:
- TurboBadgerControl(xe::ui::Loop* loop);
- ~TurboBadgerControl() override;
-
- static bool InitializeTurboBadger(tb::graphics::Renderer* renderer);
- static void ShutdownTurboBadger();
-
- tb::graphics::Renderer* renderer() const { return renderer_.get(); }
- tb::Element* root_element() const { return root_element_.get(); }
-
- protected:
- using super = xe::ui::gl::WGLControl;
-
- bool Create() override;
- void Destroy() override;
-
- void OnLayout(xe::ui::UIEvent& e) override;
- void OnPaint(xe::ui::UIEvent& e) override;
-
- void OnGotFocus(xe::ui::UIEvent& e) override;
- void OnLostFocus(xe::ui::UIEvent& e) override;
-
- tb::ModifierKeys GetModifierKeys();
-
- void OnKeyPress(xe::ui::KeyEvent& e, bool is_down);
- bool CheckShortcutKey(xe::ui::KeyEvent& e, tb::SpecialKey special_key,
- bool is_down);
- void OnKeyDown(xe::ui::KeyEvent& e) override;
- void OnKeyUp(xe::ui::KeyEvent& e) override;
-
- void OnMouseDown(xe::ui::MouseEvent& e) override;
- void OnMouseMove(xe::ui::MouseEvent& e) override;
- void OnMouseUp(xe::ui::MouseEvent& e) override;
- void OnMouseWheel(xe::ui::MouseEvent& e) override;
-
- std::unique_ptr renderer_;
- std::unique_ptr root_element_;
-
- uint32_t frame_count_ = 0;
- uint32_t fps_ = 0;
- uint64_t fps_update_time_ = 0;
- uint64_t fps_frame_count_ = 0;
-
- bool modifier_shift_pressed_ = false;
- bool modifier_cntrl_pressed_ = false;
- bool modifier_alt_pressed_ = false;
- bool modifier_super_pressed_ = false;
- uint64_t last_click_time_ = 0;
- int last_click_x_ = 0;
- int last_click_y_ = 0;
- int last_click_counter_ = 0;
-};
-
-} // namespace ui
-} // namespace debug
-} // namespace xe
-
-#endif // XENIA_DEBUG_UI_TURBO_BADGER_CONTROL_H_
diff --git a/src/xenia/debug/ui/turbo_badger_renderer.h b/src/xenia/debug/ui/turbo_badger_renderer.h
deleted file mode 100644
index 48e874c1a..000000000
--- a/src/xenia/debug/ui/turbo_badger_renderer.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2015 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#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/graphics/batching_renderer.h"
-
-namespace xe {
-namespace debug {
-namespace ui {
-
-class TBRendererGL4 : public tb::graphics::BatchingRenderer {
- public:
- TBRendererGL4(xe::ui::gl::GLContext* context);
- ~TBRendererGL4() override;
-
- 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::graphics::Bitmap* CreateBitmap(int width, int height,
- uint32_t* data) override;
-
- void RenderBatch(Batch* batch) override;
- void SetClipRect(const tb::Rect& rect) override;
-
- private:
- class TBBitmapGL4;
-
- bool Initialize();
- void Flush();
-
- xe::ui::gl::GLContext* context_ = nullptr;
-
- GLuint program_ = 0;
- GLuint vao_ = 0;
- xe::ui::gl::CircularBuffer vertex_buffer_;
-
- static const size_t kMaxCommands = 512;
- struct {
- GLenum prim_type;
- size_t vertex_offset;
- size_t vertex_count;
- } draw_commands_[kMaxCommands] = {0};
- uint32_t draw_command_count_ = 0;
- TBBitmapGL4* current_bitmap_ = nullptr;
-};
-
-} // namespace ui
-} // namespace debug
-} // namespace xe
-
-#endif // XENIA_DEBUG_UI_TURBO_BADGER_RENDERER_H_
diff --git a/src/xenia/debug/ui/xe-debug-ui.vcxproj b/src/xenia/debug/ui/xe-debug-ui.vcxproj
index 43210310c..ebac6c8f5 100644
--- a/src/xenia/debug/ui/xe-debug-ui.vcxproj
+++ b/src/xenia/debug/ui/xe-debug-ui.vcxproj
@@ -77,15 +77,11 @@
-
-
-
-
diff --git a/src/xenia/debug/ui/xe-debug-ui.vcxproj.filters b/src/xenia/debug/ui/xe-debug-ui.vcxproj.filters
index 13e4aef1f..eabdda76d 100644
--- a/src/xenia/debug/ui/xe-debug-ui.vcxproj.filters
+++ b/src/xenia/debug/ui/xe-debug-ui.vcxproj.filters
@@ -16,6 +16,9 @@
{9a5724c2-5473-4d53-93b4-26531201f38d}
+
+ {f0ac4999-4700-4b41-b73d-c6dc8b23c5e6}
+
@@ -27,12 +30,6 @@
src\xenia\debug\ui
-
- src\xenia\debug\ui
-
-
- src\xenia\debug\ui
-
@@ -47,11 +44,5 @@
src\xenia\debug\ui
-
- src\xenia\debug\ui
-
-
- src\xenia\debug\ui
-
\ No newline at end of file
diff --git a/src/xenia/ui/control.h b/src/xenia/ui/control.h
index 8c13866a8..becdc17b6 100644
--- a/src/xenia/ui/control.h
+++ b/src/xenia/ui/control.h
@@ -87,7 +87,7 @@ class Control {
protected:
explicit Control(uint32_t flags);
- virtual bool Create() = 0;
+ virtual bool Create() { return true; }
virtual void Destroy() {}
virtual void OnCreate() {}
diff --git a/src/xenia/ui/elemental_control.cc b/src/xenia/ui/elemental_control.cc
new file mode 100644
index 000000000..1033f956f
--- /dev/null
+++ b/src/xenia/ui/elemental_control.cc
@@ -0,0 +1,478 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2015 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#include "xenia/ui/elemental_control.h"
+
+#include "el/animation_manager.h"
+#include "el/elemental_forms.h"
+#include "el/text/font_manager.h"
+#include "xenia/base/assert.h"
+#include "xenia/base/clock.h"
+#include "xenia/base/logging.h"
+
+namespace xe {
+namespace ui {
+
+constexpr bool kContinuousRepaint = false;
+
+// Enables long press behaviors (context menu, etc).
+constexpr bool kTouch = false;
+
+constexpr uint64_t kDoubleClickDelayMillis = 600;
+constexpr double kDoubleClickDistance = 5;
+
+constexpr int32_t kMouseWheelDetent = 120;
+
+class RootElement : public el::Element {
+ public:
+ RootElement(ElementalControl* owner) : owner_(owner) {}
+ void OnInvalid() override { owner_->Invalidate(); }
+
+ private:
+ ElementalControl* owner_ = nullptr;
+};
+
+bool ElementalControl::InitializeElemental(el::graphics::Renderer* renderer) {
+ static bool has_initialized = false;
+ if (has_initialized) {
+ return true;
+ }
+ has_initialized = true;
+
+ if (!el::Initialize(
+ renderer,
+ "third_party/turbobadger/resources/language/lng_en.tb.txt")) {
+ XELOGE("Failed to initialize turbobadger core");
+ return false;
+ }
+
+ // Load the default skin, and override skin that contains the graphics
+ // specific to the demo.
+ if (!el::Skin::get()->Load(
+ "third_party/elemental-forms/resources/default_skin/skin.tb.txt",
+ "third_party/elemental-forms/testbed/resources/skin/skin.tb.txt")) {
+ XELOGE("Failed to load turbobadger skin");
+ return false;
+ }
+
+// Register font renderers.
+#ifdef EL_FONT_RENDERER_TBBF
+ void register_tbbf_font_renderer();
+ register_tbbf_font_renderer();
+#endif
+#ifdef EL_FONT_RENDERER_STB
+ void register_stb_font_renderer();
+ register_stb_font_renderer();
+#endif
+#ifdef EL_FONT_RENDERER_FREETYPE
+ void register_freetype_font_renderer();
+ register_freetype_font_renderer();
+#endif
+ auto font_manager = el::text::FontManager::get();
+
+// Add fonts we can use to the font manager.
+#if defined(EL_FONT_RENDERER_STB) || defined(EL_FONT_RENDERER_FREETYPE)
+ font_manager->AddFontInfo("third_party/elemental-forms/resources/vera.ttf",
+ "Default");
+#endif
+#ifdef EL_FONT_RENDERER_TBBF
+ font_manager->AddFontInfo(
+ "third_party/elemental-forms/resources/default_font/"
+ "segoe_white_with_shadow.tb.txt",
+ "Default");
+#endif
+
+ // Set the default font description for elements to one of the fonts we just
+ // added.
+ el::FontDescription fd;
+ fd.set_id(TBIDC("Default"));
+ fd.set_size(el::Skin::get()->dimension_converter()->DpToPx(14));
+ font_manager->set_default_font_description(fd);
+
+ // Create the font now.
+ auto font =
+ font_manager->CreateFontFace(font_manager->default_font_description());
+ return true;
+}
+
+ElementalControl::ElementalControl(Loop* loop, uint32_t flags) : super(flags) {}
+
+ElementalControl::~ElementalControl() = default;
+
+bool ElementalControl::Create() {
+ if (!super::Create()) {
+ return false;
+ }
+
+ // Create subclass renderer (GL, etc).
+ renderer_ = CreateRenderer();
+
+ // Initialize elemental.
+ // TODO(benvanik): once? Do we care about multiple controls?
+ if (!InitializeElemental(renderer_.get())) {
+ XELOGE("Unable to initialize turbobadger");
+ return false;
+ }
+
+ // TODO(benvanik): setup elements.
+ root_element_ = std::make_unique(this);
+ root_element_->set_background_skin(TBIDC("background"));
+ root_element_->set_rect({0, 0, 1000, 1000});
+
+ // Block animations during init.
+ el::AnimationBlocker anim_blocker;
+
+ // TODO(benvanik): dummy UI.
+ auto message_window = new el::MessageWindow(root_element(), TBIDC(""));
+ message_window->Show("Title", "Hello!");
+
+ // el::ShowDebugInfoSettingsWindow(root_element());
+
+ return true;
+}
+
+void ElementalControl::Destroy() {
+ el::Shutdown();
+ super::Destroy();
+}
+
+void ElementalControl::OnLayout(UIEvent& e) {
+ super::OnLayout(e);
+ if (!root_element()) {
+ return;
+ }
+ // TODO(benvanik): subregion?
+ root_element()->set_rect({0, 0, width(), height()});
+}
+
+void ElementalControl::OnPaint(UIEvent& e) {
+ super::OnPaint(e);
+ if (!root_element()) {
+ return;
+ }
+
+ ++frame_count_;
+ ++fps_frame_count_;
+ uint64_t now_ns = xe::Clock::QueryHostSystemTime();
+ if (now_ns > fps_update_time_ + 1000 * 10000) {
+ fps_ = uint32_t(fps_frame_count_ /
+ (double(now_ns - fps_update_time_) / 10000000.0));
+ fps_update_time_ = now_ns;
+ fps_frame_count_ = 0;
+ }
+
+ // Update TB (run animations, handle deferred input, etc).
+ el::AnimationManager::Update();
+ root_element()->InvokeProcessStates();
+ root_element()->InvokeProcess();
+
+ renderer()->BeginPaint(width(), height());
+
+ // Render entire control hierarchy.
+ root_element()->InvokePaint(el::Element::PaintProps());
+
+ // Render debug overlay.
+ root_element()->computed_font()->DrawString(
+ 5, 5, el::Color(255, 0, 0),
+ el::format_string("Frame %lld", frame_count_));
+ if (kContinuousRepaint) {
+ root_element()->computed_font()->DrawString(
+ 5, 20, el::Color(255, 0, 0), el::format_string("FPS: %d", fps_));
+ }
+
+ renderer()->EndPaint();
+
+ // If animations are running, reinvalidate immediately.
+ if (el::AnimationManager::has_running_animations()) {
+ root_element()->Invalidate();
+ }
+ if (kContinuousRepaint) {
+ // Force an immediate repaint, always.
+ root_element()->Invalidate();
+ }
+}
+
+void ElementalControl::OnGotFocus(UIEvent& e) { super::OnGotFocus(e); }
+
+void ElementalControl::OnLostFocus(UIEvent& e) {
+ super::OnLostFocus(e);
+ modifier_shift_pressed_ = false;
+ modifier_cntrl_pressed_ = false;
+ modifier_alt_pressed_ = false;
+ modifier_super_pressed_ = false;
+ last_click_time_ = 0;
+}
+
+el::ModifierKeys ElementalControl::GetModifierKeys() {
+ auto modifiers = el::ModifierKeys::kNone;
+ if (modifier_shift_pressed_) {
+ modifiers |= el::ModifierKeys::kShift;
+ }
+ if (modifier_cntrl_pressed_) {
+ modifiers |= el::ModifierKeys::kCtrl;
+ }
+ if (modifier_alt_pressed_) {
+ modifiers |= el::ModifierKeys::kAlt;
+ }
+ if (modifier_super_pressed_) {
+ modifiers |= el::ModifierKeys::kSuper;
+ }
+ return modifiers;
+}
+
+void ElementalControl::OnKeyPress(KeyEvent& e, bool is_down) {
+ if (!root_element()) {
+ return;
+ }
+ auto special_key = el::SpecialKey::kUndefined;
+ switch (e.key_code()) {
+ case 38:
+ special_key = el::SpecialKey::kUp;
+ break;
+ case 39:
+ special_key = el::SpecialKey::kRight;
+ break;
+ case 40:
+ special_key = el::SpecialKey::kDown;
+ break;
+ case 37:
+ special_key = el::SpecialKey::kLeft;
+ break;
+ case 112:
+ special_key = el::SpecialKey::kF1;
+ break;
+ case 113:
+ special_key = el::SpecialKey::kF2;
+ break;
+ case 114:
+ special_key = el::SpecialKey::kF3;
+ break;
+ case 115:
+ special_key = el::SpecialKey::kF4;
+ break;
+ case 116:
+ special_key = el::SpecialKey::kF5;
+ break;
+ case 117:
+ special_key = el::SpecialKey::kF6;
+ break;
+ case 118:
+ special_key = el::SpecialKey::kF7;
+ break;
+ case 119:
+ special_key = el::SpecialKey::kF8;
+ break;
+ case 120:
+ special_key = el::SpecialKey::kF9;
+ break;
+ case 121:
+ special_key = el::SpecialKey::kF10;
+ break;
+ case 122:
+ special_key = el::SpecialKey::kF11;
+ break;
+ case 123:
+ special_key = el::SpecialKey::kF12;
+ break;
+ case 33:
+ special_key = el::SpecialKey::kPageUp;
+ break;
+ case 34:
+ special_key = el::SpecialKey::kPageDown;
+ break;
+ case 36:
+ special_key = el::SpecialKey::kHome;
+ break;
+ case 35:
+ special_key = el::SpecialKey::kEnd;
+ break;
+ case 45:
+ special_key = el::SpecialKey::kInsert;
+ break;
+ case 9:
+ special_key = el::SpecialKey::kTab;
+ break;
+ case 46:
+ special_key = el::SpecialKey::kDelete;
+ break;
+ case 8:
+ special_key = el::SpecialKey::kBackspace;
+ break;
+ case 13:
+ special_key = el::SpecialKey::kEnter;
+ break;
+ case 27:
+ special_key = el::SpecialKey::kEsc;
+ break;
+ case 93:
+ if (!is_down && el::Element::focused_element) {
+ el::Event ev(el::EventType::kContextMenu);
+ ev.modifierkeys = GetModifierKeys();
+ el::Element::focused_element->InvokeEvent(ev);
+ e.set_handled(true);
+ return;
+ }
+ break;
+ case 16:
+ modifier_shift_pressed_ = is_down;
+ break;
+ case 17:
+ modifier_cntrl_pressed_ = is_down;
+ break;
+ // case xx:
+ // // alt ??
+ // modifier_alt_pressed_ = is_down;
+ // break;
+ case 91:
+ modifier_super_pressed_ = is_down;
+ break;
+ }
+
+ if (!CheckShortcutKey(e, special_key, is_down)) {
+ e.set_handled(root_element()->InvokeKey(
+ special_key != el::SpecialKey::kUndefined ? e.key_code() : 0,
+ special_key, GetModifierKeys(), is_down));
+ }
+}
+
+bool ElementalControl::CheckShortcutKey(KeyEvent& e, el::SpecialKey special_key,
+ bool is_down) {
+ bool shortcut_key = modifier_cntrl_pressed_;
+ if (!el::Element::focused_element || !is_down || !shortcut_key) {
+ return false;
+ }
+ bool reverse_key = modifier_shift_pressed_;
+ int upper_key = e.key_code();
+ if (upper_key >= 'a' && upper_key <= 'z') {
+ upper_key += 'A' - 'a';
+ }
+ el::TBID id;
+ if (upper_key == 'X') {
+ id = TBIDC("cut");
+ } else if (upper_key == 'C' || special_key == el::SpecialKey::kInsert) {
+ id = TBIDC("copy");
+ } else if (upper_key == 'V' ||
+ (special_key == el::SpecialKey::kInsert && reverse_key)) {
+ id = TBIDC("paste");
+ } else if (upper_key == 'A') {
+ id = TBIDC("selectall");
+ } else if (upper_key == 'Z' || upper_key == 'Y') {
+ bool undo = upper_key == 'Z';
+ if (reverse_key) {
+ undo = !undo;
+ }
+ id = undo ? TBIDC("undo") : TBIDC("redo");
+ } else if (upper_key == 'N') {
+ id = TBIDC("new");
+ } else if (upper_key == 'O') {
+ id = TBIDC("open");
+ } else if (upper_key == 'S') {
+ id = TBIDC("save");
+ } else if (upper_key == 'W') {
+ id = TBIDC("close");
+ } else if (special_key == el::SpecialKey::kPageUp) {
+ id = TBIDC("prev_doc");
+ } else if (special_key == el::SpecialKey::kPageDown) {
+ id = TBIDC("next_doc");
+ } else {
+ return false;
+ }
+
+ el::Event ev(el::EventType::kShortcut);
+ ev.modifierkeys = GetModifierKeys();
+ ev.ref_id = id;
+ if (!el::Element::focused_element->InvokeEvent(ev)) {
+ return false;
+ }
+ e.set_handled(true);
+ return true;
+}
+
+void ElementalControl::OnKeyDown(KeyEvent& e) {
+ super::OnKeyDown(e);
+ OnKeyPress(e, true);
+}
+
+void ElementalControl::OnKeyUp(KeyEvent& e) {
+ super::OnKeyUp(e);
+ OnKeyPress(e, false);
+}
+
+void ElementalControl::OnMouseDown(MouseEvent& e) {
+ super::OnMouseDown(e);
+ if (!root_element()) {
+ return;
+ }
+ // TODO(benvanik): more button types.
+ if (e.button() == MouseEvent::Button::kLeft) {
+ // Simulated click count support.
+ // TODO(benvanik): move into Control?
+ uint64_t now = xe::Clock::QueryHostUptimeMillis();
+ if (now < last_click_time_ + kDoubleClickDelayMillis) {
+ double distance_moved = std::sqrt(std::pow(e.x() - last_click_x_, 2.0) +
+ std::pow(e.y() - last_click_y_, 2.0));
+ if (distance_moved < kDoubleClickDistance) {
+ ++last_click_counter_;
+ } else {
+ last_click_counter_ = 1;
+ }
+ } else {
+ last_click_counter_ = 1;
+ }
+ last_click_x_ = e.x();
+ last_click_y_ = e.y();
+ last_click_time_ = now;
+
+ e.set_handled(root_element()->InvokePointerDown(
+ e.x(), e.y(), last_click_counter_, GetModifierKeys(), kTouch));
+ }
+}
+
+void ElementalControl::OnMouseMove(MouseEvent& e) {
+ super::OnMouseMove(e);
+ if (!root_element()) {
+ return;
+ }
+ root_element()->InvokePointerMove(e.x(), e.y(), GetModifierKeys(), kTouch);
+ e.set_handled(true);
+}
+
+void ElementalControl::OnMouseUp(MouseEvent& e) {
+ super::OnMouseUp(e);
+ if (!root_element()) {
+ return;
+ }
+ if (e.button() == MouseEvent::Button::kLeft) {
+ e.set_handled(root_element()->InvokePointerUp(e.x(), e.y(),
+ GetModifierKeys(), kTouch));
+ } else if (e.button() == MouseEvent::Button::kRight) {
+ root_element()->InvokePointerMove(e.x(), e.y(), GetModifierKeys(), kTouch);
+ if (el::Element::hovered_element) {
+ int x = e.x();
+ int y = e.y();
+ el::Element::hovered_element->ConvertFromRoot(x, y);
+ el::Event ev(el::EventType::kContextMenu, x, y, kTouch,
+ GetModifierKeys());
+ el::Element::hovered_element->InvokeEvent(ev);
+ }
+ e.set_handled(true);
+ }
+}
+
+void ElementalControl::OnMouseWheel(MouseEvent& e) {
+ super::OnMouseWheel(e);
+ if (!root_element()) {
+ return;
+ }
+ e.set_handled(root_element()->InvokeWheel(
+ e.x(), e.y(), e.dx(), -e.dy() / kMouseWheelDetent, GetModifierKeys()));
+}
+
+} // namespace ui
+} // namespace xe
diff --git a/src/xenia/ui/elemental_control.h b/src/xenia/ui/elemental_control.h
new file mode 100644
index 000000000..f39fa2c12
--- /dev/null
+++ b/src/xenia/ui/elemental_control.h
@@ -0,0 +1,80 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2015 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#ifndef XENIA_UI_ELEMENTAL_CONTROL_H_
+#define XENIA_UI_ELEMENTAL_CONTROL_H_
+
+#include
+
+#include "el/element.h"
+#include "el/graphics/renderer.h"
+#include "xenia/ui/control.h"
+#include "xenia/ui/loop.h"
+#include "xenia/ui/platform.h"
+
+namespace xe {
+namespace ui {
+
+class ElementalControl : public PlatformControl {
+ public:
+ ElementalControl(Loop* loop, uint32_t flags);
+ ~ElementalControl() override;
+
+ el::graphics::Renderer* renderer() const { return renderer_.get(); }
+ el::Element* root_element() const { return root_element_.get(); }
+
+ protected:
+ using super = PlatformControl;
+
+ bool InitializeElemental(el::graphics::Renderer* renderer);
+ virtual std::unique_ptr CreateRenderer() = 0;
+
+ bool Create() override;
+ void Destroy() override;
+
+ void OnLayout(UIEvent& e) override;
+ void OnPaint(UIEvent& e) override;
+
+ void OnGotFocus(UIEvent& e) override;
+ void OnLostFocus(UIEvent& e) override;
+
+ el::ModifierKeys GetModifierKeys();
+
+ void OnKeyPress(KeyEvent& e, bool is_down);
+ bool CheckShortcutKey(KeyEvent& e, el::SpecialKey special_key, bool is_down);
+ void OnKeyDown(KeyEvent& e) override;
+ void OnKeyUp(KeyEvent& e) override;
+
+ void OnMouseDown(MouseEvent& e) override;
+ void OnMouseMove(MouseEvent& e) override;
+ void OnMouseUp(MouseEvent& e) override;
+ void OnMouseWheel(MouseEvent& e) override;
+
+ std::unique_ptr renderer_;
+ std::unique_ptr root_element_;
+
+ uint32_t frame_count_ = 0;
+ uint32_t fps_ = 0;
+ uint64_t fps_update_time_ = 0;
+ uint64_t fps_frame_count_ = 0;
+
+ bool modifier_shift_pressed_ = false;
+ bool modifier_cntrl_pressed_ = false;
+ bool modifier_alt_pressed_ = false;
+ bool modifier_super_pressed_ = false;
+ uint64_t last_click_time_ = 0;
+ int last_click_x_ = 0;
+ int last_click_y_ = 0;
+ int last_click_counter_ = 0;
+};
+
+} // namespace ui
+} // namespace xe
+
+#endif // XENIA_UI_ELEMENTAL_CONTROL_H_
diff --git a/src/xenia/debug/ui/turbo_badger_renderer.cc b/src/xenia/ui/gl/wgl_elemental_control.cc
similarity index 53%
rename from src/xenia/debug/ui/turbo_badger_renderer.cc
rename to src/xenia/ui/gl/wgl_elemental_control.cc
index a40f9de39..b1093a194 100644
--- a/src/xenia/debug/ui/turbo_badger_renderer.cc
+++ b/src/xenia/ui/gl/wgl_elemental_control.cc
@@ -7,44 +7,195 @@
******************************************************************************
*/
-#include "xenia/debug/ui/turbo_badger_renderer.h"
+#include "xenia/ui/gl/wgl_elemental_control.h"
+#include
+
+#include "el/graphics/batching_renderer.h"
+#include "el/graphics/bitmap_fragment.h"
+#include "el/util/math.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
-
-#include "third_party/turbobadger/src/tb/graphics/bitmap_fragment.h"
-#include "third_party/turbobadger/src/tb/util/math.h"
+#include "xenia/profiling.h"
+#include "xenia/ui/gl/circular_buffer.h"
+#include "xenia/ui/gl/gl_context.h"
+#include "xenia/ui/gl/gl.h"
namespace xe {
-namespace debug {
namespace ui {
+namespace gl {
-using namespace tb;
-
-class TBRendererGL4::TBBitmapGL4 : public tb::graphics::Bitmap {
+class GL4BatchingRenderer : public el::graphics::BatchingRenderer {
public:
- TBBitmapGL4(xe::ui::gl::GLContext* context, TBRendererGL4* renderer);
- ~TBBitmapGL4();
+ GL4BatchingRenderer(GLContext* context);
+ ~GL4BatchingRenderer() override;
- bool Init(int width, int height, uint32_t* data);
- int Width() override { return width_; }
- int Height() override { return height_; }
- void SetData(uint32_t* data) override;
+ static std::unique_ptr Create(GLContext* context);
- xe::ui::gl::GLContext* context_ = nullptr;
- TBRendererGL4* renderer_ = nullptr;
- int width_ = 0;
- int height_ = 0;
- GLuint handle_ = 0;
- GLuint64 gpu_handle_ = 0;
+ void BeginPaint(int render_target_w, int render_target_h) override;
+ void EndPaint() override;
+
+ std::unique_ptr CreateBitmap(int width, int height,
+ uint32_t* data) override;
+
+ void RenderBatch(Batch* batch) override;
+ void set_clip_rect(const el::Rect& rect) override;
+
+ private:
+ class GL4Bitmap : public el::graphics::Bitmap {
+ public:
+ GL4Bitmap(GLContext* context, GL4BatchingRenderer* renderer);
+ ~GL4Bitmap();
+
+ bool Init(int width, int height, uint32_t* data);
+ int width() override { return width_; }
+ int height() override { return height_; }
+ void set_data(uint32_t* data) override;
+
+ GLContext* context_ = nullptr;
+ GL4BatchingRenderer* renderer_ = nullptr;
+ int width_ = 0;
+ int height_ = 0;
+ GLuint handle_ = 0;
+ GLuint64 gpu_handle_ = 0;
+ };
+
+ bool Initialize();
+ void Flush();
+
+ GLContext* context_ = nullptr;
+
+ GLuint program_ = 0;
+ GLuint vao_ = 0;
+ CircularBuffer vertex_buffer_;
+
+ static const size_t kMaxCommands = 512;
+ struct {
+ GLenum prim_type;
+ size_t vertex_offset;
+ size_t vertex_count;
+ } draw_commands_[kMaxCommands] = {0};
+ uint32_t draw_command_count_ = 0;
+ GL4Bitmap* current_bitmap_ = nullptr;
};
-TBRendererGL4::TBBitmapGL4::TBBitmapGL4(xe::ui::gl::GLContext* context,
- TBRendererGL4* renderer)
+WGLElementalControl::WGLElementalControl(Loop* loop)
+ : ElementalControl(loop, Flags::kFlagOwnPaint), loop_(loop) {}
+
+WGLElementalControl::~WGLElementalControl() = default;
+
+bool WGLElementalControl::Create() {
+ HINSTANCE hInstance = GetModuleHandle(nullptr);
+
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ wcex.lpfnWndProc = Win32Control::WndProcThunk;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = hInstance;
+ wcex.hIcon = nullptr;
+ wcex.hIconSm = nullptr;
+ wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wcex.lpszMenuName = nullptr;
+ wcex.lpszClassName = L"XeniaWglElementalClass";
+ if (!RegisterClassEx(&wcex)) {
+ XELOGE("WGL RegisterClassEx failed");
+ return false;
+ }
+
+ // Create window.
+ DWORD window_style = WS_CHILD | WS_VISIBLE | SS_NOTIFY;
+ DWORD window_ex_style = 0;
+ hwnd_ =
+ CreateWindowEx(window_ex_style, L"XeniaWglElementalClass", L"Xenia",
+ window_style, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, parent_hwnd(), nullptr, hInstance, this);
+ if (!hwnd_) {
+ XELOGE("WGL CreateWindow failed");
+ return false;
+ }
+
+ if (!context_.Initialize(hwnd_)) {
+ XEFATAL(
+ "Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
+ "you have the latest drivers for your GPU and that it supports OpenGL "
+ "4.5. See http://xenia.jp/faq/ for more information.");
+ return false;
+ }
+
+ context_.AssertExtensionsPresent();
+
+ SetFocus(hwnd_);
+
+ return super::Create();
+}
+
+std::unique_ptr WGLElementalControl::CreateRenderer() {
+ return GL4BatchingRenderer::Create(&context_);
+}
+
+void WGLElementalControl::OnLayout(UIEvent& e) {
+ Control::ResizeToFill();
+ super::OnLayout(e);
+}
+
+LRESULT WGLElementalControl::WndProc(HWND hWnd, UINT message, WPARAM wParam,
+ LPARAM lParam) {
+ switch (message) {
+ case WM_PAINT: {
+ invalidated_ = false;
+ ValidateRect(hWnd, nullptr);
+ SCOPE_profile_cpu_i("gpu", "xe::ui::gl::WGLElementalControl::WM_PAINT");
+ {
+ GLContextLock context_lock(&context_);
+
+ float clear_color[] = {rand() / (float)RAND_MAX, 1.0f, 0, 1.0f};
+ glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color);
+
+ if (current_paint_callback_) {
+ current_paint_callback_();
+ current_paint_callback_ = nullptr;
+ }
+
+ UIEvent e(this);
+ OnPaint(e);
+
+ // TODO(benvanik): profiler present.
+ Profiler::Present();
+ }
+ {
+ SCOPE_profile_cpu_i("gpu",
+ "xe::ui::gl::WGLElementalControl::SwapBuffers");
+ SwapBuffers(context_.dc());
+ }
+ return 0;
+ } break;
+ }
+ return Win32Control::WndProc(hWnd, message, wParam, lParam);
+}
+
+void WGLElementalControl::SynchronousRepaint(
+ std::function paint_callback) {
+ SCOPE_profile_cpu_f("gpu");
+
+ // We may already have a pending paint from a previous request when we
+ // were minimized. We just overwrite it.
+ current_paint_callback_ = std::move(paint_callback);
+
+ // This will not return until the WM_PAINT has completed.
+ // Note, if we are minimized this won't do anything.
+ RedrawWindow(hwnd(), nullptr, nullptr,
+ RDW_INTERNALPAINT | RDW_UPDATENOW | RDW_ALLCHILDREN);
+}
+
+GL4BatchingRenderer::GL4Bitmap::GL4Bitmap(GLContext* context,
+ GL4BatchingRenderer* renderer)
: context_(context), renderer_(renderer) {}
-TBRendererGL4::TBBitmapGL4::~TBBitmapGL4() {
- xe::ui::gl::GLContextLock lock(context_);
+GL4BatchingRenderer::GL4Bitmap::~GL4Bitmap() {
+ GLContextLock lock(context_);
// Must flush and unbind before we delete the texture.
renderer_->FlushBitmap(this);
@@ -52,9 +203,10 @@ TBRendererGL4::TBBitmapGL4::~TBBitmapGL4() {
glDeleteTextures(1, &handle_);
}
-bool TBRendererGL4::TBBitmapGL4::Init(int width, int height, uint32_t* data) {
- assert(width == tb::util::GetNearestPowerOfTwo(width));
- assert(height == tb::util::GetNearestPowerOfTwo(height));
+bool GL4BatchingRenderer::GL4Bitmap::Init(int width, int height,
+ uint32_t* data) {
+ assert(width == el::util::GetNearestPowerOfTwo(width));
+ assert(height == el::util::GetNearestPowerOfTwo(height));
width_ = width;
height_ = height;
@@ -68,32 +220,30 @@ bool TBRendererGL4::TBBitmapGL4::Init(int width, int height, uint32_t* data) {
gpu_handle_ = glGetTextureHandleARB(handle_);
glMakeTextureHandleResidentARB(gpu_handle_);
- SetData(data);
+ set_data(data);
return true;
}
-void TBRendererGL4::TBBitmapGL4::SetData(uint32_t* data) {
+void GL4BatchingRenderer::GL4Bitmap::set_data(uint32_t* data) {
renderer_->FlushBitmap(this);
glTextureSubImage2D(handle_, 0, 0, 0, width_, height_, GL_RGBA,
GL_UNSIGNED_BYTE, data);
}
-TBRendererGL4::TBRendererGL4(xe::ui::gl::GLContext* context)
- : context_(context),
- vertex_buffer_(graphics::BatchingRenderer::kVertexBatchSize *
- sizeof(Vertex)) {}
+GL4BatchingRenderer::GL4BatchingRenderer(GLContext* context)
+ : context_(context), vertex_buffer_(kVertexBatchSize * sizeof(Vertex)) {}
-TBRendererGL4::~TBRendererGL4() {
- xe::ui::gl::GLContextLock lock(context_);
+GL4BatchingRenderer::~GL4BatchingRenderer() {
+ GLContextLock lock(context_);
vertex_buffer_.Shutdown();
glDeleteVertexArrays(1, &vao_);
glDeleteProgram(program_);
}
-std::unique_ptr TBRendererGL4::Create(
- xe::ui::gl::GLContext* context) {
- auto renderer = std::make_unique(context);
+std::unique_ptr GL4BatchingRenderer::Create(
+ GLContext* context) {
+ auto renderer = std::make_unique(context);
if (!renderer->Initialize()) {
XELOGE("Failed to initialize TurboBadger GL4 renderer");
return nullptr;
@@ -101,7 +251,7 @@ std::unique_ptr TBRendererGL4::Create(
return renderer;
}
-bool TBRendererGL4::Initialize() {
+bool GL4BatchingRenderer::Initialize() {
if (!vertex_buffer_.Initialize()) {
XELOGE("Failed to initialize circular buffer");
return false;
@@ -188,23 +338,23 @@ void main() { \n\
return true;
}
-graphics::Bitmap* TBRendererGL4::CreateBitmap(int width, int height,
- uint32_t* data) {
- auto bitmap = std::make_unique(context_, this);
+std::unique_ptr GL4BatchingRenderer::CreateBitmap(
+ int width, int height, uint32_t* data) {
+ auto bitmap = std::make_unique(context_, this);
if (!bitmap->Init(width, height, data)) {
return nullptr;
}
- return bitmap.release();
+ return std::unique_ptr(bitmap.release());
}
-void TBRendererGL4::SetClipRect(const Rect& rect) {
+void GL4BatchingRenderer::set_clip_rect(const el::Rect& rect) {
Flush();
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) {
- tb::graphics::BatchingRenderer::BeginPaint(render_target_w, render_target_h);
+void GL4BatchingRenderer::BeginPaint(int render_target_w, int render_target_h) {
+ BatchingRenderer::BeginPaint(render_target_w, render_target_h);
glEnablei(GL_BLEND, 0);
glBlendFunci(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -237,8 +387,8 @@ void TBRendererGL4::BeginPaint(int render_target_w, int render_target_h) {
glBindVertexArray(vao_);
}
-void TBRendererGL4::EndPaint() {
- tb::graphics::BatchingRenderer::EndPaint();
+void GL4BatchingRenderer::EndPaint() {
+ BatchingRenderer::EndPaint();
Flush();
@@ -246,7 +396,7 @@ void TBRendererGL4::EndPaint() {
glBindVertexArray(0);
}
-void TBRendererGL4::Flush() {
+void GL4BatchingRenderer::Flush() {
if (!draw_command_count_) {
return;
}
@@ -261,8 +411,8 @@ void TBRendererGL4::Flush() {
vertex_buffer_.WaitUntilClean();
}
-void TBRendererGL4::RenderBatch(Batch* batch) {
- auto bitmap = static_cast(batch->bitmap);
+void GL4BatchingRenderer::RenderBatch(Batch* batch) {
+ auto bitmap = static_cast(batch->bitmap);
if (bitmap != current_bitmap_) {
current_bitmap_ = bitmap;
Flush();
@@ -300,6 +450,6 @@ void TBRendererGL4::RenderBatch(Batch* batch) {
vertex_buffer_.Commit(std::move(allocation));
}
+} // namespace gl
} // namespace ui
-} // namespace debug
} // namespace xe
diff --git a/src/xenia/ui/gl/wgl_elemental_control.h b/src/xenia/ui/gl/wgl_elemental_control.h
new file mode 100644
index 000000000..deb0627e7
--- /dev/null
+++ b/src/xenia/ui/gl/wgl_elemental_control.h
@@ -0,0 +1,55 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2014 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#ifndef XENIA_UI_GL_WGL_ELEMENTAL_CONTROL_H_
+#define XENIA_UI_GL_WGL_ELEMENTAL_CONTROL_H_
+
+#include
+
+#include "xenia/base/threading.h"
+#include "xenia/ui/elemental_control.h"
+#include "xenia/ui/gl/gl_context.h"
+#include "xenia/ui/loop.h"
+
+namespace xe {
+namespace ui {
+namespace gl {
+
+class WGLElementalControl : public ElementalControl {
+ public:
+ WGLElementalControl(Loop* loop);
+ ~WGLElementalControl() override;
+
+ GLContext* context() { return &context_; }
+
+ void SynchronousRepaint(std::function paint_callback);
+
+ protected:
+ using super = ElementalControl;
+
+ std::unique_ptr CreateRenderer() override;
+
+ bool Create() override;
+
+ void OnLayout(UIEvent& e) override;
+
+ LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam,
+ LPARAM lParam) override;
+
+ private:
+ Loop* loop_;
+ GLContext context_;
+ std::function current_paint_callback_;
+};
+
+} // namespace gl
+} // namespace ui
+} // namespace xe
+
+#endif // XENIA_UI_GL_WGL_ELEMENTAL_CONTROL_H_
diff --git a/src/xenia/ui/platform.h b/src/xenia/ui/platform.h
index 6ba60300f..6aaf04811 100644
--- a/src/xenia/ui/platform.h
+++ b/src/xenia/ui/platform.h
@@ -12,6 +12,7 @@
#define XENIA_UI_PLATFORM_H_
// TODO(benvanik): only on windows.
+#include "xenia/ui/win32/win32_control.h"
#include "xenia/ui/win32/win32_file_picker.h"
#include "xenia/ui/win32/win32_loop.h"
#include "xenia/ui/win32/win32_menu_item.h"
@@ -20,6 +21,7 @@
namespace xe {
namespace ui {
+using PlatformControl = xe::ui::win32::Win32Control;
using PlatformFilePicker = xe::ui::win32::Win32FilePicker;
using PlatformLoop = xe::ui::win32::Win32Loop;
using PlatformMenu = xe::ui::win32::Win32MenuItem;
diff --git a/third_party/elemental-forms b/third_party/elemental-forms
new file mode 160000
index 000000000..b7322a960
--- /dev/null
+++ b/third_party/elemental-forms
@@ -0,0 +1 @@
+Subproject commit b7322a960063620938d76156c0ce4a4328788e71
diff --git a/third_party/turbobadger b/third_party/turbobadger
deleted file mode 160000
index 5075e6995..000000000
--- a/third_party/turbobadger
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5075e6995e7946c6d14cd86a813281f19f5853a9
diff --git a/xenia.sln b/xenia.sln
index 5729c0965..db7d23ddd 100644
--- a/xenia.sln
+++ b/xenia.sln
@@ -14,6 +14,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libxenia", "libxenia.vcxpro
ProjectSection(ProjectDependencies) = postProject
{AE4AF147-715A-4C24-8BFA-136332DED28F} = {AE4AF147-715A-4C24-8BFA-136332DED28F}
{93533067-6449-4691-88A8-026EBCFDCA97} = {93533067-6449-4691-88A8-026EBCFDCA97}
+ {156102D7-F2DD-4618-B2EB-2DFE607EE6DD} = {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}
{838020F9-94AA-4314-996D-69B923C45D39} = {838020F9-94AA-4314-996D-69B923C45D39}
EndProjectSection
EndProject
@@ -52,8 +53,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xe-gpu-trace-viewer", "src\
{838020F9-94AA-4314-996D-69B923C45D39} = {838020F9-94AA-4314-996D-69B923C45D39}
EndProjectSection
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libturbobadger", "third_party\turbobadger\libturbobadger.vcxproj", "{156102D7-F2DD-4618-B2EB-2DFE607EE6DD}"
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xe-debug-ui", "src\xenia\debug\ui\xe-debug-ui.vcxproj", "{C5BA52F0-C86B-4817-921C-CCA257FC04BE}"
ProjectSection(ProjectDependencies) = postProject
{AE4AF147-715A-4C24-8BFA-136332DED28F} = {AE4AF147-715A-4C24-8BFA-136332DED28F}
@@ -64,6 +63,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xe-debug-ui", "src\xenia\de
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libxenia-base", "libxenia-base.vcxproj", "{93533067-6449-4691-88A8-026EBCFDCA97}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libelemental", "third_party\elemental-forms\libelemental.vcxproj", "{156102D7-F2DD-4618-B2EB-2DFE607EE6DD}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elemental-forms-demo", "third_party\elemental-forms\testbed\elemental-testbed.vcxproj", "{C3FDE1FE-1FCB-4156-BB37-2E38F5C2DFE7}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|x64 = Checked|x64
@@ -125,14 +128,8 @@ Global
{21DDCB81-68A3-4AB2-8CB0-C2B051B9FDDC}.Debug|x64.Build.0 = Debug|x64
{21DDCB81-68A3-4AB2-8CB0-C2B051B9FDDC}.Release|x64.ActiveCfg = Release|x64
{21DDCB81-68A3-4AB2-8CB0-C2B051B9FDDC}.Release|x64.Build.0 = Release|x64
- {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Checked|x64.ActiveCfg = Debug|x64
- {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Checked|x64.Build.0 = Debug|x64
- {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Debug|x64.ActiveCfg = Debug|x64
- {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Debug|x64.Build.0 = Debug|x64
- {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Release|x64.ActiveCfg = Release|x64
- {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Release|x64.Build.0 = Release|x64
- {C5BA52F0-C86B-4817-921C-CCA257FC04BE}.Checked|x64.ActiveCfg = Release|x64
- {C5BA52F0-C86B-4817-921C-CCA257FC04BE}.Checked|x64.Build.0 = Release|x64
+ {C5BA52F0-C86B-4817-921C-CCA257FC04BE}.Checked|x64.ActiveCfg = Debug|x64
+ {C5BA52F0-C86B-4817-921C-CCA257FC04BE}.Checked|x64.Build.0 = Debug|x64
{C5BA52F0-C86B-4817-921C-CCA257FC04BE}.Debug|x64.ActiveCfg = Debug|x64
{C5BA52F0-C86B-4817-921C-CCA257FC04BE}.Debug|x64.Build.0 = Debug|x64
{C5BA52F0-C86B-4817-921C-CCA257FC04BE}.Release|x64.ActiveCfg = Release|x64
@@ -143,6 +140,18 @@ Global
{93533067-6449-4691-88A8-026EBCFDCA97}.Debug|x64.Build.0 = Debug|x64
{93533067-6449-4691-88A8-026EBCFDCA97}.Release|x64.ActiveCfg = Release|x64
{93533067-6449-4691-88A8-026EBCFDCA97}.Release|x64.Build.0 = Release|x64
+ {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Checked|x64.ActiveCfg = Checked|x64
+ {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Checked|x64.Build.0 = Checked|x64
+ {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Debug|x64.ActiveCfg = Debug|x64
+ {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Debug|x64.Build.0 = Debug|x64
+ {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Release|x64.ActiveCfg = Release|x64
+ {156102D7-F2DD-4618-B2EB-2DFE607EE6DD}.Release|x64.Build.0 = Release|x64
+ {C3FDE1FE-1FCB-4156-BB37-2E38F5C2DFE7}.Checked|x64.ActiveCfg = Checked|x64
+ {C3FDE1FE-1FCB-4156-BB37-2E38F5C2DFE7}.Checked|x64.Build.0 = Checked|x64
+ {C3FDE1FE-1FCB-4156-BB37-2E38F5C2DFE7}.Debug|x64.ActiveCfg = Debug|x64
+ {C3FDE1FE-1FCB-4156-BB37-2E38F5C2DFE7}.Debug|x64.Build.0 = Debug|x64
+ {C3FDE1FE-1FCB-4156-BB37-2E38F5C2DFE7}.Release|x64.ActiveCfg = Release|x64
+ {C3FDE1FE-1FCB-4156-BB37-2E38F5C2DFE7}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -155,7 +164,8 @@ Global
{6EC54AD0-4F5B-48D9-B820-43DF2F0DC83C} = {9C5BDD9E-831B-4AEE-957F-0E88ADED79C6}
{9B8AC22F-9147-490F-BE03-3B8BA31990A8} = {9C5BDD9E-831B-4AEE-957F-0E88ADED79C6}
{21DDCB81-68A3-4AB2-8CB0-C2B051B9FDDC} = {345BD157-B21D-4989-9CE4-FA3C90FFC095}
- {156102D7-F2DD-4618-B2EB-2DFE607EE6DD} = {FCCBE57F-ECAE-420A-8A82-4B85F722C272}
{C5BA52F0-C86B-4817-921C-CCA257FC04BE} = {345BD157-B21D-4989-9CE4-FA3C90FFC095}
+ {156102D7-F2DD-4618-B2EB-2DFE607EE6DD} = {FCCBE57F-ECAE-420A-8A82-4B85F722C272}
+ {C3FDE1FE-1FCB-4156-BB37-2E38F5C2DFE7} = {9C5BDD9E-831B-4AEE-957F-0E88ADED79C6}
EndGlobalSection
EndGlobal