ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Window.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include <Logging.h>
11 #include <imgui.h>
12 #include <imgui_internal.h> // so we can examine the current context
13 
14 #include <algorithm>
15 #include <cmath>
16 #include <memory>
17 #include <queue>
18 #include <unordered_map>
19 #include <vector>
20 
27 #include "visualization/gui/Menu.h"
30 #include "visualization/gui/Util.h"
34 
35 #ifdef BUILD_WEBRTC
37 #endif
38 
39 // ----------------------------------------------------------------------------
40 namespace cloudViewer {
41 namespace visualization {
42 namespace gui {
43 
44 namespace {
45 
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;
50 
51 // Assumes the correct ImGuiContext is current
52 void UpdateImGuiForScaling(float new_scaling) {
53  ImGuiStyle& style = ImGui::GetStyle();
54  // FrameBorderSize is not adjusted (we want minimal borders)
55  style.FrameRounding *= new_scaling;
56 }
57 
58 void ChangeAllRenderQuality(
59  SceneWidget::Quality quality,
60  const std::vector<std::shared_ptr<Widget>>& children) {
61  for (auto child : children) {
62  auto sw = std::dynamic_pointer_cast<SceneWidget>(child);
63  if (sw) {
64  sw->SetRenderQuality(quality);
65  } else {
66  if (child->GetChildren().size() > 0) {
67  ChangeAllRenderQuality(quality, child->GetChildren());
68  }
69  }
70  }
71 }
72 
73 struct ImguiWindowContext : public FontContext {
74  const Theme* theme = nullptr;
75  std::unique_ptr<ImguiFilamentBridge> imgui_bridge;
76  ImGuiContext* context = nullptr;
77  std::vector<ImFont*> fonts; // references, not owned by us
78  float scaling = 1.0;
79 
80  void* GetFont(FontId font_id) { return this->fonts[font_id]; }
81 
82  void CreateFonts() {
83  // ImGUI puts all fonts into one big texture atlas. However, there are
84  // separate ImFont* pointers for each conceptual font. This means that
85  // while we can have many fonts, all the fonts that we are ever going
86  // to use must be loaded up front, which makes a large font selection
87  // inconsistent with small memory footprint. Also, we might bump into
88  // OpenGL texture size limitations.
89  auto& font_descs = Application::GetInstance().GetFontDescriptions();
90  this->fonts.reserve(font_descs.size());
91  for (auto& fd : font_descs) {
92  this->fonts.push_back(AddFont(fd));
93  }
94 
95  ImGuiIO& io = ImGui::GetIO();
96  unsigned char* pixels;
97  int textureW, textureH, bytesPerPx;
98  io.Fonts->GetTexDataAsAlpha8(&pixels, &textureW, &textureH,
99  &bytesPerPx);
100  // Some fonts seem to result in 0x0 textures (maybe if the font does
101  // not contain any of the code points?), which cause Filament to
102  // panic. Handle this gracefully.
103  if (textureW == 0 || textureH == 0) {
105  "Got zero-byte font texture; ignoring custom fonts");
106  io.Fonts->Clear();
107  this->fonts[0] =
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];
112  }
113  io.Fonts->GetTexDataAsAlpha8(&pixels, &textureW, &textureH,
114  &bytesPerPx);
115  }
116  this->imgui_bridge->CreateAtlasTextureAlpha8(pixels, textureW, textureH,
117  bytesPerPx);
118  ImGui::SetCurrentFont(this->fonts[Application::DEFAULT_FONT_ID]);
119  }
120 
121  ImFont* AddFont(const FontDescription& fd) {
122  // We can assume that everything in the FontDescription is usable, since
123  // Application::SetFont() should have ensured that it is usable.
124  ImFont* imfont = nullptr;
125 
126  ImGuiIO& io = ImGui::GetIO();
127  float point_size;
128  if (fd.point_size_ <= 0) {
129  point_size = float(this->theme->font_size);
130  } else {
131  point_size = this->scaling * float(fd.point_size_);
132  }
133  // The first range should be "en" from
134  // FontDescription::FontDescription()
135  if (fd.ranges_.size() == 1) {
136  imfont = io.Fonts->AddFontFromFileTTF(fd.ranges_[0].path.c_str(),
137  point_size);
138  } else {
139  imfont = io.Fonts->AddFontFromFileTTF(
140  fd.ranges_[0].path.c_str(), point_size, NULL,
141  io.Fonts->GetGlyphRangesDefault());
142  }
143 
144  ImFontConfig config;
145  config.MergeMode = true;
146  for (auto& r : fd.ranges_) {
147  if (!r.lang.empty()) {
148  const ImWchar* range;
149  if (r.lang == "en") {
150  continue; // added above, don't add cyrillic too
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();
163  } else { // so many languages use Cyrillic it can be the
164  // default
165  range = io.Fonts->GetGlyphRangesCyrillic();
166  }
167  imfont = io.Fonts->AddFontFromFileTTF(
168  r.path.c_str(), point_size, &config, range);
169  } else if (!r.code_points.empty()) {
170  // TODO: the ImGui docs say that this must exist until
171  // CreateAtlastTextureAlpha8().
172  ImVector<ImWchar> range;
173  ImFontGlyphRangesBuilder builder;
174  for (auto c : r.code_points) {
175  builder.AddChar(c);
176  }
177  builder.BuildRanges(&range);
178  imfont = io.Fonts->AddFontFromFileTTF(
179  r.path.c_str(), point_size, &config, range.Data);
180  }
181  }
182 
183  return imfont;
184  }
185 };
186 
187 } // namespace
188 
189 const int Window::FLAG_HIDDEN = (1 << 0);
190 const int Window::FLAG_TOPMOST = (1 << 1);
191 
192 struct Window::Impl {
193  Impl() {}
194  ~Impl() {}
195 
197  std::string title_; // there is no glfwGetWindowTitle()...
198  bool draw_menu_ = true;
199  std::unordered_map<Menu::ItemId, std::function<void()>> menu_callbacks_;
200  std::function<bool(void)> on_tick_event_;
201  std::function<bool(void)> on_close_;
202  std::function<bool(const KeyEvent&)> on_key_event_;
203  // We need these for mouse moves and wheel events.
204  // The only source of ground truth is button events, so the rest of
205  // the time we monitor key up/down events.
206  int mouse_mods_ = 0; // ORed KeyModifiers
207  double last_render_time_ = 0.0;
208  double last_button_down_time_ = 0.0; // we have to compute double-click
210 
211  Theme theme_; // so that the font size can be different based on scaling
213  ImguiWindowContext imgui_;
214  std::vector<std::shared_ptr<Widget>> children_;
215 
216  // Active dialog is owned here. It is not put in the children because
217  // we are going to add it and take it out during draw (since that's
218  // how an immediate mode GUI works) and that involves changing the
219  // children while iterating over it. Also, conceptually it is not a
220  // child, it is a child window, and needs to be on top, which we cannot
221  // guarantee if it is a child widget.
222  std::shared_ptr<Dialog> active_dialog_;
223 
224  std::queue<std::function<void()>> deferred_until_before_draw_;
225  std::queue<std::function<void()>> deferred_until_draw_;
226  Widget* mouse_grabber_widget_ = nullptr; // only if not ImGUI widget
228  nullptr; // only used if ImGUI isn't taking keystrokes
229  bool wants_auto_size_ = false;
230  bool wants_auto_center_ = false;
231  bool needs_layout_ = true;
232  bool needs_redraw_ = true; // set by PostRedraw to defer if already drawing
233  bool is_resizing_ = false;
234  bool is_drawing_ = false;
235 };
236 
237 Window::Window(const std::string& title, int flags /*= 0*/)
238  : Window(title, CENTERED_X, CENTERED_Y, AUTOSIZE_WIDTH, AUTOSIZE_HEIGHT) {}
239 
240 Window::Window(const std::string& title,
241  int width,
242  int height,
243  int flags /*= 0*/)
244  : Window(title, CENTERED_X, CENTERED_Y, width, height) {}
245 
246 Window::Window(const std::string& title,
247  int x,
248  int y,
249  int width,
250  int height,
251  int flags /*= 0*/)
252  : impl_(new Window::Impl()) {
253  // Make sure that the Application instance is initialized before creating
254  // the window. It is easy to call, e.g. O3DVisualizer() and forgetting to
255  // initialize the application. This will cause a crash because the window
256  // system will not exist, nor will the resource directory be located, and
257  // so the renderer will not load properly and give cryptic messages.
259 
260  impl_->wants_auto_center_ = (x == CENTERED_X || y == CENTERED_Y);
261  impl_->wants_auto_size_ =
262  (width == AUTOSIZE_WIDTH || height == AUTOSIZE_HEIGHT);
263 
264  bool visible = (!(flags & FLAG_HIDDEN) &&
265  (impl_->wants_auto_size_ || impl_->wants_auto_center_));
266  int ws_flags = 0;
267  if (!visible) {
268  ws_flags |= WindowSystem::FLAG_HIDDEN;
269  }
270  if (flags & FLAG_TOPMOST) {
271  ws_flags |= WindowSystem::FLAG_TOPMOST;
272  }
273 
274  int initial_width = std::max(10, width);
275  int initial_height = std::max(10, height);
277  impl_->window_ = ws.CreateOSWindow(this, initial_width, initial_height,
278  title.c_str(), ws_flags);
279  impl_->title_ = title;
280 
281  if (x != CENTERED_X || y != CENTERED_Y) {
282  ws.SetWindowPos(impl_->window_, x, y);
283  }
284 
285  auto& theme = impl_->theme_; // shorter alias
286  impl_->imgui_.context = ImGui::CreateContext();
287  auto oldContext = MakeDrawContextCurrent();
288 
289  // ImGUI creates a bitmap atlas from a font, so we need to have the correct
290  // size when we create it, because we can't change the bitmap without
291  // reloading the whole thing (expensive).
292  // Note that GetScaling() gets the pixel scaling. On macOS, coordinates are
293  // specified in points, not device pixels. The conversion to device pixels
294  // is the scaling factor. On Linux, there is no scaling of pixels (just
295  // like in CloudViewer's GUI library), and glfwGetWindowContentScale()
296  // returns the appropriate scale factor for text and icons and such.
297  float scaling = ws.GetUIScaleFactor(impl_->window_);
298  impl_->imgui_.scaling = scaling;
299  impl_->theme_ = Application::GetInstance().GetTheme();
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));
306 
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);
315  style.Colors[ImGuiCol_WindowBg] = colorToImgui(theme.background_color);
316  style.Colors[ImGuiCol_ChildBg] = colorToImgui(theme.background_color);
317  style.Colors[ImGuiCol_Text] = colorToImgui(theme.text_color);
318  style.Colors[ImGuiCol_Border] = colorToImgui(theme.border_color);
319  style.Colors[ImGuiCol_Button] = colorToImgui(theme.button_color);
320  style.Colors[ImGuiCol_ButtonHovered] =
321  colorToImgui(theme.button_hover_color);
322  style.Colors[ImGuiCol_ButtonActive] =
323  colorToImgui(theme.button_active_color);
324  style.Colors[ImGuiCol_CheckMark] = colorToImgui(theme.checkbox_check_color);
325  style.Colors[ImGuiCol_FrameBg] =
326  colorToImgui(theme.combobox_background_color);
327  style.Colors[ImGuiCol_FrameBgHovered] =
328  colorToImgui(theme.combobox_hover_color);
329  style.Colors[ImGuiCol_FrameBgActive] =
330  style.Colors[ImGuiCol_FrameBgHovered];
331  style.Colors[ImGuiCol_SliderGrab] = colorToImgui(theme.slider_grab_color);
332  style.Colors[ImGuiCol_SliderGrabActive] =
333  colorToImgui(theme.slider_grab_color);
334  style.Colors[ImGuiCol_Tab] = colorToImgui(theme.tab_inactive_color);
335  style.Colors[ImGuiCol_TabHovered] = colorToImgui(theme.tab_hover_color);
336  style.Colors[ImGuiCol_TabActive] = colorToImgui(theme.tab_active_color);
337 
338  ImGuiIO& io = ImGui::GetIO();
339  io.IniFilename = nullptr;
340 
341  // ImGUI's io.KeysDown is indexed by our scan codes, and we fill out
342  // io.KeyMap to map from our code to ImGui's code.
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;
348  io.KeyMap[ImGuiKey_PageUp] = KEY_PAGEUP;
349  io.KeyMap[ImGuiKey_PageDown] = KEY_PAGEDOWN;
350  io.KeyMap[ImGuiKey_Home] = KEY_HOME;
351  io.KeyMap[ImGuiKey_End] = KEY_END;
352  io.KeyMap[ImGuiKey_Insert] = KEY_INSERT;
353  io.KeyMap[ImGuiKey_Delete] = KEY_DELETE;
354  io.KeyMap[ImGuiKey_Backspace] = KEY_BACKSPACE;
355  io.KeyMap[ImGuiKey_Space] = ' ';
356  io.KeyMap[ImGuiKey_Enter] = KEY_ENTER;
357  io.KeyMap[ImGuiKey_Escape] = KEY_ESCAPE;
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';
364  /* io.SetClipboardTextFn = [this](void*, const char* text) {
365  glfwSetClipboardString(this->impl_->window, text);
366  };
367  io.GetClipboardTextFn = [this](void*) -> const char* {
368  return glfwGetClipboardString(this->impl_->window);
369  }; */
370  io.ClipboardUserData = nullptr;
371 
372  // Restore the context, in case we are creating a window during a draw.
373  // (This is quite likely, since ImGUI only handles things like button
374  // presses during draw. A file open dialog is likely to create a window
375  // after pressing "Open".)
376  RestoreDrawContext(oldContext);
377 
378  CreateRenderer();
379 }
380 
381 void Window::CreateRenderer() {
382  // This is a delayed part of the constructor. See comment at end of ctor.
383  auto old_context = MakeDrawContextCurrent();
384 
385  // On single-threaded platforms, Filament's OpenGL context must be current,
386  // not GLFW's context, so create the renderer after the window.
387  impl_->renderer_ =
389  impl_->window_);
390  impl_->renderer_->SetClearColor({1.0f, 1.0f, 1.0f, 1.0f});
391 
392  impl_->imgui_.imgui_bridge =
393  std::make_unique<ImguiFilamentBridge>(impl_->renderer_, GetSize());
394  impl_->imgui_.theme = &impl_->theme_;
395  impl_->imgui_.CreateFonts();
396 
397  RestoreDrawContext(old_context);
398 }
399 
401  impl_->active_dialog_.reset();
402  impl_->children_.clear(); // needs to happen before deleting renderer
403  ImGui::SetCurrentContext(impl_->imgui_.context);
404  ImGui::DestroyContext();
405  delete impl_->renderer_;
406  DestroyWindow();
407 }
408 
411  // Ensure DestroyWindow() can be called multiple times, which will
412  // happen if you call DestroyWindow() before the destructor.
413  impl_->window_ = nullptr;
414 }
415 
416 int Window::GetMouseMods() const { return impl_->mouse_mods_; }
417 
418 std::string Window::GetWebRTCUID() const {
419 #ifdef BUILD_WEBRTC
420  if (auto* webrtc_ws = dynamic_cast<webrtc_server::WebRTCWindowSystem*>(
421  &Application::GetInstance().GetWindowSystem())) {
422  return webrtc_ws->GetWindowUID(impl_->window_);
423  } else {
424  return "window_undefined";
425  }
426 #else
427  return "window_undefined";
428 #endif
429 }
430 
431 const std::vector<std::shared_ptr<Widget>>& Window::GetChildren() const {
432  return impl_->children_;
433 }
434 
435 void* Window::MakeDrawContextCurrent() const {
436  auto old_context = ImGui::GetCurrentContext();
437  ImGui::SetCurrentContext(impl_->imgui_.context);
438  return old_context;
439 }
440 
441 void Window::RestoreDrawContext(void* oldContext) const {
442  ImGui::SetCurrentContext((ImGuiContext*)oldContext);
443 }
444 
445 const Theme& Window::GetTheme() const { return impl_->theme_; }
446 
448  return *impl_->renderer_;
449 }
450 
453  auto pos = ws.GetWindowPos(impl_->window_);
454  auto size = ws.GetWindowSize(impl_->window_);
455  return Rect(pos.x, pos.y, size.width, size.height);
456 }
457 
458 void Window::SetOSFrame(const Rect& r) {
460  ws.SetWindowPos(impl_->window_, r.x, r.y);
461  ws.SetWindowSize(impl_->window_, r.width, r.height);
462 }
463 
464 const char* Window::GetTitle() const { return impl_->title_.c_str(); }
465 
466 void Window::SetTitle(const char* title) {
467  impl_->title_ = title;
469  impl_->window_, title);
470 }
471 
472 // Note: can only be called if the ImGUI context is current (that is,
473 // after MakeDrawContextCurrent() has been called), otherwise
474 // ImGUI won't be able to access the font.
476  // If we don't have any children--unlikely, but might happen when you're
477  // experimenting and just create an empty window to see if you understand
478  // how to config the library--return a non-zero size, since a size of (0, 0)
479  // will end up with a crash.
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)));
483  }
484 
485  Rect bbox(0, 0, 0, 0);
486  for (auto& child : impl_->children_) {
487  auto pref = child->CalcPreferredSize(GetLayoutContext(),
489  Rect r(child->GetFrame().x, child->GetFrame().y, pref.width,
490  pref.height);
491  bbox = bbox.UnionedWith(r);
492  }
493 
494  // Note: we are doing (bbox.GetRight() - 0) NOT (bbox.GetRight() - bbox.x)
495  // (and likewise for height) because the origin of the window is
496  // (0, 0) and anything up/left is clipped.
497  return Size(bbox.GetRight(), bbox.GetBottom());
498 }
499 
501  // CalcPreferredSize() can only be called while the ImGUI context
502  // is current, but we are probably calling this while setting up the
503  // window.
504  auto auto_size = [this]() { SetSize(CalcPreferredSize()); };
505  impl_->deferred_until_draw_.push(auto_size);
506 }
507 
508 void Window::SetSize(const Size& size) {
509  // Make sure we do the resize outside of a draw, to avoid unsightly
510  // errors if we happen to do this in the middle of a draw.
511  auto resize = [this, size /*copy*/]() {
512  auto scaling = this->impl_->imgui_.scaling;
513  int width = int(std::round(float(size.width) / scaling));
514  int height = int(std::round(float(size.height) / scaling));
516  impl_->window_, width, height);
517  };
518  impl_->deferred_until_before_draw_.push(resize);
519 }
520 
523  impl_->window_);
524 }
525 
527  auto size = GetSize();
528  int menu_height = 0;
529  MakeDrawContextCurrent();
530  auto menubar = Application::GetInstance().GetMenubar();
531  if (menubar && impl_->draw_menu_) {
532  menu_height = menubar->CalcHeight(GetTheme());
533  }
534 
535  return Rect(0, menu_height, size.width, size.height - menu_height);
536 }
537 
538 float Window::GetScaling() const {
540  impl_->window_);
541 }
542 
543 Point Window::GlobalToWindowCoord(int global_x, int global_y) {
545  impl_->window_);
546  return Point(global_y - pos.x, global_y - pos.y);
547 }
548 
549 bool Window::IsVisible() const {
551  impl_->window_);
552 }
553 
554 void Window::Show(bool vis /*= true*/) {
556  vis);
557 }
558 
560  if (impl_->on_close_) {
561  bool should_close = impl_->on_close_();
562  if (!should_close) {
564  impl_->window_);
565  return;
566  }
567  }
569 }
570 
571 void Window::SetNeedsLayout() { impl_->needs_layout_ = true; }
572 
574  // Windows cannot actually post an expose event, and the actual mechanism
575  // requires that PostNativeExposeEvent() not be called while drawing
576  // (see the implementation for details).
577  if (impl_->is_drawing_) {
578  impl_->needs_redraw_ = true;
579  } else {
581  impl_->window_);
582  }
583 }
584 
585 void Window::RaiseToTop() const {
587  impl_->window_);
588 }
589 
592  impl_->window_);
593 }
594 
595 void Window::SetFocusWidget(Widget* w) { impl_->focus_widget_ = w; }
596 
597 void Window::AddChild(std::shared_ptr<Widget> w) {
598  impl_->children_.push_back(w);
599  impl_->needs_layout_ = true;
600 }
601 
603  std::function<void()> callback) {
604  impl_->menu_callbacks_[item_id] = callback;
605 }
606 
607 void Window::SetOnTickEvent(std::function<bool()> callback) {
608  impl_->on_tick_event_ = callback;
609 }
610 
611 void Window::SetOnClose(std::function<bool()> callback) {
612  impl_->on_close_ = callback;
613 }
614 
615 void Window::SetOnKeyEvent(std::function<bool(const KeyEvent&)> callback) {
616  impl_->on_key_event_ = callback;
617 }
618 
619 void Window::ShowDialog(std::shared_ptr<Dialog> dlg) {
620  if (impl_->active_dialog_) {
621  CloseDialog();
622  }
623  impl_->active_dialog_ = dlg;
624  dlg->OnWillShow();
625 
626  auto deferred_layout = [this, dlg]() {
627  auto context = GetLayoutContext();
628  auto content_rect = GetContentRect();
629  auto pref = dlg->CalcPreferredSize(context, Widget::Constraints());
630  int w = dlg->GetFrame().width;
631  int h = dlg->GetFrame().height;
632  if (w == 0) {
633  w = pref.width;
634  }
635  if (h == 0) {
636  h = pref.height;
637  }
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));
642  dlg->Layout(context);
643  };
644 
645  impl_->deferred_until_draw_.push(deferred_layout);
646 }
647 
649  if (impl_->focus_widget_ == impl_->active_dialog_.get()) {
650  SetFocusWidget(nullptr);
651  }
652  impl_->active_dialog_.reset();
653 
654  // The dialog might not be closing from within a draw call, such as when
655  // a native file dialog closes, so we need to post a redraw, just in case.
656  // If it is from within a draw call, then any redraw request from that will
657  // get merged in with this one by the OS.
658  PostRedraw();
659 }
660 
661 void Window::ShowMessageBox(const char* title, const char* message) {
662  auto em = GetTheme().font_size;
663  auto margins = Margins(GetTheme().default_margin);
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(); });
669  layout->AddChild(Horiz::MakeCentered(ok));
670  dlg->AddChild(layout);
671  ShowDialog(dlg);
672 }
673 
674 void Window::ShowMenu(bool show) {
675  impl_->draw_menu_ = show;
676  SetNeedsLayout();
677 }
678 
679 LayoutContext Window::GetLayoutContext() { return {GetTheme(), impl_->imgui_}; }
680 
682  if (impl_->children_.size() == 1) {
683  auto r = GetContentRect();
684  impl_->children_[0]->SetFrame(r);
685  impl_->children_[0]->Layout(context);
686  } else {
687  for (auto& child : impl_->children_) {
688  child->Layout(context);
689  }
690  }
691 }
692 
694  auto callback = impl_->menu_callbacks_.find(item_id);
695  if (callback != impl_->menu_callbacks_.end()) {
696  callback->second();
697  PostRedraw(); // might not be in a draw if from native menu
698  }
699 }
700 
701 WindowSystem::OSWindow Window::GetOSWindow() const { return impl_->window_; }
702 
703 namespace {
704 enum Mode { NORMAL, DIALOG, NO_INPUT };
705 
706 Widget::DrawResult DrawChild(DrawContext& dc,
707  const char* name,
708  std::shared_ptr<Widget> child,
709  Mode mode) {
710  // Note: ImGUI's concept of a "window" is really a movable child of the
711  // OS window. We want a child to act like a child of the OS window,
712  // like native UI toolkits, Qt, etc. So the top-level widgets of
713  // a window are drawn using ImGui windows whose frame is specified
714  // and which have no title bar, resizability, etc.
715 
716  ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar |
717  ImGuiWindowFlags_NoResize |
718  ImGuiWindowFlags_NoCollapse;
719  // Q: When we want no input, why not use ImGui::BeginPopupModal(),
720  // which takes care of blocking input for us, since a modal popup
721  // is the most likely use case for wanting no input?
722  // A: It animates an overlay, which would require us to constantly
723  // redraw, otherwise it only animates when the mouse moves. But
724  // we don't need constant animation for anything else, so that would
725  // be a waste of CPU and battery (and really annoys people like me).
726  if (mode == NO_INPUT) {
727  flags |= ImGuiWindowFlags_NoInputs;
728  }
729  auto frame = child->GetFrame();
730  bool bg_color_not_default = !child->IsDefaultBackgroundColor();
731  auto is_3d = (std::dynamic_pointer_cast<SceneWidget>(child) != nullptr);
732  if (!is_3d) {
733  dc.uiOffsetX = frame.x;
734  dc.uiOffsetY = frame.y;
735  ImGui::SetNextWindowPos(ImVec2(float(frame.x), float(frame.y)));
736  ImGui::SetNextWindowSize(
737  ImVec2(float(frame.width), float(frame.height)));
738  if (bg_color_not_default) {
739  auto& bgColor = child->GetBackgroundColor();
740  ImGui::PushStyleColor(ImGuiCol_WindowBg, colorToImgui(bgColor));
741  }
742  ImGui::Begin(name, nullptr, flags);
743  } else {
744  dc.uiOffsetX = 0;
745  dc.uiOffsetY = 0;
746  }
747 
749  result = child->Draw(dc);
750 
751  if (!is_3d) {
752  ImGui::End();
753  if (bg_color_not_default) {
754  ImGui::PopStyleColor();
755  }
756  }
757 
758  return result;
759 }
760 } // namespace
761 
762 Widget::DrawResult Window::DrawOnce(bool is_layout_pass) {
763  // These are here to provide fast unique window names. (Hence using
764  // char* instead of a std::string, just in case c_str() recreates
765  // the buffer on some platform and unwittingly makes
766  // ImGui::DrawChild(dc, name.c_str(), ...) slow.
767  // If you find yourself needing more than a handful of top-level
768  // children, you should probably be using a layout of some sort
769  // (gui::Vert, gui::Horiz, gui::VGrid, etc. See Layout.h).
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"};
774 
775  bool needs_layout = false;
776  bool needs_redraw = false;
777 
778  // ImGUI uses the dt parameter to calculate double-clicks, so it
779  // needs to be reasonably accurate.
780  double now = Application::GetInstance().Now();
781  double dt_sec = now - impl_->last_render_time_;
782  impl_->last_render_time_ = now;
783 
784  // Run the deferred callbacks that need to happen outside a draw
785  while (!impl_->deferred_until_before_draw_.empty()) {
786  impl_->deferred_until_before_draw_.front()();
787  impl_->deferred_until_before_draw_.pop();
788  }
789 
790  // Set current context
791  MakeDrawContextCurrent(); // make sure our ImGUI context is active
792  ImGuiIO& io = ImGui::GetIO();
793  io.DeltaTime = float(dt_sec);
794 
795  // Set mouse information
796  io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
798  if (IsActiveWindow()) {
799  auto mouse_pos = ws.GetMousePosInWindow(impl_->window_);
800  io.MousePos = ImVec2(float(mouse_pos.x), float(mouse_pos.y));
801  }
802  auto buttons = ws.GetMouseButtons(impl_->window_);
803  io.MouseDown[0] = (buttons & int(MouseButton::LEFT));
804  io.MouseDown[1] = (buttons & int(MouseButton::RIGHT));
805  io.MouseDown[2] = (buttons & int(MouseButton::MIDDLE));
806 
807  // Set key information
808  io.KeyShift = (impl_->mouse_mods_ & int(KeyModifier::SHIFT));
809  io.KeyAlt = (impl_->mouse_mods_ & int(KeyModifier::ALT));
810  io.KeyCtrl = (impl_->mouse_mods_ & int(KeyModifier::CTRL));
811  io.KeySuper = (impl_->mouse_mods_ & int(KeyModifier::META));
812 
813  // Begin an ImGUI frame. We should NOT begin a filament frame here:
814  // a) ImGUI always needs to "draw", because event processing happens
815  // during draw for immediate mode GUIs, but if this is a layout
816  // pass (as ImGUI can take up two draws to layout widgets and text)
817  // we aren't actually going to render it.
818  // b) Filament pumps events during a beginFrame(), which can cause
819  // a key up event to process and erase the key down state from
820  // the ImGuiIO structure before we get a chance to draw/process it.
821  ImGui::NewFrame();
822  ImGui::PushFont(
823  (ImFont*)impl_->imgui_.GetFont(Application::DEFAULT_FONT_ID));
824 
825  // Run the deferred callbacks that need to happen inside a draw
826  // In particular, text sizing with ImGUI seems to require being
827  // in a frame, otherwise there isn't an GL texture info and we crash.
828  while (!impl_->deferred_until_draw_.empty()) {
829  impl_->deferred_until_draw_.front()();
830  impl_->deferred_until_draw_.pop();
831  }
832 
833  // Layout if necessary. This must happen within ImGui setup so that widgets
834  // can query font information.
835  auto& theme = impl_->theme_;
836  if (impl_->needs_layout_) {
838  impl_->needs_layout_ = false;
839  }
840 
841  auto size = GetSize();
842  int em = theme.font_size; // em = font size in digital type (see Wikipedia)
843  DrawContext dc{theme,
844  *impl_->renderer_,
845  impl_->imgui_,
846  0,
847  0,
848  size.width,
849  size.height,
850  em,
851  float(dt_sec)};
852 
853  // Draw all the widgets. These will get recorded by ImGui.
854  size_t win_idx = 0;
855  Mode draw_mode = (impl_->active_dialog_ ? NO_INPUT : NORMAL);
856  for (auto& child : this->impl_->children_) {
857  if (!child->IsVisible()) {
858  continue;
859  }
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 "
864  "instead.");
865  }
866  auto result = DrawChild(dc, win_names[win_idx++], child, draw_mode);
868  needs_redraw = true;
869  }
871  needs_layout = true;
872  }
873  }
874 
875  // Draw menubar after the children so it is always on top (although it
876  // shouldn't matter, as there shouldn't be anything under it)
877  auto menubar = Application::GetInstance().GetMenubar();
878  if (menubar && impl_->draw_menu_) {
879  auto id = menubar->DrawMenuBar(dc, !impl_->active_dialog_);
880  if (id != Menu::NO_ITEM) {
881  OnMenuItemSelected(id);
882  needs_redraw = true;
883  }
884  }
885 
886  // Draw any 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) !=
894  needs_redraw = true;
895  }
896  ImGui::PopStyleVar(2);
897  }
898 
899  // Finish frame and generate the commands
900  ImGui::PopFont();
901  ImGui::EndFrame();
902  ImGui::Render(); // creates the draw data (i.e. Render()s to data)
903 
904  // Draw the ImGui commands
905  impl_->imgui_.imgui_bridge->Update(ImGui::GetDrawData());
906 
907  // Draw. Since ImGUI is an immediate mode gui, it does layout during
908  // draw, and if we are drawing for layout purposes, don't actually
909  // draw, because we are just going to draw again after this returns.
910  if (!is_layout_pass) {
911  impl_->renderer_->BeginFrame();
912  impl_->renderer_->Draw();
913  impl_->renderer_->EndFrame();
914  }
915 
916  if (needs_layout) {
918  } else if (needs_redraw) {
920  } else {
922  }
923 }
924 
926  impl_->is_drawing_ = true;
927  bool needed_layout = impl_->needs_layout_;
928 
929  auto result = DrawOnce(needed_layout);
931  impl_->needs_layout_ = true;
932  }
933 
934  // ImGUI can take two frames to do its layout, so if we did a layout
935  // redraw a second time. This helps prevent a brief red flash when the
936  // window first appears, as well as corrupted images if the
937  // window initially appears underneath the mouse.
938  if (needed_layout || impl_->needs_layout_) {
939  DrawOnce(false);
940  }
941 
942  impl_->is_drawing_ = false;
943  if (impl_->needs_redraw_) {
945  impl_->needs_redraw_ = false;
946  }
947 
949  // Can't just draw here, because Filament sometimes fences within
950  // a draw, and then you can get two draws happening at the same
951  // time, which ends up with a crash.
952  PostRedraw();
953  }
954 }
955 
957  impl_->needs_layout_ = true;
958 
960  impl_->window_, impl_->renderer_);
961 
962  impl_->imgui_.imgui_bridge->OnWindowResized(*this);
963 
964  auto size = GetSize();
965  auto scaling = GetScaling();
966 
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); // undo previous
972  UpdateImGuiForScaling(scaling);
973  impl_->imgui_.scaling = scaling;
974  }
975  io.DisplayFramebufferScale.x = 1.0f;
976  io.DisplayFramebufferScale.y = 1.0f;
977 
978  if (impl_->wants_auto_size_ || impl_->wants_auto_center_) {
980  auto screen_size = ws.GetScreenSize(impl_->window_);
981  int w = GetOSFrame().width;
982  int h = GetOSFrame().height;
983 
984  if (impl_->wants_auto_size_) {
985  ImGui::NewFrame();
986  ImGui::PushFont((ImFont*)impl_->imgui_.GetFont(
988  auto pref = CalcPreferredSize();
989  ImGui::PopFont();
990  ImGui::EndFrame();
991 
992  w = std::min(screen_size.width,
993  int(std::round(pref.width / impl_->imgui_.scaling)));
994  // screen_height is the screen height, not the usable screen height.
995  // If we cannot call glfwGetMonitorWorkarea(), then we need to guess
996  // at the size. The window titlebar is about 2 * em, and then there
997  // is often a global menubar (Linux/GNOME, macOS) or a toolbar
998  // (Windows). A toolbar is somewhere around 2 - 3 ems.
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);
1003  }
1004 
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);
1009  }
1010 
1011  impl_->wants_auto_size_ = false;
1012  impl_->wants_auto_center_ = false;
1013 
1014  OnResize();
1015  }
1016 
1017  // Resizing looks bad if drawing takes a long time, so turn off MSAA
1018  // while we resize. On macOS this is critical, because the GL driver does
1019  // not release the memory for all the buffers of the new sizes right away
1020  // so it eats up GBs of memory rapidly and then resizing looks awful and
1021  // eventually stops working correctly. Unfortunately, there isn't a good
1022  // way to tell when we've stopped resizing, so we use the mouse movement.
1023  // (We get no mouse events while resizing, so any mouse even must mean we
1024  // are no longer resizing.)
1025  if (!impl_->is_resizing_) {
1026  impl_->is_resizing_ = true;
1027  ChangeAllRenderQuality(SceneWidget::Quality::FAST, impl_->children_);
1028  }
1029 
1030  RestoreDrawContext(old_context);
1031  PostRedraw();
1032 }
1033 
1035  MakeDrawContextCurrent();
1036 
1037  // We don't have a good way of determining when resizing ends; the most
1038  // likely action after resizing a window is to move the mouse.
1039  if (impl_->is_resizing_) {
1040  impl_->is_resizing_ = false;
1041  ChangeAllRenderQuality(SceneWidget::Quality::BEST, impl_->children_);
1042  }
1043 
1044  impl_->mouse_mods_ = e.modifiers;
1045 
1046  switch (e.type) {
1047  case MouseEvent::MOVE:
1049  case MouseEvent::DRAG:
1050  case MouseEvent::BUTTON_UP:
1051  break;
1052  case MouseEvent::WHEEL: {
1053  ImGuiIO& io = ImGui::GetIO();
1054  float dx = 0.0, dy = 0.0;
1055  if (e.wheel.dx != 0) {
1056  dx = e.wheel.dx / std::abs(e.wheel.dx); // get sign
1057  }
1058  if (e.wheel.dy != 0) {
1059  dy = e.wheel.dy / std::abs(e.wheel.dy); // get sign
1060  }
1061  // Note: ImGUI's documentation says that 1 unit of wheel movement
1062  // is about 5 lines of text scrolling.
1063  if (e.wheel.isTrackpad) {
1064  io.MouseWheelH += dx * 0.25f;
1065  io.MouseWheel += dy * 0.25f;
1066  } else {
1067  io.MouseWheelH += dx;
1068  io.MouseWheel += dy;
1069  }
1070  break;
1071  }
1072  }
1073 
1074  if (impl_->mouse_grabber_widget_) {
1075  impl_->mouse_grabber_widget_->Mouse(e);
1076  if (e.type == MouseEvent::BUTTON_UP) {
1077  impl_->mouse_grabber_widget_ = nullptr;
1078  }
1079  PostRedraw();
1080  return;
1081  }
1082 
1083  // Some ImGUI widgets have popup windows, in particular, the color
1084  // picker, which creates a popup window when you click on the color
1085  // patch. Since these aren't gui::Widgets, we don't know about them,
1086  // and will deliver mouse events to something below them. So find any
1087  // that would use the mouse, and if it isn't a toplevel child, then
1088  // eat the event for it.
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),
1095  int(w->Size.y));
1096  if (r.Contains(e.x, e.y)) {
1097  bool weKnowThis = false;
1098  for (auto child : impl_->children_) {
1099  if (child->GetFrame() == r) {
1100  weKnowThis = true;
1101  break;
1102  }
1103  }
1104  if (!weKnowThis) {
1105  // This is not a rect that is one of our children,
1106  // must be an ImGUI internal popup. Eat event.
1107  PostRedraw();
1108  return;
1109  }
1110  }
1111  }
1112  }
1113  }
1114 
1115  // Iterate backwards so that we send mouse events from the top down.
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()) {
1119  if (e.type == MouseEvent::BUTTON_DOWN) {
1120  SetFocusWidget(child.get());
1121  }
1122  auto result = child->Mouse(e);
1123  if (e.type == MouseEvent::BUTTON_DOWN) {
1125  impl_->mouse_grabber_widget_ = child.get();
1126  }
1127  } else if (e.type == MouseEvent::BUTTON_UP) {
1128  impl_->mouse_grabber_widget_ = nullptr;
1129  }
1130  return true;
1131  }
1132  return false;
1133  };
1134  if (impl_->active_dialog_) {
1135  HandleMouseForChild(e, impl_->active_dialog_);
1136  } else {
1137  // Mouse move and wheel always get delivered.
1138  // Button up and down get delivered if they weren't in an ImGUI popup.
1139  // Drag should only be delivered if the grabber widget exists;
1140  // if it is null, then the mouse is being dragged over an ImGUI popup.
1141  if (e.type != MouseEvent::DRAG || impl_->mouse_grabber_widget_) {
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)) {
1145  break;
1146  }
1147  }
1148  }
1149  }
1150 
1151  PostRedraw();
1152 }
1153 
1155  auto this_mod = 0;
1156  if (e.key == KEY_LSHIFT || e.key == KEY_RSHIFT) {
1157  this_mod = int(KeyModifier::SHIFT);
1158  } else if (e.key == KEY_LCTRL || e.key == KEY_RCTRL) {
1159  this_mod = int(KeyModifier::CTRL);
1160  } else if (e.key == KEY_ALT) {
1161  this_mod = int(KeyModifier::ALT);
1162  } else if (e.key == KEY_META) {
1163  this_mod = int(KeyModifier::META);
1164  } else if (e.key == KEY_ESCAPE) {
1165  Close();
1166  }
1167 
1168  if (e.type == KeyEvent::UP) {
1169  impl_->mouse_mods_ &= ~this_mod;
1170  } else {
1171  impl_->mouse_mods_ |= this_mod;
1172  }
1173 
1174  auto old_context = MakeDrawContextCurrent();
1175  ImGuiIO& io = ImGui::GetIO();
1176  if (e.key < IM_ARRAYSIZE(io.KeysDown)) {
1177  io.KeysDown[e.key] = (e.type == KeyEvent::DOWN);
1178  }
1179 
1180  // If an ImGUI widget is not getting keystrokes, we can send them to
1181  // non-ImGUI widgets
1182  if (ImGui::GetCurrentContext()->ActiveId == 0) {
1183  // dispatch key event to focused widget if not intercepted
1184  if (!impl_->on_key_event_ || !impl_->on_key_event_(e)) {
1185  if (impl_->focus_widget_) {
1186  impl_->focus_widget_->Key(e);
1187  }
1188  }
1189  }
1190 
1191  RestoreDrawContext(old_context);
1192  PostRedraw();
1193 }
1194 
1196  auto old_context = MakeDrawContextCurrent();
1197  ImGuiIO& io = ImGui::GetIO();
1198  io.AddInputCharactersUTF8(e.utf8);
1199  RestoreDrawContext(old_context);
1200 
1201  PostRedraw();
1202 }
1203 
1205  auto old_context = MakeDrawContextCurrent();
1206  bool redraw = false;
1207 
1208  if (impl_->on_tick_event_) {
1209  redraw = impl_->on_tick_event_();
1210  }
1211 
1212  for (auto child : impl_->children_) {
1213  if (child->Tick(e) == Widget::DrawResult::REDRAW) {
1214  redraw = true;
1215  }
1216  }
1217  RestoreDrawContext(old_context);
1218 
1219  if (redraw) {
1220  PostRedraw();
1221  }
1222 }
1223 
1224 void Window::OnDragDropped(const char* path) {}
1225 
1226 } // namespace gui
1227 } // namespace visualization
1228 } // namespace cloudViewer
Point mouse_pos
Rect frame
std::function< void(std::shared_ptr< core::Tensor >)> callback
int width
int size
std::string name
int height
#define NULL
core::Tensor result
Definition: VtkUtils.cpp:76
const std::vector< FontDescription > & GetFontDescriptions() const
static constexpr FontId DEFAULT_FONT_ID
Identifier for font used by default for all UI elements.
Definition: Application.h:57
std::shared_ptr< Menu > GetMenubar() const
static std::shared_ptr< Horiz > MakeCentered(std::shared_ptr< Widget > w)
Definition: Layout.cpp:531
static constexpr ItemId NO_ITEM
Definition: MenuBase.h:29
virtual void SetWindowPos(OSWindow w, int x, int y)=0
virtual Size GetWindowSizePixels(OSWindow w) const =0
virtual void PostRedrawEvent(OSWindow w)=0
virtual Point GetWindowPos(OSWindow w) const =0
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.
Definition: Window.cpp:595
const std::vector< std::shared_ptr< Widget > > & GetChildren() const
Definition: Window.cpp:431
void SetNeedsLayout()
Instructs the window to relayout before the next draw.
Definition: Window.cpp:571
void OnTickEvent(const TickEvent &e)
Definition: Window.cpp:1204
void OnTextInput(const TextInputEvent &e)
Definition: Window.cpp:1195
const Theme & GetTheme() const
Definition: Window.cpp:445
Window(const std::string &title, int flags=0)
Definition: Window.cpp:237
Point GlobalToWindowCoord(int global_x, int global_y)
Returns the global point (in OS pixels) in window local coordinates.
Definition: Window.cpp:543
void SetSize(const Size &size)
Sets the size of the window in pixels. Includes menubar on Linux.
Definition: Window.cpp:508
void AddChild(std::shared_ptr< Widget > w)
Definition: Window.cpp:597
WindowSystem::OSWindow GetOSWindow() const
Definition: Window.cpp:701
void SetOnMenuItemActivated(Menu::ItemId item_id, std::function< void()> callback)
Definition: Window.cpp:602
void SetOnKeyEvent(std::function< bool(const KeyEvent &)> callback)
Definition: Window.cpp:615
visualization::rendering::Renderer & GetRenderer() const
Definition: Window.cpp:447
void SetTitle(const char *title)
Definition: Window.cpp:466
virtual void Layout(const LayoutContext &context)
Definition: Window.cpp:681
void ShowDialog(std::shared_ptr< Dialog > dlg)
Definition: Window.cpp:619
void OnKeyEvent(const KeyEvent &e)
Definition: Window.cpp:1154
void SetOnTickEvent(std::function< bool()> callback)
Definition: Window.cpp:607
void CloseDialog()
Closes the dialog.
Definition: Window.cpp:648
void ShowMessageBox(const char *title, const char *message)
Definition: Window.cpp:661
float GetScaling() const
Returns the scaling factor from OS pixels to device pixels.
Definition: Window.cpp:538
void SetOnClose(std::function< bool()> callback)
Definition: Window.cpp:611
virtual void OnMenuItemSelected(Menu::ItemId item_id)
Definition: Window.cpp:693
void OnMouseEvent(const MouseEvent &e)
Definition: Window.cpp:1034
virtual void OnDragDropped(const char *path)
Definition: Window.cpp:1224
WebRTCWindowSystem is a BitmapWindowSystem with a WebRTC server that sends video frames to remote cli...
#define LogWarning(...)
Definition: Logging.h:72
int min(int a, int b)
Definition: cutil_math.h:53
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
int max(int a, int b)
Definition: cutil_math.h:48
float scaling
Definition: Window.cpp:78
ImGuiContext * context
Definition: Window.cpp:76
std::unique_ptr< ImguiFilamentBridge > imgui_bridge
Definition: Window.cpp:75
const Theme * theme
Definition: Window.cpp:74
std::vector< ImFont * > fonts
Definition: Window.cpp:77
static const std::string path
Definition: PointCloud.cpp:59
ImVec4 colorToImgui(const Color &color)
Definition: Util.cpp:20
Generic file read and write utility for python interface.
struct cloudViewer::visualization::gui::MouseEvent::@17::@21 wheel
Rect UnionedWith(const Rect &r) const
Definition: Gui.cpp:46
bool Contains(int x, int y) const
Definition: Gui.cpp:39
visualization::rendering::FilamentRenderer * renderer_
Definition: Window.cpp:212
std::unordered_map< Menu::ItemId, std::function< void()> > menu_callbacks_
Definition: Window.cpp:199
std::function< bool(void)> on_close_
Definition: Window.cpp:201
std::queue< std::function< void()> > deferred_until_before_draw_
Definition: Window.cpp:224
std::vector< std::shared_ptr< Widget > > children_
Definition: Window.cpp:214
std::shared_ptr< Dialog > active_dialog_
Definition: Window.cpp:222
std::function< bool(void)> on_tick_event_
Definition: Window.cpp:200
std::function< bool(const KeyEvent &)> on_key_event_
Definition: Window.cpp:202
std::queue< std::function< void()> > deferred_until_draw_
Definition: Window.cpp:225