Dropping elemental-forms and swapping out with imgui.

Too much code for such little use. This should simplify porting.
This commit is contained in:
Ben Vanik
2015-12-26 20:25:24 -08:00
parent 2071286040
commit 0e58208add
39 changed files with 644 additions and 1100 deletions

View File

@@ -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

View File

@@ -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_

View File

@@ -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",
})

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_;
};

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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();

View File

@@ -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_