Dropping elemental-forms and swapping out with imgui.
Too much code for such little use. This should simplify porting.
This commit is contained in:
parent
2071286040
commit
0e58208add
|
@ -10,9 +10,6 @@
|
|||
[submodule "third_party/capstone"]
|
||||
path = third_party/capstone
|
||||
url = https://github.com/xenia-project/capstone.git
|
||||
[submodule "third_party/elemental-forms"]
|
||||
path = third_party/elemental-forms
|
||||
url = https://github.com/xenia-project/elemental-forms.git
|
||||
[submodule "build_tools"]
|
||||
path = build_tools
|
||||
url = https://github.com/xenia-project/build-tools.git
|
||||
|
|
|
@ -159,7 +159,6 @@ solution("xenia")
|
|||
|
||||
-- Include third party files first so they don't have to deal with gflags.
|
||||
include("third_party/capstone.lua")
|
||||
include("third_party/elemental-forms")
|
||||
include("third_party/glew.lua")
|
||||
include("third_party/imgui.lua")
|
||||
include("third_party/libav.lua")
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include "xenia/app/emulator_window.h"
|
||||
|
||||
#include "third_party/elemental-forms/src/el/elements.h"
|
||||
#include "third_party/imgui/imgui.h"
|
||||
#include "xenia/base/clock.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
@ -17,6 +17,8 @@
|
|||
#include "xenia/base/threading.h"
|
||||
#include "xenia/emulator.h"
|
||||
#include "xenia/gpu/graphics_system.h"
|
||||
#include "xenia/ui/imgui_dialog.h"
|
||||
#include "xenia/ui/imgui_drawer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace app {
|
||||
|
@ -221,11 +223,10 @@ void EmulatorWindow::CpuTimeScalarSetDouble() {
|
|||
void EmulatorWindow::CpuBreakIntoDebugger() {
|
||||
auto debugger = emulator()->debugger();
|
||||
if (!debugger) {
|
||||
auto message_form = new el::MessageForm(window_->root_element(),
|
||||
TBIDC("debug_error_window"));
|
||||
message_form->Show("Xenia Debugger",
|
||||
"Xenia must be launched with the --debug flag in order "
|
||||
"to enable debugging.");
|
||||
xe::ui::ImGuiDialog::ShowMessageBox(window_.get(), "Xenia Debugger",
|
||||
"Xenia must be launched with the "
|
||||
"--debug flag in order to enable "
|
||||
"debugging.");
|
||||
return;
|
||||
}
|
||||
if (debugger->execution_state() == debug::ExecutionState::kRunning) {
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
//{{NO_DEPENDENCIES}}
|
||||
|
||||
#include "third_party\\elemental-forms\\resources.rc"
|
||||
|
||||
//IDR_xe_ui_main_resources_skin_bg_tile_png RCDATA ".\\resources\\skin\\bg_tile.png"
|
||||
|
|
|
@ -8,7 +8,6 @@ project("xenia-app")
|
|||
targetname("xenia")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"gflags",
|
||||
"imgui",
|
||||
"xenia-apu",
|
||||
|
@ -33,7 +32,6 @@ project("xenia-app")
|
|||
defines({
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
local_platform_files()
|
||||
|
@ -46,7 +44,6 @@ project("xenia-app")
|
|||
})
|
||||
resincludedirs({
|
||||
project_root,
|
||||
project_root.."/third_party/elemental-forms",
|
||||
})
|
||||
|
||||
filter("platforms:Windows")
|
||||
|
|
|
@ -8,7 +8,6 @@ project("xenia-base")
|
|||
defines({
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
local_platform_files()
|
||||
|
|
|
@ -98,8 +98,8 @@ bool X64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder,
|
|||
}
|
||||
|
||||
function->set_debug_info(std::move(debug_info));
|
||||
static_cast<X64Function*>(function)
|
||||
->Setup(reinterpret_cast<uint8_t*>(machine_code), code_size);
|
||||
static_cast<X64Function*>(function)->Setup(
|
||||
reinterpret_cast<uint8_t*>(machine_code), code_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -465,8 +465,8 @@ bool XexModule::SetupLibraryImports(const char* name,
|
|||
GuestFunction::ExternHandler handler = nullptr;
|
||||
if (kernel_export) {
|
||||
if (kernel_export->function_data.trampoline) {
|
||||
handler = (GuestFunction::ExternHandler)
|
||||
kernel_export->function_data.trampoline;
|
||||
handler = (GuestFunction::ExternHandler)kernel_export
|
||||
->function_data.trampoline;
|
||||
} else {
|
||||
handler =
|
||||
(GuestFunction::ExternHandler)kernel_export->function_data.shim;
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include "third_party/capstone/include/capstone/capstone.h"
|
||||
#include "third_party/capstone/include/capstone/x86.h"
|
||||
#include "third_party/elemental-forms/src/el/util/clipboard.h"
|
||||
#include "third_party/imgui/imgui.h"
|
||||
#include "third_party/imgui/imgui_internal.h"
|
||||
#include "xenia/base/clock.h"
|
||||
|
@ -87,10 +86,6 @@ bool DebugWindow::Initialize() {
|
|||
return false;
|
||||
}
|
||||
|
||||
window_->on_closed.AddListener([this](UIEvent* e) {
|
||||
// Kill now while we have a GL context.
|
||||
imgui_drawer_.reset();
|
||||
});
|
||||
loop_->on_quit.AddListener([this](UIEvent* e) { window_.reset(); });
|
||||
|
||||
// Main menu.
|
||||
|
@ -110,9 +105,8 @@ bool DebugWindow::Initialize() {
|
|||
auto provider = emulator_->display_window()->context()->provider();
|
||||
window_->set_context(provider->CreateContext(window_.get()));
|
||||
|
||||
// Setup ImGui.
|
||||
imgui_drawer_ = std::make_unique<xe::ui::ImGuiDrawer>(window_.get());
|
||||
imgui_drawer_->SetupDefaultInput();
|
||||
// Enable imgui input.
|
||||
window_->set_imgui_input_enabled(true);
|
||||
|
||||
window_->on_painting.AddListener([this](UIEvent* e) { DrawFrame(); });
|
||||
|
||||
|
@ -125,16 +119,7 @@ bool DebugWindow::Initialize() {
|
|||
void DebugWindow::DrawFrame() {
|
||||
xe::ui::GraphicsContextLock lock(window_->context());
|
||||
|
||||
auto& io = ImGui::GetIO();
|
||||
auto current_tick_count = Clock::QueryHostTickCount();
|
||||
io.DeltaTime = (current_tick_count - last_draw_tick_count_) /
|
||||
static_cast<float>(Clock::host_tick_frequency());
|
||||
last_draw_tick_count_ = current_tick_count;
|
||||
|
||||
io.DisplaySize = ImVec2(static_cast<float>(window_->width()),
|
||||
static_cast<float>(window_->height()));
|
||||
|
||||
ImGui::NewFrame();
|
||||
auto& io = window_->imgui_drawer()->GetIO();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(-1, 0));
|
||||
ImGui::Begin("main_window", nullptr,
|
||||
|
@ -174,7 +159,7 @@ void DebugWindow::DrawFrame() {
|
|||
ImGui::InvisibleButton("##vsplitter0",
|
||||
ImVec2(kSplitterWidth, top_panes_height));
|
||||
if (ImGui::IsItemActive()) {
|
||||
function_pane_width += ImGui::GetIO().MouseDelta.x;
|
||||
function_pane_width += io.MouseDelta.x;
|
||||
function_pane_width = xe::clamp(function_pane_width, 30.0f, FLT_MAX);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
@ -186,7 +171,7 @@ void DebugWindow::DrawFrame() {
|
|||
ImGui::InvisibleButton("##vsplitter1",
|
||||
ImVec2(kSplitterWidth, top_panes_height));
|
||||
if (ImGui::IsItemActive()) {
|
||||
source_pane_width += ImGui::GetIO().MouseDelta.x;
|
||||
source_pane_width += io.MouseDelta.x;
|
||||
source_pane_width = xe::clamp(source_pane_width, 30.0f, FLT_MAX);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
@ -198,7 +183,7 @@ void DebugWindow::DrawFrame() {
|
|||
ImGui::InvisibleButton("##vsplitter2",
|
||||
ImVec2(kSplitterWidth, top_panes_height));
|
||||
if (ImGui::IsItemActive()) {
|
||||
registers_pane_width += ImGui::GetIO().MouseDelta.x;
|
||||
registers_pane_width += io.MouseDelta.x;
|
||||
registers_pane_width = xe::clamp(registers_pane_width, 30.0f, FLT_MAX);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
@ -226,7 +211,7 @@ void DebugWindow::DrawFrame() {
|
|||
ImGui::EndChild();
|
||||
ImGui::InvisibleButton("##hsplitter0", ImVec2(-1, kSplitterWidth));
|
||||
if (ImGui::IsItemActive()) {
|
||||
bottom_panes_height -= ImGui::GetIO().MouseDelta.y;
|
||||
bottom_panes_height -= io.MouseDelta.y;
|
||||
bottom_panes_height = xe::clamp(bottom_panes_height, 30.0f, FLT_MAX);
|
||||
}
|
||||
ImGui::BeginChild("##log_pane", ImVec2(log_pane_width, bottom_panes_height),
|
||||
|
@ -237,7 +222,7 @@ void DebugWindow::DrawFrame() {
|
|||
ImGui::InvisibleButton("##vsplitter3",
|
||||
ImVec2(kSplitterWidth, bottom_panes_height));
|
||||
if (ImGui::IsItemActive()) {
|
||||
breakpoints_pane_width -= ImGui::GetIO().MouseDelta.x;
|
||||
breakpoints_pane_width -= io.MouseDelta.x;
|
||||
breakpoints_pane_width = xe::clamp(breakpoints_pane_width, 30.0f, FLT_MAX);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
@ -397,7 +382,7 @@ void DebugWindow::DrawSourcePane() {
|
|||
ImGui::Dummy(ImVec2(16, 0));
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Copy")) {
|
||||
el::util::Clipboard::SetText("TODO");
|
||||
// TODO(benvanik): move clipboard abstraction into ui?
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(4, 0));
|
||||
|
|
|
@ -21,12 +21,6 @@
|
|||
#include "xenia/ui/window.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
class ImGuiDrawer;
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
|
@ -95,7 +89,6 @@ class DebugWindow : public DebugListener {
|
|||
Debugger* debugger_ = nullptr;
|
||||
xe::ui::Loop* loop_ = nullptr;
|
||||
std::unique_ptr<xe::ui::Window> window_;
|
||||
std::unique_ptr<xe::ui::ImGuiDrawer> imgui_drawer_;
|
||||
uint64_t last_draw_tick_count_ = 0;
|
||||
|
||||
uintptr_t capstone_handle_ = 0;
|
||||
|
|
|
@ -7,7 +7,6 @@ project("xenia-debug-ui")
|
|||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"glew",
|
||||
"imgui",
|
||||
"xenia-base",
|
||||
|
@ -22,6 +21,5 @@ project("xenia-debug-ui")
|
|||
})
|
||||
includedirs({
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
})
|
||||
local_platform_files()
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include "third_party/elemental-forms/src/el/elements.h"
|
||||
#include "xenia/apu/audio_system.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/clock.h"
|
||||
|
@ -28,6 +27,7 @@
|
|||
#include "xenia/kernel/xam/xam_module.h"
|
||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h"
|
||||
#include "xenia/memory.h"
|
||||
#include "xenia/ui/imgui_dialog.h"
|
||||
#include "xenia/vfs/devices/disc_image_device.h"
|
||||
#include "xenia/vfs/devices/host_path_device.h"
|
||||
#include "xenia/vfs/devices/stfs_container_device.h"
|
||||
|
@ -334,11 +334,9 @@ bool Emulator::ExceptionCallback(Exception* ex) {
|
|||
|
||||
// Display a dialog telling the user the guest has crashed.
|
||||
display_window()->loop()->PostSynchronous([&]() {
|
||||
auto message_form = new el::MessageForm(display_window()->root_element(),
|
||||
TBIDC("crash_form"));
|
||||
message_form->Show("Uh-oh!",
|
||||
"The guest has crashed.\n\n"
|
||||
"Xenia has now paused itself.");
|
||||
xe::ui::ImGuiDialog::ShowMessageBox(display_window(), "Uh-oh!",
|
||||
"The guest has crashed.\n\n"
|
||||
"Xenia has now paused itself.");
|
||||
});
|
||||
|
||||
// Now suspend ourself (we should be a guest thread).
|
||||
|
|
|
@ -7,7 +7,6 @@ project("xenia-gpu-gl4")
|
|||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"glew",
|
||||
"xenia-base",
|
||||
"xenia-gpu",
|
||||
|
@ -20,7 +19,6 @@ project("xenia-gpu-gl4")
|
|||
"GLEW_MX=1",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
local_platform_files()
|
||||
|
@ -32,7 +30,6 @@ project("xenia-gpu-gl4-trace-viewer")
|
|||
kind("WindowedApp")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"gflags",
|
||||
"glew",
|
||||
"imgui",
|
||||
|
@ -62,7 +59,6 @@ project("xenia-gpu-gl4-trace-viewer")
|
|||
"GLEW_MX=1",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
files({
|
||||
|
@ -88,7 +84,6 @@ project("xenia-gpu-gl4-trace-dump")
|
|||
kind("ConsoleApp")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"gflags",
|
||||
"glew",
|
||||
"imgui",
|
||||
|
@ -115,7 +110,6 @@ project("xenia-gpu-gl4-trace-dump")
|
|||
"GLEW_MX=1",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
files({
|
||||
|
|
|
@ -7,7 +7,6 @@ project("xenia-gpu")
|
|||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"spirv-tools",
|
||||
"xenia-base",
|
||||
"xenia-ui",
|
||||
|
@ -18,12 +17,11 @@ project("xenia-gpu")
|
|||
defines({
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/third_party/spirv-tools/external/include",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
local_platform_files()
|
||||
|
||||
|
||||
group("src")
|
||||
project("xenia-gpu-shader-compiler")
|
||||
uuid("ad76d3e4-4c62-439b-a0f6-f83fcf0e83c5")
|
||||
|
|
|
@ -112,7 +112,6 @@ bool TraceViewer::Setup() {
|
|||
window_->on_closed.AddListener([&](xe::ui::UIEvent* e) {
|
||||
loop_->Quit();
|
||||
XELOGI("User-initiated death!");
|
||||
imgui_drawer_.reset();
|
||||
exit(1);
|
||||
});
|
||||
loop_->on_quit.AddListener([&](xe::ui::UIEvent* e) { window_.reset(); });
|
||||
|
@ -130,6 +129,8 @@ bool TraceViewer::Setup() {
|
|||
memory_ = emulator_->memory();
|
||||
graphics_system_ = emulator_->graphics_system();
|
||||
|
||||
window_->set_imgui_input_enabled(true);
|
||||
|
||||
window_->on_key_char.AddListener([&](xe::ui::KeyEvent* e) {
|
||||
if (e->key_code() == 0x74 /* VK_F5 */) {
|
||||
graphics_system_->ClearCaches();
|
||||
|
@ -139,26 +140,9 @@ bool TraceViewer::Setup() {
|
|||
|
||||
player_ = std::make_unique<TracePlayer>(loop_.get(), graphics_system_);
|
||||
|
||||
imgui_drawer_ = std::make_unique<xe::ui::ImGuiDrawer>(window_.get());
|
||||
imgui_drawer_->SetupDefaultInput();
|
||||
|
||||
window_->on_painting.AddListener([&](xe::ui::UIEvent* e) {
|
||||
auto& io = ImGui::GetIO();
|
||||
auto current_ticks = Clock::QueryHostTickCount();
|
||||
static uint64_t last_ticks = 0;
|
||||
io.DeltaTime =
|
||||
(current_ticks - last_ticks) / float(Clock::host_tick_frequency());
|
||||
last_ticks = current_ticks;
|
||||
|
||||
io.DisplaySize =
|
||||
ImVec2(float(e->target()->width()), float(e->target()->height()));
|
||||
|
||||
ImGui::NewFrame();
|
||||
|
||||
DrawUI();
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
// Continuous paint.
|
||||
window_->Invalidate();
|
||||
});
|
||||
|
@ -185,7 +169,6 @@ void TraceViewer::Run() {
|
|||
|
||||
player_.reset();
|
||||
emulator_.reset();
|
||||
imgui_drawer_.reset();
|
||||
window_.reset();
|
||||
loop_.reset();
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
class ImGuiDrawer;
|
||||
class Loop;
|
||||
class Window;
|
||||
} // namespace ui
|
||||
|
@ -62,8 +61,6 @@ class TraceViewer {
|
|||
GraphicsSystem* graphics_system_ = nullptr;
|
||||
std::unique_ptr<TracePlayer> player_;
|
||||
|
||||
std::unique_ptr<xe::ui::ImGuiDrawer> imgui_drawer_;
|
||||
|
||||
private:
|
||||
enum class ShaderDisplayType : int {
|
||||
kUcode,
|
||||
|
|
|
@ -34,7 +34,6 @@ DEFINE_string(hid, "any", "Input system. Use: [any, nop, winkey, xinput]");
|
|||
namespace xe {
|
||||
namespace hid {
|
||||
|
||||
std::unique_ptr<xe::ui::ImGuiDrawer> imgui_drawer_;
|
||||
std::unique_ptr<xe::hid::InputSystem> input_system_;
|
||||
|
||||
std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers(
|
||||
|
@ -88,7 +87,6 @@ int hid_demo_main(const std::vector<std::wstring>& args) {
|
|||
window->on_closed.AddListener([&loop](xe::ui::UIEvent* e) {
|
||||
loop->Quit();
|
||||
XELOGI("User-initiated death!");
|
||||
imgui_drawer_.reset();
|
||||
exit(1);
|
||||
});
|
||||
loop->on_quit.AddListener([&window](xe::ui::UIEvent* e) { window.reset(); });
|
||||
|
@ -105,30 +103,18 @@ int hid_demo_main(const std::vector<std::wstring>& args) {
|
|||
graphics_provider = CreateDemoGraphicsProvider(window.get());
|
||||
window->set_context(graphics_provider->CreateContext(window.get()));
|
||||
|
||||
// Initialize the ImGui renderer we'll use.
|
||||
imgui_drawer_ = std::make_unique<xe::ui::ImGuiDrawer>(window.get());
|
||||
imgui_drawer_->SetupDefaultInput();
|
||||
|
||||
// Initialize input system and all drivers.
|
||||
input_system_ = std::make_unique<xe::hid::InputSystem>(window.get());
|
||||
auto drivers = CreateInputDrivers(window.get());
|
||||
for (size_t i = 0; i < drivers.size(); ++i) {
|
||||
input_system_->AddDriver(std::move(drivers[i]));
|
||||
}
|
||||
|
||||
window->Invalidate();
|
||||
});
|
||||
|
||||
window->on_painting.AddListener([&](xe::ui::UIEvent* e) {
|
||||
auto& io = ImGui::GetIO();
|
||||
auto current_tick_count = Clock::QueryHostTickCount();
|
||||
static uint64_t last_draw_tick_count = 0;
|
||||
io.DeltaTime = (current_tick_count - last_draw_tick_count) /
|
||||
static_cast<float>(Clock::host_tick_frequency());
|
||||
last_draw_tick_count = current_tick_count;
|
||||
|
||||
io.DisplaySize = ImVec2(static_cast<float>(window->width()),
|
||||
static_cast<float>(window->height()));
|
||||
|
||||
ImGui::NewFrame();
|
||||
auto& io = window->imgui_drawer()->GetIO();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(-1, 0));
|
||||
ImGui::Begin("main_window", nullptr, ImGuiWindowFlags_NoMove |
|
||||
|
@ -146,8 +132,6 @@ int hid_demo_main(const std::vector<std::wstring>& args) {
|
|||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
// Continuous paint.
|
||||
window->Invalidate();
|
||||
});
|
||||
|
@ -155,7 +139,6 @@ int hid_demo_main(const std::vector<std::wstring>& args) {
|
|||
// Wait until we are exited.
|
||||
loop->AwaitQuit();
|
||||
|
||||
imgui_drawer_.reset();
|
||||
input_system_.reset();
|
||||
|
||||
loop->PostSynchronous([&graphics_provider]() { graphics_provider.reset(); });
|
||||
|
|
|
@ -23,7 +23,6 @@ project("xenia-hid-demo")
|
|||
kind("WindowedApp")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"gflags",
|
||||
"glew",
|
||||
"imgui",
|
||||
|
@ -41,7 +40,6 @@ project("xenia-hid-demo")
|
|||
"GLEW_MX=1",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
files({
|
||||
|
@ -49,11 +47,9 @@ project("xenia-hid-demo")
|
|||
project_root.."/src/xenia/base/main_"..platform_suffix..".cc",
|
||||
})
|
||||
files({
|
||||
project_root.."/third_party/elemental-forms/resources.rc",
|
||||
})
|
||||
resincludedirs({
|
||||
project_root,
|
||||
project_root.."/third_party/elemental-forms",
|
||||
})
|
||||
|
||||
filter("platforms:Windows")
|
||||
|
|
|
@ -15,6 +15,5 @@ project("xenia-hid-winkey")
|
|||
})
|
||||
includedirs({
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
})
|
||||
local_platform_files()
|
||||
|
|
|
@ -99,8 +99,8 @@ uint32_t KernelModule::GetProcAddressByOrdinal(uint16_t ordinal) {
|
|||
|
||||
cpu::GuestFunction::ExternHandler handler = nullptr;
|
||||
if (export_entry->function_data.trampoline) {
|
||||
handler = (cpu::GuestFunction::ExternHandler)
|
||||
export_entry->function_data.trampoline;
|
||||
handler = (cpu::GuestFunction::ExternHandler)export_entry
|
||||
->function_data.trampoline;
|
||||
} else {
|
||||
handler =
|
||||
(cpu::GuestFunction::ExternHandler)export_entry->function_data.shim;
|
||||
|
|
|
@ -7,7 +7,6 @@ project("xenia-kernel")
|
|||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"xenia-apu",
|
||||
"xenia-base",
|
||||
"xenia-cpu",
|
||||
|
@ -19,7 +18,6 @@ project("xenia-kernel")
|
|||
defines({
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
recursive_platform_files()
|
||||
|
|
|
@ -7,12 +7,13 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "el/elemental_forms.h"
|
||||
#include "third_party/imgui/imgui.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/emulator.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/kernel/xam/xam_private.h"
|
||||
#include "xenia/ui/imgui_dialog.h"
|
||||
#include "xenia/ui/window.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
|
@ -20,81 +21,66 @@ namespace xe {
|
|||
namespace kernel {
|
||||
namespace xam {
|
||||
|
||||
std::atomic<int> xam_dialogs_shown_ = 0;
|
||||
|
||||
SHIM_CALL XamIsUIActive_shim(PPCContext* ppc_context,
|
||||
KernelState* kernel_state) {
|
||||
XELOGD("XamIsUIActive()");
|
||||
SHIM_SET_RETURN_32(el::ModalForm::is_any_visible());
|
||||
SHIM_SET_RETURN_32(xam_dialogs_shown_ > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
class MessageBoxWindow : public el::ModalForm {
|
||||
class MessageBoxDialog : public xe::ui::ImGuiDialog {
|
||||
public:
|
||||
explicit MessageBoxWindow(xe::threading::Fence* fence)
|
||||
: ModalForm([fence]() { fence->Signal(); }) {}
|
||||
~MessageBoxWindow() override = default;
|
||||
|
||||
// TODO(benvanik): icon.
|
||||
void Show(el::Element* root_element, std::wstring title, std::wstring message,
|
||||
std::vector<std::wstring> buttons, uint32_t default_button,
|
||||
uint32_t* out_chosen_button) {
|
||||
title_ = std::move(title);
|
||||
message_ = std::move(message);
|
||||
buttons_ = std::move(buttons);
|
||||
default_button_ = default_button;
|
||||
out_chosen_button_ = out_chosen_button;
|
||||
|
||||
// In case we are cancelled, always set default button.
|
||||
*out_chosen_button_ = default_button;
|
||||
|
||||
ModalForm::Show(root_element);
|
||||
|
||||
EnsureFocus();
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildUI() override {
|
||||
using namespace el::dsl; // NOLINT(build/namespaces)
|
||||
|
||||
set_text(xe::to_string(title_));
|
||||
|
||||
auto button_bar = LayoutBoxNode().distribution_position(
|
||||
LayoutDistributionPosition::kRightBottom);
|
||||
for (int32_t i = 0; i < buttons_.size(); ++i) {
|
||||
button_bar.child(ButtonNode(xe::to_string(buttons_[i]).c_str())
|
||||
.id(100 + i)
|
||||
.data(100 + i));
|
||||
MessageBoxDialog(xe::ui::Window* window, std::wstring title,
|
||||
std::wstring description, std::vector<std::wstring> buttons,
|
||||
uint32_t default_button, uint32_t* out_chosen_button)
|
||||
: ImGuiDialog(window),
|
||||
title_(xe::to_string(title)),
|
||||
description_(xe::to_string(description)),
|
||||
buttons_(std::move(buttons)),
|
||||
default_button_(default_button),
|
||||
out_chosen_button_(out_chosen_button) {
|
||||
if (out_chosen_button) {
|
||||
*out_chosen_button = default_button;
|
||||
}
|
||||
|
||||
LoadNodeTree(
|
||||
LayoutBoxNode()
|
||||
.axis(Axis::kY)
|
||||
.gravity(Gravity::kAll)
|
||||
.position(LayoutPosition::kLeftTop)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.distribution_position(LayoutDistributionPosition::kLeftTop)
|
||||
.child(LabelNode(xe::to_string(message_).c_str()))
|
||||
.child(button_bar));
|
||||
|
||||
auto default_button = GetElementById<el::Button>(100 + default_button_);
|
||||
el::Button::set_auto_focus_state(true);
|
||||
default_button->set_focus(el::FocusReason::kNavigation);
|
||||
}
|
||||
|
||||
bool OnEvent(const el::Event& ev) override {
|
||||
if (ev.target->IsOfType<el::Button>() &&
|
||||
(ev.type == el::EventType::kClick ||
|
||||
ev.special_key == el::SpecialKey::kEnter)) {
|
||||
int data_value = ev.target->data.as_integer();
|
||||
if (data_value) {
|
||||
*out_chosen_button_ = data_value - 100;
|
||||
void OnDraw(ImGuiIO& io) override {
|
||||
bool first_draw = false;
|
||||
if (!has_opened_) {
|
||||
ImGui::OpenPopup(title_.c_str());
|
||||
has_opened_ = true;
|
||||
first_draw = true;
|
||||
}
|
||||
if (ImGui::BeginPopupModal(title_.c_str(), nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text(description_.c_str());
|
||||
if (first_draw) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
Die();
|
||||
return true;
|
||||
for (size_t i = 0; i < buttons_.size(); ++i) {
|
||||
auto button_name = xe::to_string(buttons_[i]);
|
||||
if (ImGui::Button(button_name.c_str())) {
|
||||
if (out_chosen_button_) {
|
||||
*out_chosen_button_ = static_cast<uint32_t>(i);
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
Close();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
Close();
|
||||
}
|
||||
return ModalForm::OnEvent(ev);
|
||||
}
|
||||
|
||||
std::wstring title_;
|
||||
std::wstring message_;
|
||||
private:
|
||||
bool has_opened_ = false;
|
||||
std::string title_;
|
||||
std::string description_;
|
||||
std::vector<std::wstring> buttons_;
|
||||
uint32_t default_button_ = 0;
|
||||
uint32_t* out_chosen_button_ = nullptr;
|
||||
|
@ -142,8 +128,7 @@ SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
|||
auto display_window = kernel_state->emulator()->display_window();
|
||||
xe::threading::Fence fence;
|
||||
display_window->loop()->PostSynchronous([&]() {
|
||||
auto root_element = display_window->root_element();
|
||||
auto window = new MessageBoxWindow(&fence);
|
||||
// TODO(benvanik): setup icon states.
|
||||
switch (flags & 0xF) {
|
||||
case 0:
|
||||
// config.pszMainIcon = nullptr;
|
||||
|
@ -158,10 +143,13 @@ SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
|||
// config.pszMainIcon = TD_INFORMATION_ICON;
|
||||
break;
|
||||
}
|
||||
window->Show(root_element, title, text, buttons, active_button,
|
||||
&chosen_button);
|
||||
(new MessageBoxDialog(display_window, title, text, buttons, active_button,
|
||||
&chosen_button))
|
||||
->Then(&fence);
|
||||
});
|
||||
++xam_dialogs_shown_;
|
||||
fence.Wait();
|
||||
--xam_dialogs_shown_;
|
||||
}
|
||||
SHIM_SET_MEM_32(result_ptr, chosen_button);
|
||||
|
||||
|
@ -169,102 +157,81 @@ SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
|||
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
|
||||
}
|
||||
|
||||
class KeyboardInputWindow : public el::ModalForm {
|
||||
class KeyboardInputDialog : public xe::ui::ImGuiDialog {
|
||||
public:
|
||||
explicit KeyboardInputWindow(xe::threading::Fence* fence)
|
||||
: ModalForm([fence]() { fence->Signal(); }) {}
|
||||
~KeyboardInputWindow() override = default;
|
||||
|
||||
// TODO(benvanik): icon.
|
||||
void Show(el::Element* root_element, std::wstring title,
|
||||
std::wstring description, std::wstring default_text,
|
||||
std::wstring* out_text, size_t max_length) {
|
||||
title_ = std::move(title);
|
||||
description_ = std::move(description);
|
||||
default_text_ = std::move(default_text);
|
||||
out_text_ = out_text;
|
||||
max_length_ = max_length;
|
||||
|
||||
// Incase we're cancelled, set as the default text.
|
||||
if (out_text) {
|
||||
*out_text = default_text;
|
||||
KeyboardInputDialog(xe::ui::Window* window, std::wstring title,
|
||||
std::wstring description, std::wstring default_text,
|
||||
std::wstring* out_text, size_t max_length)
|
||||
: ImGuiDialog(window),
|
||||
title_(xe::to_string(title)),
|
||||
description_(xe::to_string(description)),
|
||||
default_text_(xe::to_string(default_text)),
|
||||
out_text_(out_text),
|
||||
max_length_(max_length) {
|
||||
if (out_text_) {
|
||||
*out_text_ = default_text;
|
||||
}
|
||||
|
||||
ModalForm::Show(root_element);
|
||||
|
||||
EnsureFocus();
|
||||
text_buffer_.resize(max_length);
|
||||
std::strncpy(text_buffer_.data(), default_text_.c_str(),
|
||||
std::min(text_buffer_.size() - 1, default_text_.size()));
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildUI() override {
|
||||
using namespace el::dsl; // NOLINT(build/namespaces)
|
||||
|
||||
set_text(xe::to_string(title_));
|
||||
|
||||
LoadNodeTree(
|
||||
LayoutBoxNode()
|
||||
.axis(Axis::kY)
|
||||
.gravity(Gravity::kAll)
|
||||
.position(LayoutPosition::kLeftTop)
|
||||
.min_width(300)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.distribution_position(LayoutDistributionPosition::kLeftTop)
|
||||
.child(LabelNode(xe::to_string(description_).c_str()))
|
||||
.child(LayoutBoxNode()
|
||||
.axis(Axis::kX)
|
||||
.distribution(LayoutDistribution::kGravity)
|
||||
.child(TextBoxNode()
|
||||
.id("text_input")
|
||||
.gravity(Gravity::kLeftRight)
|
||||
.placeholder(xe::to_string(default_text_))
|
||||
.min_width(150)
|
||||
.autofocus(true)))
|
||||
.child(LayoutBoxNode()
|
||||
.distribution_position(
|
||||
LayoutDistributionPosition::kRightBottom)
|
||||
.child(ButtonNode("OK").id("ok_button"))
|
||||
.child(ButtonNode("Cancel").id("cancel_button"))));
|
||||
}
|
||||
|
||||
bool OnEvent(const el::Event& ev) override {
|
||||
if (ev.special_key == el::SpecialKey::kEnter ||
|
||||
(ev.target->id() == TBIDC("ok_button") &&
|
||||
ev.type == el::EventType::kClick)) {
|
||||
// Pressed enter or clicked OK
|
||||
// Grab the text.
|
||||
if (out_text_) {
|
||||
*out_text_ =
|
||||
xe::to_wstring(GetElementById<el::TextBox>("text_input")->text());
|
||||
void OnDraw(ImGuiIO& io) override {
|
||||
bool first_draw = false;
|
||||
if (!has_opened_) {
|
||||
ImGui::OpenPopup(title_.c_str());
|
||||
has_opened_ = true;
|
||||
first_draw = true;
|
||||
}
|
||||
if (ImGui::BeginPopupModal(title_.c_str(), nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextWrapped(description_.c_str());
|
||||
if (first_draw) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
|
||||
Die();
|
||||
return true;
|
||||
} else if (ev.target->id() == TBIDC("cancel_button") &&
|
||||
ev.type == el::EventType::kClick) {
|
||||
// Cancel.
|
||||
Die();
|
||||
return true;
|
||||
if (ImGui::InputText("##body", text_buffer_.data(), text_buffer_.size(),
|
||||
ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
if (out_text_) {
|
||||
*out_text_ = xe::to_wstring(text_buffer_.data());
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
Close();
|
||||
}
|
||||
if (ImGui::Button("OK")) {
|
||||
if (out_text_) {
|
||||
*out_text_ = xe::to_wstring(text_buffer_.data());
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
Close();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
Close();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
Close();
|
||||
}
|
||||
|
||||
return ModalForm::OnEvent(ev);
|
||||
}
|
||||
|
||||
std::wstring title_;
|
||||
std::wstring description_;
|
||||
std::wstring default_text_;
|
||||
private:
|
||||
bool has_opened_ = false;
|
||||
std::string title_;
|
||||
std::string description_;
|
||||
std::string default_text_;
|
||||
std::wstring* out_text_ = nullptr;
|
||||
std::vector<char> text_buffer_;
|
||||
size_t max_length_ = 0;
|
||||
};
|
||||
|
||||
// http://www.se7ensins.com/forums/threads/release-how-to-use-xshowkeyboardui-release.906568/
|
||||
dword_result_t XamShowKeyboardUI(dword_t r3, dword_t flags,
|
||||
dword_result_t XamShowKeyboardUI(dword_t user_index, dword_t flags,
|
||||
lpwstring_t default_text, lpwstring_t title,
|
||||
lpwstring_t description, lpwstring_t buffer,
|
||||
dword_t buffer_length,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||
// Unknown parameters. I've only seen zero.
|
||||
assert_zero(r3);
|
||||
|
||||
if (!buffer) {
|
||||
return X_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
@ -289,14 +256,15 @@ dword_result_t XamShowKeyboardUI(dword_t r3, dword_t flags,
|
|||
auto display_window = kernel_state()->emulator()->display_window();
|
||||
xe::threading::Fence fence;
|
||||
display_window->loop()->PostSynchronous([&]() {
|
||||
auto root_element = display_window->root_element();
|
||||
auto window = new KeyboardInputWindow(&fence);
|
||||
window->Show(root_element, title ? title.value() : L"",
|
||||
description ? description.value() : L"",
|
||||
default_text ? default_text.value() : L"", &out_text,
|
||||
buffer_length);
|
||||
(new KeyboardInputDialog(display_window, title ? title.value() : L"",
|
||||
description ? description.value() : L"",
|
||||
default_text ? default_text.value() : L"",
|
||||
&out_text, buffer_length))
|
||||
->Then(&fence);
|
||||
});
|
||||
++xam_dialogs_shown_;
|
||||
fence.Wait();
|
||||
--xam_dialogs_shown_;
|
||||
|
||||
// Zero the output buffer.
|
||||
std::memset(buffer, 0, buffer_length * 2);
|
||||
|
@ -341,46 +309,6 @@ dword_result_t XamShowDeviceSelectorUI(dword_t user_index, dword_t content_type,
|
|||
}
|
||||
DECLARE_XAM_EXPORT(XamShowDeviceSelectorUI, ExportTag::kImplemented);
|
||||
|
||||
class DirtyDiscWindow : public el::ModalForm {
|
||||
public:
|
||||
explicit DirtyDiscWindow(xe::threading::Fence* fence)
|
||||
: ModalForm([fence]() { fence->Signal(); }) {}
|
||||
~DirtyDiscWindow() override = default;
|
||||
|
||||
protected:
|
||||
void BuildUI() override {
|
||||
using namespace el::dsl; // NOLINT(build/namespaces)
|
||||
|
||||
set_text("Disc Read Error");
|
||||
|
||||
LoadNodeTree(
|
||||
LayoutBoxNode()
|
||||
.axis(Axis::kY)
|
||||
.gravity(Gravity::kAll)
|
||||
.position(LayoutPosition::kLeftTop)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.distribution_position(LayoutDistributionPosition::kLeftTop)
|
||||
.child(LabelNode(
|
||||
"There's been an issue reading content from the game disc."))
|
||||
.child(LabelNode(
|
||||
"This is likely caused by bad or unimplemented file IO "
|
||||
"calls."))
|
||||
.child(LayoutBoxNode()
|
||||
.distribution_position(
|
||||
LayoutDistributionPosition::kRightBottom)
|
||||
.child(ButtonNode("Exit").id("exit_button"))));
|
||||
}
|
||||
|
||||
bool OnEvent(const el::Event& ev) override {
|
||||
if (ev.target->id() == TBIDC("exit_button") &&
|
||||
ev.type == el::EventType::kClick) {
|
||||
Die();
|
||||
return true;
|
||||
}
|
||||
return ModalForm::OnEvent(ev);
|
||||
}
|
||||
};
|
||||
|
||||
SHIM_CALL XamShowDirtyDiscErrorUI_shim(PPCContext* ppc_context,
|
||||
KernelState* kernel_state) {
|
||||
uint32_t user_index = SHIM_GET_ARG_32(0);
|
||||
|
@ -396,11 +324,15 @@ SHIM_CALL XamShowDirtyDiscErrorUI_shim(PPCContext* ppc_context,
|
|||
auto display_window = kernel_state->emulator()->display_window();
|
||||
xe::threading::Fence fence;
|
||||
display_window->loop()->PostSynchronous([&]() {
|
||||
auto root_element = display_window->root_element();
|
||||
auto window = new DirtyDiscWindow(&fence);
|
||||
window->Show(root_element);
|
||||
xe::ui::ImGuiDialog::ShowMessageBox(
|
||||
display_window, "Disc Read Error",
|
||||
"There's been an issue reading content from the game disc.\nThis is "
|
||||
"likely caused by bad or unimplemented file IO calls.")
|
||||
->Then(&fence);
|
||||
});
|
||||
++xam_dialogs_shown_;
|
||||
fence.Wait();
|
||||
--xam_dialogs_shown_;
|
||||
|
||||
// This is death, and should never return.
|
||||
// TODO(benvanik): cleaner exit.
|
||||
|
|
|
@ -7,13 +7,11 @@ project("xenia-core")
|
|||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"xenia-base",
|
||||
})
|
||||
defines({
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
files({"*.h", "*.cc"})
|
||||
|
|
|
@ -1,112 +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/ui/elemental_drawer.h"
|
||||
|
||||
#include "el/graphics/bitmap_fragment.h"
|
||||
#include "el/util/math.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
||||
ElementalDrawer::ElementalBitmap::ElementalBitmap(ElementalDrawer* drawer,
|
||||
int width, int height,
|
||||
uint32_t* data)
|
||||
: drawer_(drawer) {
|
||||
assert(width == el::util::GetNearestPowerOfTwo(width));
|
||||
assert(height == el::util::GetNearestPowerOfTwo(height));
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
|
||||
drawer_->FlushBitmap(this);
|
||||
|
||||
auto immediate_drawer = drawer_->graphics_context_->immediate_drawer();
|
||||
texture_ = immediate_drawer->CreateTexture(
|
||||
width, height, ImmediateTextureFilter::kLinear, true,
|
||||
reinterpret_cast<uint8_t*>(data));
|
||||
}
|
||||
|
||||
ElementalDrawer::ElementalBitmap::~ElementalBitmap() {
|
||||
// Must flush and unbind before we delete the texture.
|
||||
drawer_->FlushBitmap(this);
|
||||
}
|
||||
|
||||
void ElementalDrawer::ElementalBitmap::set_data(uint32_t* data) {
|
||||
drawer_->FlushBitmap(this);
|
||||
|
||||
auto immediate_drawer = drawer_->graphics_context_->immediate_drawer();
|
||||
immediate_drawer->UpdateTexture(texture_.get(),
|
||||
reinterpret_cast<uint8_t*>(data));
|
||||
}
|
||||
|
||||
ElementalDrawer::ElementalDrawer(xe::ui::Window* window)
|
||||
: window_(window), graphics_context_(window->context()) {
|
||||
static_assert(sizeof(ImmediateVertex) == sizeof(Vertex),
|
||||
"Vertex types must match");
|
||||
}
|
||||
|
||||
ElementalDrawer::~ElementalDrawer() = default;
|
||||
|
||||
void ElementalDrawer::BeginPaint(int render_target_w, int render_target_h) {
|
||||
Renderer::BeginPaint(render_target_w, render_target_h);
|
||||
|
||||
auto immediate_drawer = graphics_context_->immediate_drawer();
|
||||
immediate_drawer->Begin(render_target_w, render_target_h);
|
||||
|
||||
batch_.vertices = vertices_;
|
||||
}
|
||||
|
||||
void ElementalDrawer::EndPaint() {
|
||||
Renderer::EndPaint();
|
||||
|
||||
auto immediate_drawer = graphics_context_->immediate_drawer();
|
||||
immediate_drawer->End();
|
||||
}
|
||||
|
||||
std::unique_ptr<el::graphics::Bitmap> ElementalDrawer::CreateBitmap(
|
||||
int width, int height, uint32_t* data) {
|
||||
auto bitmap = std::make_unique<ElementalBitmap>(this, width, height, data);
|
||||
return std::unique_ptr<el::graphics::Bitmap>(bitmap.release());
|
||||
}
|
||||
|
||||
void ElementalDrawer::RenderBatch(Batch* batch) {
|
||||
auto immediate_drawer = graphics_context_->immediate_drawer();
|
||||
|
||||
ImmediateDrawBatch draw_batch;
|
||||
|
||||
draw_batch.vertices = reinterpret_cast<ImmediateVertex*>(batch->vertices);
|
||||
draw_batch.vertex_count = static_cast<int>(batch->vertex_count);
|
||||
immediate_drawer->BeginDrawBatch(draw_batch);
|
||||
|
||||
ImmediateDraw draw;
|
||||
draw.primitive_type = ImmediatePrimitiveType::kTriangles;
|
||||
draw.count = static_cast<int>(batch->vertex_count);
|
||||
auto bitmap = static_cast<ElementalBitmap*>(batch->bitmap);
|
||||
draw.texture_handle = bitmap ? bitmap->texture_->handle : 0;
|
||||
draw.scissor = current_clip_.w && current_clip_.h;
|
||||
draw.scissor_rect[0] = current_clip_.x;
|
||||
draw.scissor_rect[1] =
|
||||
window_->height() - (current_clip_.y + current_clip_.h);
|
||||
draw.scissor_rect[2] = current_clip_.w;
|
||||
draw.scissor_rect[3] = current_clip_.h;
|
||||
immediate_drawer->Draw(draw);
|
||||
|
||||
immediate_drawer->EndDrawBatch();
|
||||
}
|
||||
|
||||
void ElementalDrawer::set_clip_rect(const el::Rect& rect) {
|
||||
current_clip_ = rect;
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -1,69 +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_UI_ELEMENTAL_DRAWER_H_
|
||||
#define XENIA_UI_ELEMENTAL_DRAWER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "el/graphics/renderer.h"
|
||||
#include "xenia/ui/immediate_drawer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
||||
class GraphicsContext;
|
||||
class Window;
|
||||
|
||||
class ElementalDrawer : public el::graphics::Renderer {
|
||||
public:
|
||||
ElementalDrawer(Window* window);
|
||||
~ElementalDrawer();
|
||||
|
||||
void BeginPaint(int render_target_w, int render_target_h) override;
|
||||
void EndPaint() override;
|
||||
|
||||
std::unique_ptr<el::graphics::Bitmap> CreateBitmap(int width, int height,
|
||||
uint32_t* data) override;
|
||||
|
||||
void RenderBatch(Batch* batch) override;
|
||||
void set_clip_rect(const el::Rect& rect) override;
|
||||
|
||||
protected:
|
||||
class ElementalBitmap : public el::graphics::Bitmap {
|
||||
public:
|
||||
ElementalBitmap(ElementalDrawer* drawer, int width, int height,
|
||||
uint32_t* data);
|
||||
~ElementalBitmap();
|
||||
|
||||
int width() override { return width_; }
|
||||
int height() override { return height_; }
|
||||
void set_data(uint32_t* data) override;
|
||||
|
||||
ElementalDrawer* drawer_ = nullptr;
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
std::unique_ptr<ImmediateTexture> texture_;
|
||||
};
|
||||
|
||||
static const uint32_t kMaxVertexBatchSize = 6 * 2048;
|
||||
size_t max_vertex_batch_size() const override { return kMaxVertexBatchSize; }
|
||||
|
||||
Window* window_ = nullptr;
|
||||
GraphicsContext* graphics_context_ = nullptr;
|
||||
|
||||
el::Rect current_clip_;
|
||||
Vertex vertices_[kMaxVertexBatchSize];
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_ELEMENTAL_DRAWER_H_
|
|
@ -7,7 +7,6 @@ project("xenia-ui-gl")
|
|||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"glew",
|
||||
"xenia-base",
|
||||
"xenia-ui",
|
||||
|
@ -17,7 +16,6 @@ project("xenia-ui-gl")
|
|||
"GLEW_MX=1",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
local_platform_files()
|
||||
|
@ -29,7 +27,6 @@ project("xenia-ui-window-gl-demo")
|
|||
kind("WindowedApp")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"gflags",
|
||||
"glew",
|
||||
"imgui",
|
||||
|
@ -45,7 +42,6 @@ project("xenia-ui-window-gl-demo")
|
|||
"GLEW_MX=1",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
files({
|
||||
|
@ -54,9 +50,7 @@ project("xenia-ui-window-gl-demo")
|
|||
project_root.."/src/xenia/base/main_"..platform_suffix..".cc",
|
||||
})
|
||||
files({
|
||||
project_root.."/third_party/elemental-forms/resources.rc",
|
||||
})
|
||||
resincludedirs({
|
||||
project_root,
|
||||
project_root.."/third_party/elemental-forms",
|
||||
})
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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/imgui_dialog.h"
|
||||
|
||||
#include "third_party/imgui/imgui.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
||||
ImGuiDialog::ImGuiDialog(Window* window) : window_(window) {
|
||||
window_->AttachListener(this);
|
||||
had_imgui_active_ = window_->is_imgui_input_enabled();
|
||||
window_->set_imgui_input_enabled(true);
|
||||
}
|
||||
|
||||
ImGuiDialog::~ImGuiDialog() {
|
||||
window_->set_imgui_input_enabled(had_imgui_active_);
|
||||
window_->DetachListener(this);
|
||||
for (auto fence : waiting_fences_) {
|
||||
fence->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiDialog::Then(xe::threading::Fence* fence) {
|
||||
waiting_fences_.push_back(fence);
|
||||
}
|
||||
|
||||
void ImGuiDialog::Close() { has_close_pending_ = true; }
|
||||
|
||||
ImGuiIO& ImGuiDialog::GetIO() { return window_->imgui_drawer()->GetIO(); }
|
||||
|
||||
void ImGuiDialog::OnPaint(UIEvent* e) {
|
||||
// Keep imgui rendering every frame.
|
||||
window_->Invalidate();
|
||||
|
||||
// Draw UI.
|
||||
OnDraw(GetIO());
|
||||
|
||||
// Check to see if the UI closed itself and needs to be deleted.
|
||||
if (has_close_pending_) {
|
||||
OnClose();
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
class MessageBoxDialog : public ImGuiDialog {
|
||||
public:
|
||||
MessageBoxDialog(Window* window, std::string title, std::string body)
|
||||
: ImGuiDialog(window), title_(std::move(title)), body_(std::move(body)) {}
|
||||
|
||||
void OnDraw(ImGuiIO& io) override {
|
||||
if (!has_opened_) {
|
||||
ImGui::OpenPopup(title_.c_str());
|
||||
has_opened_ = true;
|
||||
}
|
||||
if (ImGui::BeginPopupModal(title_.c_str(), nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
char* text = const_cast<char*>(body_.c_str());
|
||||
ImGui::InputTextMultiline(
|
||||
"##body", text, body_.size(), ImVec2(0, 0),
|
||||
ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_ReadOnly);
|
||||
if (ImGui::Button("OK")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
Close();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool has_opened_ = false;
|
||||
std::string title_;
|
||||
std::string body_;
|
||||
};
|
||||
|
||||
ImGuiDialog* ImGuiDialog::ShowMessageBox(Window* window, std::string title,
|
||||
std::string body) {
|
||||
return new MessageBoxDialog(window, std::move(title), std::move(body));
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_IMGUI_DIALOG_H_
|
||||
#define XENIA_UI_IMGUI_DIALOG_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/ui/imgui_drawer.h"
|
||||
#include "xenia/ui/window_listener.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
||||
class ImGuiDialog : private WindowListener {
|
||||
public:
|
||||
~ImGuiDialog();
|
||||
|
||||
// Shows a simple message box containing a text message.
|
||||
// Callers can want for the dialog to close with Wait().
|
||||
// Dialogs retain themselves and will delete themselves when closed.
|
||||
static ImGuiDialog* ShowMessageBox(Window* window, std::string title,
|
||||
std::string body);
|
||||
|
||||
// A fence to signal when the dialog is closed.
|
||||
void Then(xe::threading::Fence* fence);
|
||||
|
||||
protected:
|
||||
ImGuiDialog(Window* window);
|
||||
|
||||
Window* window() const { return window_; }
|
||||
ImGuiIO& GetIO();
|
||||
|
||||
// Closes the dialog and returns to any waiters.
|
||||
void Close();
|
||||
|
||||
virtual void OnShow() {}
|
||||
virtual void OnClose() {}
|
||||
virtual void OnDraw(ImGuiIO& io) {}
|
||||
|
||||
private:
|
||||
void OnPaint(UIEvent* e) override;
|
||||
|
||||
Window* window_ = nullptr;
|
||||
bool had_imgui_active_ = false;
|
||||
bool has_close_pending_ = false;
|
||||
std::vector<xe::threading::Fence*> waiting_fences_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_IMGUI_DIALOG_H_
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "third_party/imgui/imgui.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -24,31 +26,42 @@ const char kProggyTinyCompressedDataBase85[10950 + 1] =
|
|||
static_assert(sizeof(ImmediateVertex) == sizeof(ImDrawVert),
|
||||
"Vertex types must match");
|
||||
|
||||
ImGuiDrawer* ImGuiDrawer::global_drawer_ = nullptr;
|
||||
ImGuiDrawer* ImGuiDrawer::current_drawer_ = nullptr;
|
||||
|
||||
ImGuiDrawer::ImGuiDrawer(xe::ui::Window* window)
|
||||
: window_(window), graphics_context_(window->context()) {
|
||||
assert_null(global_drawer_);
|
||||
global_drawer_ = this;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
ImGuiDrawer::~ImGuiDrawer() {
|
||||
auto previous_state = ImGui::GetInternalState();
|
||||
ImGui::SetInternalState(internal_state_.data());
|
||||
ImGui::Shutdown();
|
||||
if (previous_state != internal_state_.data()) {
|
||||
ImGui::SetInternalState(previous_state);
|
||||
}
|
||||
|
||||
assert_true(global_drawer_ == this);
|
||||
global_drawer_ = nullptr;
|
||||
current_drawer_ = nullptr;
|
||||
}
|
||||
|
||||
void ImGuiDrawer::Initialize() {
|
||||
// Setup ImGui internal state.
|
||||
// This will give us state we can swap to the ImGui globals when in use.
|
||||
internal_state_.resize(ImGui::GetInternalStateSize());
|
||||
ImGui::SetInternalState(internal_state_.data(), true);
|
||||
current_drawer_ = this;
|
||||
|
||||
auto& io = ImGui::GetIO();
|
||||
|
||||
font_atlas_ = std::make_unique<ImFontAtlas>();
|
||||
io.Fonts = font_atlas_.get();
|
||||
|
||||
SetupFont();
|
||||
|
||||
io.DeltaTime = 1.0f / 60.0f;
|
||||
io.RenderDrawListsFn = [](ImDrawData* data) {
|
||||
global_drawer_->RenderDrawLists(data);
|
||||
assert_not_null(current_drawer_);
|
||||
current_drawer_->RenderDrawLists(data);
|
||||
};
|
||||
|
||||
auto& style = ImGui::GetStyle();
|
||||
|
@ -124,7 +137,7 @@ void ImGuiDrawer::Initialize() {
|
|||
}
|
||||
|
||||
void ImGuiDrawer::SetupFont() {
|
||||
auto& io = ImGui::GetIO();
|
||||
auto& io = GetIO();
|
||||
|
||||
ImFontConfig font_config;
|
||||
font_config.OversampleH = font_config.OversampleV = 1;
|
||||
|
@ -136,6 +149,21 @@ void ImGuiDrawer::SetupFont() {
|
|||
io.Fonts->AddFontFromMemoryCompressedBase85TTF(
|
||||
kProggyTinyCompressedDataBase85, 10.0f, &font_config, font_glyph_ranges);
|
||||
|
||||
// TODO(benvanik): jp font on other platforms?
|
||||
// https://github.com/Koruri/kibitaki looks really good, but is 1.5MiB.
|
||||
const char* jp_font_path = "C:\\Windows\\Fonts\\msgothic.ttc";
|
||||
if (xe::filesystem::PathExists(xe::to_wstring(jp_font_path))) {
|
||||
ImFontConfig jp_font_config;
|
||||
jp_font_config.MergeMode = true;
|
||||
jp_font_config.OversampleH = jp_font_config.OversampleV = 1;
|
||||
jp_font_config.PixelSnapH = true;
|
||||
jp_font_config.FontNo = 0;
|
||||
io.Fonts->AddFontFromFileTTF(jp_font_path, 12.0f, &jp_font_config,
|
||||
io.Fonts->GetGlyphRangesJapanese());
|
||||
} else {
|
||||
XELOGW("Unable to load japanese font; jp characters will be boxes");
|
||||
}
|
||||
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
@ -196,71 +224,81 @@ void ImGuiDrawer::RenderDrawLists(ImDrawData* data) {
|
|||
drawer->End();
|
||||
}
|
||||
|
||||
void ImGuiDrawer::SetupDefaultInput() {
|
||||
window_->on_key_char.AddListener([](xe::ui::KeyEvent* e) {
|
||||
auto& io = ImGui::GetIO();
|
||||
if (e->key_code() > 0 && e->key_code() < 0x10000) {
|
||||
io.AddInputCharacter(e->key_code());
|
||||
e->set_handled(true);
|
||||
}
|
||||
});
|
||||
window_->on_key_down.AddListener([](xe::ui::KeyEvent* e) {
|
||||
auto& io = ImGui::GetIO();
|
||||
io.KeysDown[e->key_code()] = true;
|
||||
switch (e->key_code()) {
|
||||
case 16: {
|
||||
io.KeyShift = true;
|
||||
} break;
|
||||
case 17: {
|
||||
io.KeyCtrl = true;
|
||||
} break;
|
||||
}
|
||||
});
|
||||
window_->on_key_up.AddListener([](xe::ui::KeyEvent* e) {
|
||||
auto& io = ImGui::GetIO();
|
||||
io.KeysDown[e->key_code()] = false;
|
||||
switch (e->key_code()) {
|
||||
case 16: {
|
||||
io.KeyShift = false;
|
||||
} break;
|
||||
case 17: {
|
||||
io.KeyCtrl = false;
|
||||
} break;
|
||||
}
|
||||
});
|
||||
window_->on_mouse_down.AddListener([](xe::ui::MouseEvent* e) {
|
||||
auto& io = ImGui::GetIO();
|
||||
io.MousePos = ImVec2(float(e->x()), float(e->y()));
|
||||
switch (e->button()) {
|
||||
case xe::ui::MouseEvent::Button::kLeft: {
|
||||
io.MouseDown[0] = true;
|
||||
} break;
|
||||
case xe::ui::MouseEvent::Button::kRight: {
|
||||
io.MouseDown[1] = true;
|
||||
} break;
|
||||
}
|
||||
});
|
||||
window_->on_mouse_move.AddListener([](xe::ui::MouseEvent* e) {
|
||||
auto& io = ImGui::GetIO();
|
||||
io.MousePos = ImVec2(float(e->x()), float(e->y()));
|
||||
});
|
||||
window_->on_mouse_up.AddListener([](xe::ui::MouseEvent* e) {
|
||||
auto& io = ImGui::GetIO();
|
||||
io.MousePos = ImVec2(float(e->x()), float(e->y()));
|
||||
switch (e->button()) {
|
||||
case xe::ui::MouseEvent::Button::kLeft: {
|
||||
io.MouseDown[0] = false;
|
||||
} break;
|
||||
case xe::ui::MouseEvent::Button::kRight: {
|
||||
io.MouseDown[1] = false;
|
||||
} break;
|
||||
}
|
||||
});
|
||||
window_->on_mouse_wheel.AddListener([](xe::ui::MouseEvent* e) {
|
||||
auto& io = ImGui::GetIO();
|
||||
io.MousePos = ImVec2(float(e->x()), float(e->y()));
|
||||
io.MouseWheel += float(e->dy() / 120.0f);
|
||||
});
|
||||
ImGuiIO& ImGuiDrawer::GetIO() {
|
||||
current_drawer_ = this;
|
||||
ImGui::SetInternalState(internal_state_.data());
|
||||
return ImGui::GetIO();
|
||||
}
|
||||
|
||||
void ImGuiDrawer::OnKeyDown(KeyEvent* e) {
|
||||
auto& io = GetIO();
|
||||
io.KeysDown[e->key_code()] = true;
|
||||
switch (e->key_code()) {
|
||||
case 16: {
|
||||
io.KeyShift = true;
|
||||
} break;
|
||||
case 17: {
|
||||
io.KeyCtrl = true;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiDrawer::OnKeyUp(KeyEvent* e) {
|
||||
auto& io = GetIO();
|
||||
io.KeysDown[e->key_code()] = false;
|
||||
switch (e->key_code()) {
|
||||
case 16: {
|
||||
io.KeyShift = false;
|
||||
} break;
|
||||
case 17: {
|
||||
io.KeyCtrl = false;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiDrawer::OnKeyChar(KeyEvent* e) {
|
||||
auto& io = GetIO();
|
||||
if (e->key_code() > 0 && e->key_code() < 0x10000) {
|
||||
io.AddInputCharacter(e->key_code());
|
||||
e->set_handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiDrawer::OnMouseDown(MouseEvent* e) {
|
||||
auto& io = GetIO();
|
||||
io.MousePos = ImVec2(float(e->x()), float(e->y()));
|
||||
switch (e->button()) {
|
||||
case xe::ui::MouseEvent::Button::kLeft: {
|
||||
io.MouseDown[0] = true;
|
||||
} break;
|
||||
case xe::ui::MouseEvent::Button::kRight: {
|
||||
io.MouseDown[1] = true;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiDrawer::OnMouseMove(MouseEvent* e) {
|
||||
auto& io = GetIO();
|
||||
io.MousePos = ImVec2(float(e->x()), float(e->y()));
|
||||
}
|
||||
|
||||
void ImGuiDrawer::OnMouseUp(MouseEvent* e) {
|
||||
auto& io = GetIO();
|
||||
io.MousePos = ImVec2(float(e->x()), float(e->y()));
|
||||
switch (e->button()) {
|
||||
case xe::ui::MouseEvent::Button::kLeft: {
|
||||
io.MouseDown[0] = false;
|
||||
} break;
|
||||
case xe::ui::MouseEvent::Button::kRight: {
|
||||
io.MouseDown[1] = false;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiDrawer::OnMouseWheel(MouseEvent* e) {
|
||||
auto& io = GetIO();
|
||||
io.MousePos = ImVec2(float(e->x()), float(e->y()));
|
||||
io.MouseWheel += float(e->dy() / 120.0f);
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
|
|
@ -11,10 +11,14 @@
|
|||
#define XENIA_UI_IMGUI_DRAWER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/ui/immediate_drawer.h"
|
||||
#include "xenia/ui/window_listener.h"
|
||||
|
||||
struct ImDrawData;
|
||||
struct ImFontAtlas;
|
||||
struct ImGuiIO;
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
@ -22,12 +26,14 @@ namespace ui {
|
|||
class GraphicsContext;
|
||||
class Window;
|
||||
|
||||
class ImGuiDrawer {
|
||||
class ImGuiDrawer : public WindowListener {
|
||||
public:
|
||||
ImGuiDrawer(Window* window);
|
||||
~ImGuiDrawer();
|
||||
|
||||
void SetupDefaultInput();
|
||||
void SetupDefaultInput() {}
|
||||
|
||||
ImGuiIO& GetIO();
|
||||
|
||||
protected:
|
||||
void Initialize();
|
||||
|
@ -35,11 +41,21 @@ class ImGuiDrawer {
|
|||
|
||||
void RenderDrawLists(ImDrawData* data);
|
||||
|
||||
static ImGuiDrawer* global_drawer_;
|
||||
void OnKeyDown(KeyEvent* e) override;
|
||||
void OnKeyUp(KeyEvent* e) override;
|
||||
void OnKeyChar(KeyEvent* e) override;
|
||||
void OnMouseDown(MouseEvent* e) override;
|
||||
void OnMouseMove(MouseEvent* e) override;
|
||||
void OnMouseUp(MouseEvent* e) override;
|
||||
void OnMouseWheel(MouseEvent* e) override;
|
||||
|
||||
static ImGuiDrawer* current_drawer_;
|
||||
|
||||
Window* window_ = nullptr;
|
||||
GraphicsContext* graphics_context_ = nullptr;
|
||||
|
||||
std::vector<uint8_t> internal_state_;
|
||||
std::unique_ptr<ImFontAtlas> font_atlas_;
|
||||
std::unique_ptr<ImmediateTexture> font_texture_;
|
||||
};
|
||||
|
||||
|
|
|
@ -9,34 +9,15 @@
|
|||
|
||||
#include "xenia/ui/loop.h"
|
||||
|
||||
#include "el/message_handler.h"
|
||||
#include "el/util/metrics.h"
|
||||
#include "el/util/timer.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/threading.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
||||
// The loop designated as being the main loop elemental-forms will use for all
|
||||
// activity.
|
||||
// TODO(benvanik): refactor elemental-forms to now need a global.
|
||||
Loop* elemental_loop_ = nullptr;
|
||||
|
||||
Loop* Loop::elemental_loop() { return elemental_loop_; }
|
||||
|
||||
void Loop::set_elemental_loop(Loop* loop) {
|
||||
assert_null(elemental_loop_);
|
||||
elemental_loop_ = loop;
|
||||
}
|
||||
|
||||
Loop::Loop() = default;
|
||||
|
||||
Loop::~Loop() {
|
||||
if (this == elemental_loop_) {
|
||||
elemental_loop_ = nullptr;
|
||||
}
|
||||
}
|
||||
Loop::~Loop() = default;
|
||||
|
||||
void Loop::PostSynchronous(std::function<void()> fn) {
|
||||
if (is_on_loop_thread()) {
|
||||
|
@ -54,36 +35,3 @@ void Loop::PostSynchronous(std::function<void()> fn) {
|
|||
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
void el::util::RescheduleTimer(uint64_t fire_time) {
|
||||
assert_not_null(xe::ui::elemental_loop_);
|
||||
if (fire_time == el::MessageHandler::kNotSoon) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t now = el::util::GetTimeMS();
|
||||
uint64_t delay_millis = fire_time >= now ? fire_time - now : 0;
|
||||
xe::ui::elemental_loop_->PostDelayed(
|
||||
[]() {
|
||||
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
|
||||
// of the platform timer again with the same time.
|
||||
// ReschedulePlatformTimer(next_fire_time, true);
|
||||
return;
|
||||
}
|
||||
|
||||
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.
|
||||
el::util::RescheduleTimer(el::MessageHandler::GetNextMessageFireTime());
|
||||
},
|
||||
delay_millis);
|
||||
}
|
||||
|
|
|
@ -23,11 +23,6 @@ class Loop {
|
|||
public:
|
||||
static std::unique_ptr<Loop> Create();
|
||||
|
||||
static Loop* elemental_loop();
|
||||
// Sets the loop designated as being the main loop elemental-forms will use
|
||||
// for all activity.
|
||||
static void set_elemental_loop(Loop* loop);
|
||||
|
||||
Loop();
|
||||
virtual ~Loop();
|
||||
|
||||
|
|
|
@ -7,13 +7,11 @@ project("xenia-ui")
|
|||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"elemental-forms",
|
||||
"xenia-base",
|
||||
})
|
||||
defines({
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/elemental-forms/src",
|
||||
project_root.."/build_tools/third_party/gflags/src",
|
||||
})
|
||||
local_platform_files()
|
||||
|
|
|
@ -9,21 +9,11 @@
|
|||
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
#include "el/animation_manager.h"
|
||||
#include "el/elemental_forms.h"
|
||||
#include "el/io/file_manager.h"
|
||||
#include "el/io/posix_file_system.h"
|
||||
#include "el/io/win32_res_file_system_win.h"
|
||||
#include "el/message_handler.h"
|
||||
#include "el/text/font_manager.h"
|
||||
#include "el/util/debug.h"
|
||||
#include "el/util/metrics.h"
|
||||
#include "el/util/string_table.h"
|
||||
#include "el/util/timer.h"
|
||||
#include "third_party/imgui/imgui.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/clock.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/ui/elemental_drawer.h"
|
||||
#include "xenia/ui/imgui_drawer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
@ -39,18 +29,6 @@ constexpr double kDoubleClickDistance = 5;
|
|||
|
||||
constexpr int32_t kMouseWheelDetent = 120;
|
||||
|
||||
// Ref count of elemental initializations.
|
||||
int32_t elemental_initialization_count_ = 0;
|
||||
|
||||
class RootElement : public el::Element {
|
||||
public:
|
||||
explicit RootElement(Window* owner) : owner_(owner) {}
|
||||
void OnInvalid() override { owner_->Invalidate(); }
|
||||
|
||||
private:
|
||||
Window* owner_ = nullptr;
|
||||
};
|
||||
|
||||
Window::Window(Loop* loop, const std::wstring& title)
|
||||
: loop_(loop), title_(title) {}
|
||||
|
||||
|
@ -59,101 +37,58 @@ Window::~Window() {
|
|||
assert_null(context_.get());
|
||||
}
|
||||
|
||||
bool Window::InitializeElemental(Loop* loop, el::graphics::Renderer* renderer) {
|
||||
GraphicsContextLock context_lock(context_.get());
|
||||
|
||||
if (++elemental_initialization_count_ > 1) {
|
||||
return true;
|
||||
void Window::AttachListener(WindowListener* listener) {
|
||||
auto it = std::find(listeners_.begin(), listeners_.end(), listener);
|
||||
if (it != listeners_.end()) {
|
||||
return;
|
||||
}
|
||||
listeners_.push_back(listener);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
// Need to pass off the Loop we want Elemental to use.
|
||||
// TODO(benvanik): give the callback to elemental instead.
|
||||
Loop::set_elemental_loop(loop);
|
||||
|
||||
if (!el::Initialize(renderer)) {
|
||||
XELOGE("Failed to initialize elemental core");
|
||||
return false;
|
||||
void Window::DetachListener(WindowListener* listener) {
|
||||
auto it = std::find(listeners_.begin(), listeners_.end(), listener);
|
||||
if (it == listeners_.end()) {
|
||||
return;
|
||||
}
|
||||
listeners_.erase(it);
|
||||
}
|
||||
|
||||
el::io::FileManager::RegisterFileSystem(
|
||||
std::make_unique<el::io::Win32ResFileSystem>("IDR_default_resources_"));
|
||||
el::io::FileManager::RegisterFileSystem(
|
||||
std::make_unique<el::io::PosixFileSystem>(
|
||||
"third_party/elemental-forms/testbed/resources/"));
|
||||
|
||||
// Load default translations.
|
||||
el::util::StringTable::get()->Load("default_language/language_en.tb.txt");
|
||||
|
||||
// Load the default skin. Hosting controls may load additional skins later.
|
||||
if (!LoadSkin("default_skin/skin.tb.txt")) {
|
||||
XELOGE("Failed to load default skin");
|
||||
return false;
|
||||
void Window::set_imgui_input_enabled(bool value) {
|
||||
if (value == is_imgui_input_enabled_) {
|
||||
return;
|
||||
}
|
||||
is_imgui_input_enabled_ = value;
|
||||
if (!value) {
|
||||
DetachListener(imgui_drawer_.get());
|
||||
} else {
|
||||
AttachListener(imgui_drawer_.get());
|
||||
}
|
||||
|
||||
// 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("fonts/vera.ttf", "Default");
|
||||
#endif
|
||||
#ifdef EL_FONT_RENDERER_TBBF
|
||||
font_manager->AddFontInfo("fonts/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());
|
||||
assert(font != nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Window::OnCreate() { return true; }
|
||||
|
||||
bool Window::MakeReady() {
|
||||
renderer_ = std::make_unique<ElementalDrawer>(this);
|
||||
|
||||
// Initialize elemental.
|
||||
// TODO(benvanik): once? Do we care about multiple controls?
|
||||
if (!InitializeElemental(loop_, renderer_.get())) {
|
||||
XELOGE("Unable to initialize elemental-forms");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(benvanik): setup elements.
|
||||
root_element_ = std::make_unique<RootElement>(this);
|
||||
root_element_->set_background_skin(TBIDC("background"));
|
||||
root_element_->set_rect({0, 0, width(), height()});
|
||||
|
||||
// el::util::ShowDebugInfoSettingsForm(root_element_.get());
|
||||
imgui_drawer_ = std::make_unique<xe::ui::ImGuiDrawer>(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Window::OnMainMenuChange() {}
|
||||
void Window::OnMainMenuChange() {
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnMainMenuChange();
|
||||
}
|
||||
}
|
||||
|
||||
void Window::OnClose() {
|
||||
UIEvent e(this);
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnClosing(&e);
|
||||
}
|
||||
on_closing(&e);
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnClosed(&e);
|
||||
}
|
||||
on_closed(&e);
|
||||
}
|
||||
|
||||
|
@ -162,24 +97,12 @@ void Window::OnDestroy() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (--elemental_initialization_count_ == 0) {
|
||||
el::Shutdown();
|
||||
}
|
||||
imgui_drawer_.reset();
|
||||
|
||||
// Context must go last.
|
||||
root_element_.reset();
|
||||
renderer_.reset();
|
||||
context_.reset();
|
||||
}
|
||||
|
||||
bool Window::LoadLanguage(std::string filename) {
|
||||
return el::util::StringTable::get()->Load(filename.c_str());
|
||||
}
|
||||
|
||||
bool Window::LoadSkin(std::string filename) {
|
||||
return el::Skin::get()->Load(filename.c_str());
|
||||
}
|
||||
|
||||
void Window::Layout() {
|
||||
UIEvent e(this);
|
||||
OnLayout(&e);
|
||||
|
@ -187,213 +110,106 @@ void Window::Layout() {
|
|||
|
||||
void Window::Invalidate() {}
|
||||
|
||||
void Window::OnResize(UIEvent* e) { on_resize(e); }
|
||||
void Window::OnResize(UIEvent* e) {
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnResize(e);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::OnLayout(UIEvent* e) {
|
||||
on_layout(e);
|
||||
if (!root_element()) {
|
||||
return;
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnLayout(e);
|
||||
}
|
||||
// TODO(benvanik): subregion?
|
||||
root_element()->set_rect({0, 0, width(), height()});
|
||||
}
|
||||
|
||||
void Window::OnPaint(UIEvent* e) {
|
||||
if (!renderer()) {
|
||||
if (!context_) {
|
||||
return;
|
||||
}
|
||||
|
||||
++frame_count_;
|
||||
++fps_frame_count_;
|
||||
uint64_t now_ns = xe::Clock::QueryHostSystemTime();
|
||||
if (now_ns > fps_update_time_ + 1000 * 10000) {
|
||||
if (now_ns > fps_update_time_ns_ + 1000 * 10000) {
|
||||
fps_ = static_cast<uint32_t>(
|
||||
fps_frame_count_ /
|
||||
(static_cast<double>(now_ns - fps_update_time_) / 10000000.0));
|
||||
fps_update_time_ = now_ns;
|
||||
(static_cast<double>(now_ns - fps_update_time_ns_) / 10000000.0));
|
||||
fps_update_time_ns_ = now_ns;
|
||||
fps_frame_count_ = 0;
|
||||
}
|
||||
|
||||
// Update TB (run animations, handle deferred input, etc).
|
||||
el::AnimationManager::Update();
|
||||
if (root_element()) {
|
||||
root_element()->InvokeProcessStates();
|
||||
root_element()->InvokeProcess();
|
||||
}
|
||||
|
||||
GraphicsContextLock context_lock(context_.get());
|
||||
|
||||
context_->BeginSwap();
|
||||
|
||||
on_painting(e);
|
||||
|
||||
renderer()->BeginPaint(width(), height());
|
||||
|
||||
// Render entire control hierarchy.
|
||||
if (root_element()) {
|
||||
root_element()->InvokePaint(el::Element::PaintProps());
|
||||
// Prepare ImGui for use this frame.
|
||||
auto& io = imgui_drawer_->GetIO();
|
||||
if (!last_paint_time_ns_) {
|
||||
io.DeltaTime = 0.0f;
|
||||
last_paint_time_ns_ = now_ns;
|
||||
} else {
|
||||
io.DeltaTime = (now_ns - last_paint_time_ns_) / 10000000.0f;
|
||||
last_paint_time_ns_ = now_ns;
|
||||
}
|
||||
io.DisplaySize =
|
||||
ImVec2(static_cast<float>(width()), static_cast<float>(height()));
|
||||
ImGui::NewFrame();
|
||||
|
||||
context_->BeginSwap();
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnPainting(e);
|
||||
}
|
||||
on_painting(e);
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnPaint(e);
|
||||
}
|
||||
on_paint(e);
|
||||
|
||||
if (root_element() && kShowPresentFps) {
|
||||
// Render debug overlay.
|
||||
root_element()->computed_font()->DrawString(
|
||||
5, 5, el::Color(255, 0, 0),
|
||||
el::format_string("Frame %lld", frame_count_));
|
||||
root_element()->computed_font()->DrawString(
|
||||
5, 20, el::Color(255, 0, 0),
|
||||
el::format_string("Present FPS: %d", fps_));
|
||||
// Flush ImGui buffers before we swap.
|
||||
ImGui::Render();
|
||||
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnPainted(e);
|
||||
}
|
||||
|
||||
renderer()->EndPaint();
|
||||
|
||||
on_painted(e);
|
||||
|
||||
context_->EndSwap();
|
||||
|
||||
// If animations are running, reinvalidate immediately.
|
||||
if (root_element()) {
|
||||
if (el::AnimationManager::has_running_animations()) {
|
||||
root_element()->Invalidate();
|
||||
}
|
||||
if (kContinuousRepaint) {
|
||||
// Force an immediate repaint, always.
|
||||
root_element()->Invalidate();
|
||||
}
|
||||
} else {
|
||||
if (kContinuousRepaint) {
|
||||
Invalidate();
|
||||
}
|
||||
if (kContinuousRepaint) {
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void Window::OnVisible(UIEvent* e) { on_visible(e); }
|
||||
void Window::OnVisible(UIEvent* e) {
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnVisible(e);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::OnHidden(UIEvent* e) { on_hidden(e); }
|
||||
void Window::OnHidden(UIEvent* e) {
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnHidden(e);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::OnGotFocus(UIEvent* e) { on_got_focus(e); }
|
||||
void Window::OnGotFocus(UIEvent* e) {
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnGotFocus(e);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::OnLostFocus(UIEvent* e) {
|
||||
modifier_shift_pressed_ = false;
|
||||
modifier_cntrl_pressed_ = false;
|
||||
modifier_alt_pressed_ = false;
|
||||
modifier_super_pressed_ = false;
|
||||
last_click_time_ = 0;
|
||||
on_lost_focus(e);
|
||||
}
|
||||
|
||||
el::ModifierKeys Window::GetModifierKeys() {
|
||||
auto modifiers = el::ModifierKeys::kNone;
|
||||
if (modifier_shift_pressed_) {
|
||||
modifiers |= el::ModifierKeys::kShift;
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnLostFocus(e);
|
||||
}
|
||||
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 Window::OnKeyPress(KeyEvent* e, bool is_down, bool is_char) {
|
||||
if (!root_element()) {
|
||||
return;
|
||||
}
|
||||
auto special_key = el::SpecialKey::kUndefined;
|
||||
if (!is_char) {
|
||||
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(std::move(ev));
|
||||
e->set_handled(true);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
modifier_shift_pressed_ = is_down;
|
||||
break;
|
||||
|
@ -409,71 +225,6 @@ void Window::OnKeyPress(KeyEvent* e, bool is_down, bool is_char) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CheckShortcutKey(e, special_key, is_down)) {
|
||||
int key_code = 0;
|
||||
if (is_char) {
|
||||
key_code = e->key_code();
|
||||
if (key_code < 32 || (key_code > 126 && key_code < 160)) {
|
||||
key_code = 0;
|
||||
}
|
||||
}
|
||||
e->set_handled(root_element()->InvokeKey(key_code, special_key,
|
||||
GetModifierKeys(), is_down));
|
||||
}
|
||||
}
|
||||
|
||||
bool Window::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(std::move(ev))) {
|
||||
return false;
|
||||
}
|
||||
e->set_handled(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Window::OnKeyDown(KeyEvent* e) {
|
||||
|
@ -481,6 +232,12 @@ void Window::OnKeyDown(KeyEvent* e) {
|
|||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnKeyDown(e);
|
||||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
OnKeyPress(e, true, false);
|
||||
}
|
||||
|
||||
|
@ -489,12 +246,21 @@ void Window::OnKeyUp(KeyEvent* e) {
|
|||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnKeyUp(e);
|
||||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
OnKeyPress(e, false, false);
|
||||
}
|
||||
|
||||
void Window::OnKeyChar(KeyEvent* e) {
|
||||
OnKeyPress(e, true, true);
|
||||
on_key_char(e);
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnKeyChar(e);
|
||||
}
|
||||
OnKeyPress(e, false, true);
|
||||
}
|
||||
|
||||
|
@ -503,31 +269,11 @@ void Window::OnMouseDown(MouseEvent* e) {
|
|||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnMouseDown(e);
|
||||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -536,11 +282,12 @@ void Window::OnMouseMove(MouseEvent* e) {
|
|||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
if (!root_element()) {
|
||||
return;
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnMouseMove(e);
|
||||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
root_element()->InvokePointerMove(e->x(), e->y(), GetModifierKeys(), kTouch);
|
||||
e->set_handled(true);
|
||||
}
|
||||
|
||||
void Window::OnMouseUp(MouseEvent* e) {
|
||||
|
@ -548,24 +295,11 @@ void Window::OnMouseUp(MouseEvent* e) {
|
|||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
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(std::move(ev));
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnMouseUp(e);
|
||||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
e->set_handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -574,12 +308,12 @@ void Window::OnMouseWheel(MouseEvent* e) {
|
|||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
if (!root_element()) {
|
||||
return;
|
||||
for (auto listener : listeners_) {
|
||||
listener->OnMouseWheel(e);
|
||||
if (e->is_handled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
e->set_handled(root_element()->InvokeWheel(e->x(), e->y(), e->dx(),
|
||||
-e->dy() / kMouseWheelDetent,
|
||||
GetModifierKeys()));
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
|
|
@ -13,13 +13,12 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "el/element.h"
|
||||
#include "el/graphics/renderer.h"
|
||||
#include "xenia/base/delegate.h"
|
||||
#include "xenia/ui/graphics_context.h"
|
||||
#include "xenia/ui/loop.h"
|
||||
#include "xenia/ui/menu_item.h"
|
||||
#include "xenia/ui/ui_event.h"
|
||||
#include "xenia/ui/window_listener.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
@ -27,6 +26,8 @@ namespace ui {
|
|||
typedef void* NativePlatformHandle;
|
||||
typedef void* NativeWindowHandle;
|
||||
|
||||
class ImGuiDrawer;
|
||||
|
||||
class Window {
|
||||
public:
|
||||
static std::unique_ptr<Window> Create(Loop* loop, const std::wstring& title);
|
||||
|
@ -71,8 +72,12 @@ class Window {
|
|||
int32_t bottom) = 0;
|
||||
|
||||
GraphicsContext* context() const { return context_.get(); }
|
||||
el::graphics::Renderer* renderer() const { return renderer_.get(); }
|
||||
el::Element* root_element() const { return root_element_.get(); }
|
||||
ImGuiDrawer* imgui_drawer() const { return imgui_drawer_.get(); }
|
||||
bool is_imgui_input_enabled() const { return is_imgui_input_enabled_; }
|
||||
void set_imgui_input_enabled(bool value);
|
||||
|
||||
void AttachListener(WindowListener* listener);
|
||||
void DetachListener(WindowListener* listener);
|
||||
|
||||
virtual bool Initialize() { return true; }
|
||||
void set_context(std::unique_ptr<GraphicsContext> context) {
|
||||
|
@ -82,9 +87,6 @@ class Window {
|
|||
}
|
||||
}
|
||||
|
||||
bool LoadLanguage(std::string filename);
|
||||
bool LoadSkin(std::string filename);
|
||||
|
||||
void Layout();
|
||||
virtual void Invalidate();
|
||||
|
||||
|
@ -94,20 +96,10 @@ class Window {
|
|||
Delegate<UIEvent*> on_closing;
|
||||
Delegate<UIEvent*> on_closed;
|
||||
|
||||
Delegate<int> on_command;
|
||||
|
||||
Delegate<UIEvent*> on_resize;
|
||||
Delegate<UIEvent*> on_layout;
|
||||
Delegate<UIEvent*> on_painting;
|
||||
Delegate<UIEvent*> on_paint;
|
||||
Delegate<UIEvent*> on_painted;
|
||||
|
||||
Delegate<UIEvent*> on_visible;
|
||||
Delegate<UIEvent*> on_hidden;
|
||||
|
||||
Delegate<UIEvent*> on_got_focus;
|
||||
Delegate<UIEvent*> on_lost_focus;
|
||||
|
||||
Delegate<KeyEvent*> on_key_down;
|
||||
Delegate<KeyEvent*> on_key_up;
|
||||
Delegate<KeyEvent*> on_key_char;
|
||||
|
@ -122,9 +114,6 @@ class Window {
|
|||
|
||||
virtual bool MakeReady();
|
||||
|
||||
virtual bool InitializeElemental(Loop* loop,
|
||||
el::graphics::Renderer* renderer);
|
||||
|
||||
virtual bool OnCreate();
|
||||
virtual void OnMainMenuChange();
|
||||
virtual void OnClose();
|
||||
|
@ -149,9 +138,7 @@ class Window {
|
|||
virtual void OnMouseUp(MouseEvent* e);
|
||||
virtual void OnMouseWheel(MouseEvent* e);
|
||||
|
||||
el::ModifierKeys GetModifierKeys();
|
||||
void OnKeyPress(KeyEvent* e, bool is_down, bool is_char);
|
||||
bool CheckShortcutKey(KeyEvent* e, el::SpecialKey special_key, bool is_down);
|
||||
|
||||
Loop* loop_ = nullptr;
|
||||
std::unique_ptr<MenuItem> main_menu_;
|
||||
|
@ -160,24 +147,24 @@ class Window {
|
|||
int32_t height_ = 0;
|
||||
bool has_focus_ = true;
|
||||
bool is_cursor_visible_ = true;
|
||||
bool is_imgui_input_enabled_ = false;
|
||||
|
||||
std::unique_ptr<GraphicsContext> context_;
|
||||
std::unique_ptr<el::graphics::Renderer> renderer_;
|
||||
std::unique_ptr<el::Element> root_element_;
|
||||
std::unique_ptr<ImGuiDrawer> imgui_drawer_;
|
||||
|
||||
uint32_t frame_count_ = 0;
|
||||
uint32_t fps_ = 0;
|
||||
uint64_t fps_update_time_ = 0;
|
||||
uint64_t fps_update_time_ns_ = 0;
|
||||
uint64_t fps_frame_count_ = 0;
|
||||
uint64_t last_paint_time_ns_ = 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;
|
||||
|
||||
// All currently-attached listeners that get event notifications.
|
||||
std::vector<WindowListener*> listeners_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <cstring>
|
||||
|
||||
#include "third_party/elemental-forms/src/el/util/debug.h"
|
||||
#include "third_party/imgui/imgui.h"
|
||||
#include "xenia/base/clock.h"
|
||||
#include "xenia/base/logging.h"
|
||||
|
@ -20,6 +19,7 @@
|
|||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/ui/graphics_provider.h"
|
||||
#include "xenia/ui/imgui_dialog.h"
|
||||
#include "xenia/ui/imgui_drawer.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
|
@ -71,8 +71,7 @@ int window_demo_main(const std::vector<std::wstring>& args) {
|
|||
|
||||
// Create the graphics context used for drawing and setup the window.
|
||||
std::unique_ptr<GraphicsProvider> graphics_provider;
|
||||
std::unique_ptr<xe::ui::ImGuiDrawer> imgui_drawer;
|
||||
loop->PostSynchronous([&window, &graphics_provider, &imgui_drawer]() {
|
||||
loop->PostSynchronous([&window, &graphics_provider]() {
|
||||
// Create graphics provider and an initial context for the window.
|
||||
// The window will finish initialization wtih the context (loading
|
||||
// resources, etc).
|
||||
|
@ -83,19 +82,14 @@ int window_demo_main(const std::vector<std::wstring>& args) {
|
|||
GraphicsContextLock context_lock(window->context());
|
||||
Profiler::set_window(window.get());
|
||||
|
||||
// Initialize the ImGui renderer we'll use.
|
||||
imgui_drawer = std::make_unique<xe::ui::ImGuiDrawer>(window.get());
|
||||
imgui_drawer->SetupDefaultInput();
|
||||
|
||||
// Show the elemental-forms debug UI so we can see it working.
|
||||
el::util::ShowDebugInfoSettingsForm(window->root_element());
|
||||
// Enable imgui input.
|
||||
window->set_imgui_input_enabled(true);
|
||||
});
|
||||
|
||||
window->on_closed.AddListener(
|
||||
[&loop, &graphics_provider, &imgui_drawer](xe::ui::UIEvent* e) {
|
||||
[&loop, &graphics_provider](xe::ui::UIEvent* e) {
|
||||
loop->Quit();
|
||||
XELOGI("User-initiated death!");
|
||||
imgui_drawer.reset();
|
||||
graphics_provider.reset();
|
||||
exit(1);
|
||||
});
|
||||
|
@ -110,23 +104,11 @@ int window_demo_main(const std::vector<std::wstring>& args) {
|
|||
});
|
||||
|
||||
window->on_painting.AddListener([&](xe::ui::UIEvent* e) {
|
||||
auto& io = ImGui::GetIO();
|
||||
auto current_tick_count = Clock::QueryHostTickCount();
|
||||
static uint64_t last_draw_tick_count = 0;
|
||||
io.DeltaTime = (current_tick_count - last_draw_tick_count) /
|
||||
static_cast<float>(Clock::host_tick_frequency());
|
||||
last_draw_tick_count = current_tick_count;
|
||||
|
||||
io.DisplaySize = ImVec2(static_cast<float>(window->width()),
|
||||
static_cast<float>(window->height()));
|
||||
|
||||
ImGui::NewFrame();
|
||||
auto& io = window->imgui_drawer()->GetIO();
|
||||
|
||||
ImGui::ShowTestWindow();
|
||||
ImGui::ShowMetricsWindow();
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
// Continuous paint.
|
||||
window->Invalidate();
|
||||
});
|
||||
|
@ -134,8 +116,6 @@ int window_demo_main(const std::vector<std::wstring>& args) {
|
|||
// Wait until we are exited.
|
||||
loop->AwaitQuit();
|
||||
|
||||
imgui_drawer.reset();
|
||||
|
||||
loop->PostSynchronous([&graphics_provider]() { graphics_provider.reset(); });
|
||||
window.reset();
|
||||
loop.reset();
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_WINDOW_LISTENER_H_
|
||||
#define XENIA_UI_WINDOW_LISTENER_H_
|
||||
|
||||
#include "xenia/ui/ui_event.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
||||
// Virtual interface for types that want to listen for Window events.
|
||||
// Use Window::AttachListener and Window::DetachListener to manage active
|
||||
// listeners.
|
||||
class WindowListener {
|
||||
public:
|
||||
virtual ~WindowListener() = default;
|
||||
|
||||
virtual void OnMainMenuChange() {}
|
||||
virtual void OnClosing(UIEvent* e) {}
|
||||
virtual void OnClosed(UIEvent* e) {}
|
||||
|
||||
virtual void OnResize(UIEvent* e) {}
|
||||
virtual void OnLayout(UIEvent* e) {}
|
||||
virtual void OnPainting(UIEvent* e) {}
|
||||
virtual void OnPaint(UIEvent* e) {}
|
||||
virtual void OnPainted(UIEvent* e) {}
|
||||
|
||||
virtual void OnVisible(UIEvent* e) {}
|
||||
virtual void OnHidden(UIEvent* e) {}
|
||||
|
||||
virtual void OnGotFocus(UIEvent* e) {}
|
||||
virtual void OnLostFocus(UIEvent* e) {}
|
||||
|
||||
virtual void OnKeyDown(KeyEvent* e) {}
|
||||
virtual void OnKeyUp(KeyEvent* e) {}
|
||||
virtual void OnKeyChar(KeyEvent* e) {}
|
||||
|
||||
virtual void OnMouseDown(MouseEvent* e) {}
|
||||
virtual void OnMouseMove(MouseEvent* e) {}
|
||||
virtual void OnMouseUp(MouseEvent* e) {}
|
||||
virtual void OnMouseWheel(MouseEvent* e) {}
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_WINDOW_LISTENER_H_
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 3e7d961fea8cf8a857292d21106834c8566903f2
|
14
xenia-build
14
xenia-build
|
@ -775,25 +775,13 @@ def find_xenia_source_files():
|
|||
if name.endswith(('.cc', '.c', '.h', '.inl'))]
|
||||
|
||||
|
||||
def find_elemental_source_files():
|
||||
"""Gets all elemental-forms source files in the project.
|
||||
|
||||
Returns:
|
||||
A list of file paths.
|
||||
"""
|
||||
return [os.path.join(root, name)
|
||||
for root, dirs, files in os.walk('third_party/elemental-forms/src/')
|
||||
for name in files
|
||||
if name.endswith(('.cc', '.c', '.h', '.inl'))]
|
||||
|
||||
|
||||
def find_all_source_files():
|
||||
"""Gets all interesting source files in the project.
|
||||
|
||||
Returns:
|
||||
A list of file paths.
|
||||
"""
|
||||
return find_xenia_source_files() + find_elemental_source_files()
|
||||
return find_xenia_source_files()
|
||||
|
||||
|
||||
class LintCommand(Command):
|
||||
|
|
Loading…
Reference in New Issue