12 #include <imgui_internal.h>
18 #include <unordered_map>
41 namespace visualization {
46 static constexpr
int CENTERED_X = -10000;
47 static constexpr
int CENTERED_Y = -10000;
48 static constexpr
int AUTOSIZE_WIDTH = 0;
49 static constexpr
int AUTOSIZE_HEIGHT = 0;
52 void UpdateImGuiForScaling(
float new_scaling) {
53 ImGuiStyle& style = ImGui::GetStyle();
55 style.FrameRounding *= new_scaling;
58 void ChangeAllRenderQuality(
60 const std::vector<std::shared_ptr<Widget>>& children) {
61 for (
auto child : children) {
62 auto sw = std::dynamic_pointer_cast<SceneWidget>(child);
64 sw->SetRenderQuality(quality);
66 if (child->GetChildren().size() > 0) {
67 ChangeAllRenderQuality(quality, child->GetChildren());
73 struct ImguiWindowContext :
public FontContext {
80 void* GetFont(
FontId font_id) {
return this->fonts[font_id]; }
90 this->fonts.reserve(font_descs.size());
91 for (
auto& fd : font_descs) {
92 this->fonts.push_back(AddFont(fd));
95 ImGuiIO& io = ImGui::GetIO();
96 unsigned char* pixels;
97 int textureW, textureH, bytesPerPx;
98 io.Fonts->GetTexDataAsAlpha8(&pixels, &textureW, &textureH,
103 if (textureW == 0 || textureH == 0) {
105 "Got zero-byte font texture; ignoring custom fonts");
108 io.Fonts->AddFontFromFileTTF(this->theme->font_path.c_str(),
109 float(this->theme->font_size));
110 for (
unsigned int i = 1; i < font_descs.size(); ++i) {
111 this->fonts[i] = this->fonts[0];
113 io.Fonts->GetTexDataAsAlpha8(&pixels, &textureW, &textureH,
116 this->imgui_bridge->CreateAtlasTextureAlpha8(pixels, textureW, textureH,
121 ImFont* AddFont(
const FontDescription& fd) {
124 ImFont* imfont =
nullptr;
126 ImGuiIO& io = ImGui::GetIO();
128 if (fd.point_size_ <= 0) {
129 point_size = float(this->theme->font_size);
131 point_size = this->scaling * float(fd.point_size_);
135 if (fd.ranges_.size() == 1) {
136 imfont = io.Fonts->AddFontFromFileTTF(fd.ranges_[0].path.c_str(),
139 imfont = io.Fonts->AddFontFromFileTTF(
140 fd.ranges_[0].path.c_str(), point_size,
NULL,
141 io.Fonts->GetGlyphRangesDefault());
145 config.MergeMode =
true;
146 for (
auto& r : fd.ranges_) {
147 if (!r.lang.empty()) {
148 const ImWchar* range;
149 if (r.lang ==
"en") {
151 }
else if (r.lang ==
"ja") {
152 range = io.Fonts->GetGlyphRangesJapanese();
153 }
else if (r.lang ==
"ko") {
154 range = io.Fonts->GetGlyphRangesKorean();
155 }
else if (r.lang ==
"th") {
156 range = io.Fonts->GetGlyphRangesThai();
157 }
else if (r.lang ==
"vi") {
158 range = io.Fonts->GetGlyphRangesVietnamese();
159 }
else if (r.lang ==
"zh") {
160 range = io.Fonts->GetGlyphRangesChineseSimplifiedCommon();
161 }
else if (r.lang ==
"zh_all") {
162 range = io.Fonts->GetGlyphRangesChineseFull();
165 range = io.Fonts->GetGlyphRangesCyrillic();
167 imfont = io.Fonts->AddFontFromFileTTF(
168 r.path.c_str(), point_size, &config, range);
169 }
else if (!r.code_points.empty()) {
172 ImVector<ImWchar> range;
173 ImFontGlyphRangesBuilder builder;
174 for (
auto c : r.code_points) {
177 builder.BuildRanges(&range);
178 imfont = io.Fonts->AddFontFromFileTTF(
179 r.path.c_str(), point_size, &config, range.Data);
192 struct Window::Impl {
238 :
Window(title, CENTERED_X, CENTERED_Y, AUTOSIZE_WIDTH, AUTOSIZE_HEIGHT) {}
260 impl_->wants_auto_center_ = (x == CENTERED_X || y == CENTERED_Y);
261 impl_->wants_auto_size_ =
262 (
width == AUTOSIZE_WIDTH ||
height == AUTOSIZE_HEIGHT);
265 (impl_->wants_auto_size_ || impl_->wants_auto_center_));
277 impl_->window_ = ws.CreateOSWindow(
this, initial_width, initial_height,
278 title.c_str(), ws_flags);
279 impl_->title_ = title;
281 if (x != CENTERED_X || y != CENTERED_Y) {
282 ws.SetWindowPos(impl_->window_, x, y);
285 auto&
theme = impl_->theme_;
286 impl_->imgui_.context = ImGui::CreateContext();
287 auto oldContext = MakeDrawContextCurrent();
297 float scaling = ws.GetUIScaleFactor(impl_->window_);
298 impl_->imgui_.scaling =
scaling;
300 impl_->theme_.font_size =
301 int(std::round(impl_->theme_.font_size *
scaling));
302 impl_->theme_.default_margin =
303 int(std::round(impl_->theme_.default_margin *
scaling));
304 impl_->theme_.default_layout_spacing =
305 int(std::round(impl_->theme_.default_layout_spacing *
scaling));
307 ImGui::StyleColorsDark();
308 ImGuiStyle& style = ImGui::GetStyle();
309 style.WindowPadding = ImVec2(0, 0);
310 style.WindowRounding = 0;
311 style.WindowBorderSize = 0;
312 style.FrameBorderSize = float(
theme.border_width);
313 style.FrameRounding = float(
theme.border_radius);
314 style.ChildRounding = float(
theme.border_radius);
320 style.Colors[ImGuiCol_ButtonHovered] =
322 style.Colors[ImGuiCol_ButtonActive] =
325 style.Colors[ImGuiCol_FrameBg] =
327 style.Colors[ImGuiCol_FrameBgHovered] =
329 style.Colors[ImGuiCol_FrameBgActive] =
330 style.Colors[ImGuiCol_FrameBgHovered];
332 style.Colors[ImGuiCol_SliderGrabActive] =
338 ImGuiIO& io = ImGui::GetIO();
339 io.IniFilename =
nullptr;
343 io.KeyMap[ImGuiKey_Tab] =
KEY_TAB;
344 io.KeyMap[ImGuiKey_LeftArrow] =
KEY_LEFT;
345 io.KeyMap[ImGuiKey_RightArrow] =
KEY_RIGHT;
346 io.KeyMap[ImGuiKey_UpArrow] =
KEY_UP;
347 io.KeyMap[ImGuiKey_DownArrow] =
KEY_DOWN;
350 io.KeyMap[ImGuiKey_Home] =
KEY_HOME;
351 io.KeyMap[ImGuiKey_End] =
KEY_END;
355 io.KeyMap[ImGuiKey_Space] =
' ';
358 io.KeyMap[ImGuiKey_A] =
'a';
359 io.KeyMap[ImGuiKey_C] =
'c';
360 io.KeyMap[ImGuiKey_V] =
'v';
361 io.KeyMap[ImGuiKey_X] =
'x';
362 io.KeyMap[ImGuiKey_Y] =
'y';
363 io.KeyMap[ImGuiKey_Z] =
'z';
370 io.ClipboardUserData =
nullptr;
376 RestoreDrawContext(oldContext);
381 void Window::CreateRenderer() {
383 auto old_context = MakeDrawContextCurrent();
390 impl_->renderer_->SetClearColor({1.0f, 1.0f, 1.0f, 1.0f});
392 impl_->imgui_.imgui_bridge =
393 std::make_unique<ImguiFilamentBridge>(impl_->renderer_,
GetSize());
394 impl_->imgui_.theme = &impl_->theme_;
395 impl_->imgui_.CreateFonts();
397 RestoreDrawContext(old_context);
401 impl_->active_dialog_.reset();
402 impl_->children_.clear();
403 ImGui::SetCurrentContext(impl_->imgui_.context);
404 ImGui::DestroyContext();
405 delete impl_->renderer_;
413 impl_->window_ =
nullptr;
422 return webrtc_ws->GetWindowUID(impl_->window_);
424 return "window_undefined";
427 return "window_undefined";
432 return impl_->children_;
435 void* Window::MakeDrawContextCurrent()
const {
436 auto old_context = ImGui::GetCurrentContext();
437 ImGui::SetCurrentContext(impl_->imgui_.context);
441 void Window::RestoreDrawContext(
void* oldContext)
const {
442 ImGui::SetCurrentContext((ImGuiContext*)oldContext);
448 return *impl_->renderer_;
454 auto size = ws.GetWindowSize(impl_->window_);
461 ws.SetWindowSize(impl_->window_, r.
width, r.
height);
467 impl_->title_ = title;
469 impl_->window_, title);
480 if (impl_->children_.empty()) {
481 return Size(
int(std::round(640.0f * impl_->imgui_.scaling)),
482 int(std::round(480.0f * impl_->imgui_.scaling)));
485 Rect bbox(0, 0, 0, 0);
486 for (
auto& child : impl_->children_) {
489 Rect r(child->GetFrame().x, child->GetFrame().y, pref.width,
505 impl_->deferred_until_draw_.push(auto_size);
511 auto resize = [
this,
size ]() {
512 auto scaling = this->impl_->imgui_.scaling;
518 impl_->deferred_until_before_draw_.push(resize);
529 MakeDrawContextCurrent();
531 if (menubar && impl_->draw_menu_) {
532 menu_height = menubar->CalcHeight(
GetTheme());
535 return Rect(0, menu_height,
size.width,
size.height - menu_height);
546 return Point(global_y - pos.x, global_y - pos.y);
560 if (impl_->on_close_) {
561 bool should_close = impl_->on_close_();
577 if (impl_->is_drawing_) {
578 impl_->needs_redraw_ =
true;
598 impl_->children_.push_back(w);
599 impl_->needs_layout_ =
true;
604 impl_->menu_callbacks_[item_id] =
callback;
620 if (impl_->active_dialog_) {
623 impl_->active_dialog_ = dlg;
626 auto deferred_layout = [
this, dlg]() {
630 int w = dlg->GetFrame().width;
631 int h = dlg->GetFrame().height;
638 w =
std::min(w,
int(std::round(0.8 * content_rect.width)));
639 h =
std::min(h,
int(std::round(0.8 * content_rect.height)));
640 dlg->SetFrame(
gui::Rect((content_rect.width - w) / 2,
641 (content_rect.height - h) / 2, w, h));
645 impl_->deferred_until_draw_.push(deferred_layout);
649 if (impl_->focus_widget_ == impl_->active_dialog_.get()) {
652 impl_->active_dialog_.reset();
664 auto dlg = std::make_shared<Dialog>(title);
665 auto layout = std::make_shared<Vert>(em, margins);
666 layout->AddChild(std::make_shared<Label>(message));
667 auto ok = std::make_shared<Button>(
"Ok");
668 ok->SetOnClicked([
this]() { this->
CloseDialog(); });
670 dlg->AddChild(layout);
675 impl_->draw_menu_ = show;
682 if (impl_->children_.size() == 1) {
684 impl_->children_[0]->SetFrame(r);
685 impl_->children_[0]->Layout(
context);
687 for (
auto& child : impl_->children_) {
694 auto callback = impl_->menu_callbacks_.find(item_id);
695 if (
callback != impl_->menu_callbacks_.end()) {
704 enum Mode {
NORMAL, DIALOG, NO_INPUT };
708 std::shared_ptr<Widget> child,
716 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar |
717 ImGuiWindowFlags_NoResize |
718 ImGuiWindowFlags_NoCollapse;
726 if (mode == NO_INPUT) {
727 flags |= ImGuiWindowFlags_NoInputs;
729 auto frame = child->GetFrame();
730 bool bg_color_not_default = !child->IsDefaultBackgroundColor();
731 auto is_3d = (std::dynamic_pointer_cast<SceneWidget>(child) !=
nullptr);
735 ImGui::SetNextWindowPos(ImVec2(
float(
frame.
x),
float(
frame.
y)));
736 ImGui::SetNextWindowSize(
738 if (bg_color_not_default) {
739 auto& bgColor = child->GetBackgroundColor();
740 ImGui::PushStyleColor(ImGuiCol_WindowBg,
colorToImgui(bgColor));
742 ImGui::Begin(
name,
nullptr, flags);
753 if (bg_color_not_default) {
754 ImGui::PopStyleColor();
770 static const std::vector<const char*> win_names = {
771 "win1",
"win2",
"win3",
"win4",
"win5",
"win6",
"win7",
772 "win8",
"win9",
"win10",
"win11",
"win12",
"win13",
"win14",
773 "win15",
"win16",
"win17",
"win18",
"win19",
"win20"};
775 bool needs_layout =
false;
776 bool needs_redraw =
false;
781 double dt_sec = now - impl_->last_render_time_;
782 impl_->last_render_time_ = now;
785 while (!impl_->deferred_until_before_draw_.empty()) {
786 impl_->deferred_until_before_draw_.front()();
787 impl_->deferred_until_before_draw_.pop();
791 MakeDrawContextCurrent();
792 ImGuiIO& io = ImGui::GetIO();
793 io.DeltaTime = float(dt_sec);
796 io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
799 auto mouse_pos = ws.GetMousePosInWindow(impl_->window_);
802 auto buttons = ws.GetMouseButtons(impl_->window_);
828 while (!impl_->deferred_until_draw_.empty()) {
829 impl_->deferred_until_draw_.front()();
830 impl_->deferred_until_draw_.pop();
835 auto&
theme = impl_->theme_;
836 if (impl_->needs_layout_) {
838 impl_->needs_layout_ =
false;
842 int em =
theme.font_size;
843 DrawContext dc{
theme,
855 Mode draw_mode = (impl_->active_dialog_ ? NO_INPUT :
NORMAL);
856 for (
auto& child : this->impl_->children_) {
857 if (!child->IsVisible()) {
860 if (win_idx >= win_names.size()) {
861 win_idx = win_names.size() - 1;
863 "Using too many top-level child widgets; use a layout "
866 auto result = DrawChild(dc, win_names[win_idx++], child, draw_mode);
878 if (menubar && impl_->draw_menu_) {
879 auto id = menubar->DrawMenuBar(dc, !impl_->active_dialog_);
887 if (impl_->active_dialog_) {
888 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize,
889 float(
theme.dialog_border_width));
890 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding,
891 float(
theme.dialog_border_radius));
892 if (DrawChild(dc,
"dialog", impl_->active_dialog_, DIALOG) !=
896 ImGui::PopStyleVar(2);
905 impl_->imgui_.imgui_bridge->Update(ImGui::GetDrawData());
910 if (!is_layout_pass) {
911 impl_->renderer_->BeginFrame();
912 impl_->renderer_->Draw();
913 impl_->renderer_->EndFrame();
918 }
else if (needs_redraw) {
926 impl_->is_drawing_ =
true;
927 bool needed_layout = impl_->needs_layout_;
929 auto result = DrawOnce(needed_layout);
931 impl_->needs_layout_ =
true;
938 if (needed_layout || impl_->needs_layout_) {
942 impl_->is_drawing_ =
false;
943 if (impl_->needs_redraw_) {
945 impl_->needs_redraw_ =
false;
957 impl_->needs_layout_ =
true;
960 impl_->window_, impl_->renderer_);
962 impl_->imgui_.imgui_bridge->OnWindowResized(*
this);
967 auto old_context = MakeDrawContextCurrent();
968 ImGuiIO& io = ImGui::GetIO();
969 io.DisplaySize = ImVec2(
float(
size.width),
float(
size.height));
970 if (impl_->imgui_.scaling !=
scaling) {
971 UpdateImGuiForScaling(1.0f / impl_->imgui_.scaling);
972 UpdateImGuiForScaling(
scaling);
973 impl_->imgui_.scaling =
scaling;
975 io.DisplayFramebufferScale.x = 1.0f;
976 io.DisplayFramebufferScale.y = 1.0f;
978 if (impl_->wants_auto_size_ || impl_->wants_auto_center_) {
984 if (impl_->wants_auto_size_) {
986 ImGui::PushFont((ImFont*)impl_->imgui_.GetFont(
993 int(std::round(pref.width / impl_->imgui_.scaling)));
999 int unusable_height = 4 * impl_->theme_.font_size;
1000 h =
std::min(screen_size.height - unusable_height,
1001 int(std::round(pref.height / impl_->imgui_.scaling)));
1002 ws.SetWindowSize(impl_->window_, w, h);
1005 if (impl_->wants_auto_center_) {
1006 int x = (screen_size.width - w) / 2;
1007 int y = (screen_size.height - h) / 2;
1008 ws.SetWindowPos(impl_->window_, x, y);
1011 impl_->wants_auto_size_ =
false;
1012 impl_->wants_auto_center_ =
false;
1025 if (!impl_->is_resizing_) {
1026 impl_->is_resizing_ =
true;
1030 RestoreDrawContext(old_context);
1035 MakeDrawContextCurrent();
1039 if (impl_->is_resizing_) {
1040 impl_->is_resizing_ =
false;
1053 ImGuiIO& io = ImGui::GetIO();
1054 float dx = 0.0, dy = 0.0;
1055 if (e.
wheel.dx != 0) {
1058 if (e.
wheel.dy != 0) {
1063 if (e.
wheel.isTrackpad) {
1064 io.MouseWheelH += dx * 0.25f;
1065 io.MouseWheel += dy * 0.25f;
1067 io.MouseWheelH += dx;
1068 io.MouseWheel += dy;
1074 if (impl_->mouse_grabber_widget_) {
1075 impl_->mouse_grabber_widget_->Mouse(e);
1077 impl_->mouse_grabber_widget_ =
nullptr;
1090 ImGuiContext*
context = ImGui::GetCurrentContext();
1091 for (
auto* w :
context->Windows) {
1092 if (w->Flags & ImGuiWindowFlags_Popup &&
1093 ImGui::IsPopupOpen(w->PopupId, 0)) {
1094 Rect r(
int(w->Pos.x),
int(w->Pos.y),
int(w->Size.x),
1097 bool weKnowThis =
false;
1098 for (
auto child : impl_->children_) {
1099 if (child->GetFrame() == r) {
1116 auto HandleMouseForChild = [
this](
const MouseEvent& e,
1117 std::shared_ptr<Widget> child) ->
bool {
1118 if (child->GetFrame().Contains(e.
x, e.
y) && child->IsVisible()) {
1122 auto result = child->Mouse(e);
1125 impl_->mouse_grabber_widget_ = child.get();
1128 impl_->mouse_grabber_widget_ =
nullptr;
1134 if (impl_->active_dialog_) {
1135 HandleMouseForChild(e, impl_->active_dialog_);
1142 std::vector<std::shared_ptr<Widget>>& children = impl_->children_;
1143 for (
auto it = children.rbegin(); it != children.rend(); ++it) {
1144 if (HandleMouseForChild(e, *it)) {
1169 impl_->mouse_mods_ &= ~this_mod;
1171 impl_->mouse_mods_ |= this_mod;
1174 auto old_context = MakeDrawContextCurrent();
1175 ImGuiIO& io = ImGui::GetIO();
1176 if (e.
key < IM_ARRAYSIZE(io.KeysDown)) {
1182 if (ImGui::GetCurrentContext()->ActiveId == 0) {
1184 if (!impl_->on_key_event_ || !impl_->on_key_event_(e)) {
1185 if (impl_->focus_widget_) {
1186 impl_->focus_widget_->Key(e);
1191 RestoreDrawContext(old_context);
1196 auto old_context = MakeDrawContextCurrent();
1197 ImGuiIO& io = ImGui::GetIO();
1198 io.AddInputCharactersUTF8(e.
utf8);
1199 RestoreDrawContext(old_context);
1205 auto old_context = MakeDrawContextCurrent();
1206 bool redraw =
false;
1208 if (impl_->on_tick_event_) {
1209 redraw = impl_->on_tick_event_();
1212 for (
auto child : impl_->children_) {
1217 RestoreDrawContext(old_context);
std::function< void(std::shared_ptr< core::Tensor >)> callback
const std::vector< FontDescription > & GetFontDescriptions() const
static Application & GetInstance()
static constexpr FontId DEFAULT_FONT_ID
Identifier for font used by default for all UI elements.
void RemoveWindow(Window *window)
void VerifyIsInitialized()
std::shared_ptr< Menu > GetMenubar() const
WindowSystem & GetWindowSystem() const
const Theme & GetTheme() const
static std::shared_ptr< Horiz > MakeCentered(std::shared_ptr< Widget > w)
virtual void SetWindowPos(OSWindow w, int x, int y)=0
virtual Size GetWindowSizePixels(OSWindow w) const =0
virtual void PostRedrawEvent(OSWindow w)=0
static constexpr int FLAG_TOPMOST
virtual Point GetWindowPos(OSWindow w) const =0
static constexpr int FLAG_HIDDEN
virtual void DestroyWindow(OSWindow w)=0
virtual void SetWindowSize(OSWindow w, int width, int height)=0
virtual bool IsActiveWindow(OSWindow w) const =0
virtual rendering::FilamentRenderer * CreateRenderer(OSWindow w)=0
virtual void CancelUserClose(OSWindow w)=0
virtual float GetWindowScaleFactor(OSWindow w) const =0
virtual void ShowWindow(OSWindow w, bool show)=0
virtual void RaiseWindowToTop(OSWindow w)=0
virtual void SetWindowTitle(OSWindow w, const char *title)=0
virtual Size GetScreenSize(OSWindow w)=0
virtual void ResizeRenderer(OSWindow w, rendering::FilamentRenderer *renderer)=0
virtual bool GetWindowIsVisible(OSWindow w) const =0
void SetFocusWidget(Widget *w)
Sets.
const std::vector< std::shared_ptr< Widget > > & GetChildren() const
static const int FLAG_TOPMOST
bool IsActiveWindow() const
void SetNeedsLayout()
Instructs the window to relayout before the next draw.
void OnTickEvent(const TickEvent &e)
void OnTextInput(const TextInputEvent &e)
static const int FLAG_HIDDEN
const Theme & GetTheme() const
std::string GetWebRTCUID() const
Window(const std::string &title, int flags=0)
Point GlobalToWindowCoord(int global_x, int global_y)
Returns the global point (in OS pixels) in window local coordinates.
void SetSize(const Size &size)
Sets the size of the window in pixels. Includes menubar on Linux.
virtual Size CalcPreferredSize()
const char * GetTitle() const
void AddChild(std::shared_ptr< Widget > w)
WindowSystem::OSWindow GetOSWindow() const
void SetOnMenuItemActivated(Menu::ItemId item_id, std::function< void()> callback)
Rect GetContentRect() const
void SetOnKeyEvent(std::function< bool(const KeyEvent &)> callback)
visualization::rendering::Renderer & GetRenderer() const
void SetTitle(const char *title)
virtual void Layout(const LayoutContext &context)
void SetOSFrame(const Rect &r)
void ShowDialog(std::shared_ptr< Dialog > dlg)
void OnKeyEvent(const KeyEvent &e)
void SetOnTickEvent(std::function< bool()> callback)
LayoutContext GetLayoutContext()
void CloseDialog()
Closes the dialog.
void ShowMessageBox(const char *title, const char *message)
float GetScaling() const
Returns the scaling factor from OS pixels to device pixels.
void SetOnClose(std::function< bool()> callback)
virtual void OnMenuItemSelected(Menu::ItemId item_id)
void OnMouseEvent(const MouseEvent &e)
virtual void OnDragDropped(const char *path)
WebRTCWindowSystem is a BitmapWindowSystem with a WebRTC server that sends video frames to remote cli...
__host__ __device__ int2 abs(int2 v)
std::unique_ptr< ImguiFilamentBridge > imgui_bridge
std::vector< ImFont * > fonts
static const std::string path
ImVec4 colorToImgui(const Color &color)
Generic file read and write utility for python interface.
struct cloudViewer::visualization::gui::MouseEvent::@17::@21 wheel
Rect UnionedWith(const Rect &r) const
bool Contains(int x, int y) const
visualization::rendering::FilamentRenderer * renderer_
Widget * mouse_grabber_widget_
std::unordered_map< Menu::ItemId, std::function< void()> > menu_callbacks_
double last_button_down_time_
std::function< bool(void)> on_close_
std::queue< std::function< void()> > deferred_until_before_draw_
std::vector< std::shared_ptr< Widget > > children_
std::shared_ptr< Dialog > active_dialog_
std::function< bool(void)> on_tick_event_
MouseButton last_button_down_
std::function< bool(const KeyEvent &)> on_key_event_
ImguiWindowContext imgui_
std::queue< std::function< void()> > deferred_until_draw_
WindowSystem::OSWindow window_