11 #define WIN32_LEAN_AND_MEAN
16 #include <GLFW/glfw3.h>
26 #include <unordered_set>
46 const double RUNLOOP_DELAY_SEC = 0.010;
48 std::string FindResourcePath(
int argc,
const char *argv[]) {
51 if (argc != 0 && argv) {
56 for (
auto &c : argv0) {
63 auto last_slash = argv0.rfind(
"/");
64 auto path = argv0.substr(0, last_slash);
66 if (argv0[0] ==
'/' ||
67 (argv0.size() > 3 && argv0[1] ==
':' && argv0[2] ==
'/')) {
77 if (cwd.rfind(
"/Contents/Resources") == cwd.size() - 19) {
85 if (
path.rfind(
"MacOS") ==
path.size() - 5) {
86 return path.substr(0,
path.size() - 5) +
"Resources";
91 {
"/resources",
"/../resources" ,
93 "/share/cloudViewer/resources" }) {
97 return path + subpath;
108 namespace visualization {
122 std::unordered_set<std::shared_ptr<Window>>
windows_;
129 std::function<void()>
f;
187 auto alert = std::make_shared<Window>((title ? title :
"Alert"),
189 auto em = alert->GetTheme().font_size;
190 auto layout = std::make_shared<Vert>(em,
Margins(em));
191 auto msg = std::make_shared<Label>(message);
192 auto ok = std::make_shared<Button>(
"Ok");
193 ok->SetOnClicked([alert = alert.get() ]() {
194 Application::GetInstance().RemoveWindow(alert);
198 alert->AddChild(layout);
202 Application::Application() : impl_(new
Application::Impl()) {
203 Color highlight_color(0.5, 0.5, 0.5);
206 impl_->theme_.font_path =
208 impl_->theme_.font_bold_path =
"Roboto-Bold.ttf";
209 impl_->theme_.font_italic_path =
"Roboto-MediumItalic.ttf";
210 impl_->theme_.font_bold_italic_path =
"Roboto-BoldItalic.ttf";
211 impl_->theme_.font_mono_path =
"RobotoMono-Medium.ttf";
212 impl_->theme_.font_size = 16;
213 impl_->theme_.default_margin = 8;
214 impl_->theme_.default_layout_spacing = 6;
216 impl_->theme_.background_color =
Color(0.175f, 0.175f, 0.175f);
217 impl_->theme_.text_color =
Color(0.875f, 0.875f, 0.875f);
218 impl_->theme_.border_width = 1;
219 impl_->theme_.border_radius = 3;
220 impl_->theme_.border_color =
Color(0.5f, 0.5f, 0.5f);
221 impl_->theme_.menubar_border_color =
Color(0.25f, 0.25f, 0.25f);
222 impl_->theme_.button_color =
Color(0.4f, 0.4f, 0.4f);
223 impl_->theme_.button_hover_color =
Color(0.6f, 0.6f, 0.6f);
224 impl_->theme_.button_active_color =
Color(0.5f, 0.5f, 0.5f);
225 impl_->theme_.button_on_color =
Color(0.7f, 0.7f, 0.7f);
226 impl_->theme_.button_on_hover_color =
Color(0.9f, 0.9f, 0.9f);
227 impl_->theme_.button_on_active_color =
Color(0.8f, 0.8f, 0.8f);
228 impl_->theme_.button_on_text_color =
Color(0, 0, 0);
229 impl_->theme_.checkbox_background_off_color =
Color(0.333f, 0.333f, .333f);
230 impl_->theme_.checkbox_background_on_color = highlight_color;
231 impl_->theme_.checkbox_background_hover_off_color =
Color(0.5f, 0.5f, 0.5f);
232 impl_->theme_.checkbox_background_hover_on_color =
233 highlight_color.Lightened(0.15f);
234 impl_->theme_.checkbox_check_color =
Color(0.9f, 0.9f, 0.9f);
235 impl_->theme_.radiobtn_background_off_color =
Color(0.333f, 0.333f, .333f);
236 impl_->theme_.radiobtn_background_on_color = highlight_color;
237 impl_->theme_.radiobtn_background_hover_off_color =
Color(0.5f, 0.5f, 0.5f);
238 impl_->theme_.radiobtn_background_hover_on_color =
239 highlight_color.Lightened(0.15f);
240 impl_->theme_.toggle_background_off_color =
241 impl_->theme_.checkbox_background_off_color;
242 impl_->theme_.toggle_background_on_color =
Color(0.666f, 0.666f, 0.666f);
243 impl_->theme_.toggle_background_hover_off_color =
244 impl_->theme_.checkbox_background_hover_off_color;
245 impl_->theme_.toggle_background_hover_on_color =
246 impl_->theme_.toggle_background_on_color.Lightened(0.15f);
247 impl_->theme_.toggle_thumb_color =
Color(1, 1, 1);
248 impl_->theme_.combobox_background_color =
Color(0.4f, 0.4f, 0.4f);
249 impl_->theme_.combobox_hover_color =
Color(0.5f, 0.5f, 0.5f);
250 impl_->theme_.combobox_arrow_background_color = highlight_color;
251 impl_->theme_.slider_grab_color =
Color(0.666f, 0.666f, 0.666f);
252 impl_->theme_.text_edit_background_color =
Color(0.1f, 0.1f, 0.1f);
253 impl_->theme_.list_background_color =
Color(0.1f, 0.1f, 0.1f);
254 impl_->theme_.list_hover_color =
Color(0.6f, 0.6f, 0.6f);
255 impl_->theme_.list_selected_color =
Color(0.5f, 0.5f, 0.5f);
256 impl_->theme_.tree_background_color = impl_->theme_.list_background_color;
257 impl_->theme_.tree_selected_color = impl_->theme_.list_selected_color;
258 impl_->theme_.tab_inactive_color = impl_->theme_.button_color;
259 impl_->theme_.tab_hover_color = impl_->theme_.button_hover_color;
260 impl_->theme_.tab_active_color = impl_->theme_.button_active_color;
261 impl_->theme_.dialog_border_width = 1;
262 impl_->theme_.dialog_border_radius = 10;
274 char *argv = strdup(
path.c_str());
280 Initialize(FindResourcePath(argc, argv).c_str());
287 impl_->PrepareForRunning();
289 if (impl_->is_initialized_) {
294 std::string uiblit_path = std::string(resource_path) +
"/ui_blit.filamat";
297 "Resource directory does not have CloudViewer resources: {}",
301 impl_->theme_.font_path = std::string(resource_path) + std::string(
"/") +
302 impl_->theme_.font_path;
303 impl_->theme_.font_bold_path = std::string(resource_path) +
305 impl_->theme_.font_bold_path;
306 impl_->theme_.font_italic_path = std::string(resource_path) +
308 impl_->theme_.font_italic_path;
309 impl_->theme_.font_bold_italic_path = std::string(resource_path) +
311 impl_->theme_.font_bold_italic_path;
312 impl_->theme_.font_mono_path = std::string(resource_path) +
314 impl_->theme_.font_mono_path;
315 if (impl_->fonts_.empty()) {
319 impl_->is_initialized_ =
true;
323 if (impl_->is_initialized_) {
335 "gui::Initialize() must be called before creating a window or UI "
341 std::dynamic_pointer_cast<GLFWWindowSystem>(impl_->window_system_);
342 return (os_ws !=
nullptr);
346 return *impl_->window_system_;
350 if (impl_->window_system_ !=
nullptr) {
353 impl_->window_system_ = ws;
354 impl_->is_ws_initialized_ =
false;
358 FontId id = impl_->fonts_.size();
359 impl_->fonts_.push_back(fd);
365 auto GetSansSerifPath = [
this](
FontStyle style) {
368 return impl_->theme_.font_bold_path;
370 return impl_->theme_.font_italic_path;
372 return impl_->theme_.font_bold_italic_path;
374 return impl_->theme_.font_path;
378 auto GetStyleName = [](
FontStyle style) {
385 return "BOLD_ITALIC";
391 impl_->fonts_[id] = fd;
392 auto style = impl_->fonts_[id].style_;
393 for (
auto &range : impl_->fonts_[
id].ranges_) {
396 range.path = GetSansSerifPath(style);
398 range.path = impl_->theme_.font_mono_path;
408 if (fallback.empty()) {
410 fallback = GetSansSerifPath(style);
413 range.path, GetStyleName(style));
414 range.path = fallback;
424 return impl_->fonts_;
428 static auto g_tzero = std::chrono::steady_clock::now();
429 std::chrono::duration<double> t =
430 std::chrono::steady_clock::now() - g_tzero;
435 return impl_->menubar_;
439 auto old = impl_->menubar_;
440 impl_->menubar_ = menubar;
443 if ((!old && menubar) || (old && !menubar)) {
444 for (
auto w : impl_->windows_) {
449 #if defined(__APPLE__)
450 auto *native = menubar->GetNativePointer();
452 SetNativeMenubar(native);
460 impl_->windows_.insert(window);
464 if (impl_->should_quit_) {
468 for (
auto it = impl_->windows_.begin(); it != impl_->windows_.end(); ++it) {
469 if (it->get() == window) {
470 impl_->windows_to_be_destroyed_.insert(*it);
471 impl_->windows_.erase(it);
472 if (impl_->windows_.empty()) {
473 impl_->should_quit_ =
true;
482 while (!impl_->windows_.empty()) {
506 for (
auto w : impl_->windows_to_be_destroyed_) {
509 impl_->windows_to_be_destroyed_.clear();
510 impl_->CleanupAfterRunning();
514 for (
auto w : impl_->windows_) {
515 if (w->IsActiveWindow()) {
516 w->OnMenuItemSelected(itemId);
536 bool cleanup_if_no_windows ) {
538 if (!impl_->is_running_) {
542 if (!impl_->is_initialized_) {
544 "Internal error: Application::Initialize() was not called");
549 std::stringstream err;
550 err <<
"Could not find resource directory:\n'" << resource_path
551 <<
"' does not exist";
556 std::stringstream err;
557 err <<
"Could not load UI font:\n'" << impl_->theme_.font_path
558 <<
"' does not exist";
563 impl_->PrepareForRunning();
564 impl_->is_running_ =
true;
568 auto status = ProcessQueuedEvents(unlocker);
571 if (status == RunStatus::DONE) {
572 if (cleanup_if_no_windows) {
575 for (
auto it = impl_->running_tasks_.begin();
576 it != impl_->running_tasks_.end(); ++it) {
579 impl_->running_tasks_.erase(current);
582 impl_->is_running_ =
false;
583 impl_->CleanupAfterRunning();
586 impl_->should_quit_ =
false;
589 return (status == RunStatus::CONTINUE);
592 Application::RunStatus Application::ProcessQueuedEvents(EnvUnlocker &unlocker) {
594 impl_->window_system_->WaitEventsTimeout(RUNLOOP_DELAY_SEC);
600 if (now - impl_->last_time_ >= 0.95 * RUNLOOP_DELAY_SEC) {
601 for (
auto w : impl_->windows_) {
604 impl_->last_time_ = now;
610 decltype(impl_->posted_) posted;
617 std::lock_guard<std::mutex> lock(impl_->posted_lock_);
619 posted = std::move(impl_->posted_);
622 for (
auto &p : posted) {
628 for (
auto w : impl_->windows_) {
629 if (w.get() == p.window) {
640 old = p.window->MakeDrawContextCurrent();
644 p.window->RestoreDrawContext(old);
645 p.window->PostRedraw();
650 impl_->running_tasks_.remove_if(
651 [](
const Task &t) {
return t.IsFinished(); });
656 impl_->windows_to_be_destroyed_.clear();
658 if (impl_->should_quit_) {
659 return RunStatus::DONE;
661 return RunStatus::CONTINUE;
666 impl_->running_tasks_.emplace_back(f);
667 impl_->running_tasks_.back().Run();
671 std::lock_guard<std::mutex> lock(impl_->posted_lock_);
672 impl_->posted_.emplace_back(window, f);
687 std::shared_ptr<geometry::Image> img;
688 auto callback = [&img](std::shared_ptr<geometry::Image> _img) {
711 bool z_in_view_space ) {
712 std::shared_ptr<geometry::Image> img;
713 auto callback = [&img](std::shared_ptr<geometry::Image> _img) {
std::function< void(std::shared_ptr< core::Tensor >)> callback
void SetMenubar(std::shared_ptr< Menu > menubar)
const std::vector< FontDescription > & GetFontDescriptions() const
std::shared_ptr< geometry::Image > RenderToImage(rendering::Renderer &renderer, rendering::View *view, rendering::Scene *scene, int width, int height)
bool UsingNativeWindows() const
static Application & GetInstance()
static constexpr FontId DEFAULT_FONT_ID
Identifier for font used by default for all UI elements.
void SetFont(FontId id, const FontDescription &fd)
void RemoveWindow(Window *window)
void VerifyIsInitialized()
std::shared_ptr< Menu > GetMenubar() const
const char * GetResourcePath() const
WindowSystem & GetWindowSystem() const
void RunInThread(std::function< void()> f)
FontId AddFont(const FontDescription &fd)
void Run()
Does not return until the UI is completely finished.
void SetWindowSystem(std::shared_ptr< WindowSystem > ws)
void ShowMessageBox(const char *title, const char *message)
void OnMenuItemSelected(Menu::ItemId itemId)
Delivers the itemId to the active window. Used internally.
void AddWindow(std::shared_ptr< Window > window)
Must be called on the same thread that calls Run()
void Quit()
Closes all the windows, which exits as a result.
const Theme & GetTheme() const
void PostToMainThread(Window *window, std::function< void()> f)
std::shared_ptr< geometry::Image > RenderToDepthImage(rendering::Renderer &renderer, rendering::View *view, rendering::Scene *scene, int width, int height, bool z_in_view_space=false)
bool RunOneTick(EnvUnlocker &unlocker, bool cleanup_if_no_windows=true)
constexpr static const char * MONOSPACE
constexpr static const char * SANS_SERIF
static std::shared_ptr< Horiz > MakeCentered(std::shared_ptr< Widget > w)
static const int FLAG_TOPMOST
static void SelectBackend(RenderingType type)
static const std::string & GetResourcePath()
static void DestroyInstance()
static void SetResourcePath(const std::string &resource_path)
virtual void EndFrame()=0
void RenderToImage(View *view, Scene *scene, std::function< void(std::shared_ptr< geometry::Image >)> cb)
virtual void BeginFrame()=0
void RenderToDepthImage(View *view, Scene *scene, std::function< void(std::shared_ptr< geometry::Image >)> cb, bool z_in_view_space=false)
virtual void SetViewport(std::int32_t x, std::int32_t y, std::uint32_t w, std::uint32_t h)=0
static const std::string path
std::string GetWorkingDirectory()
bool DirectoryExists(const std::string &directory)
bool FileExists(const std::string &filename)
void ShowNativeAlert(const char *message)
std::string FindFontPath(std::string font, FontStyle style)
Generic file read and write utility for python interface.
Posted(Window *w, std::function< void()> func)
std::vector< Posted > posted_
std::shared_ptr< Menu > menubar_
std::vector< FontDescription > fonts_
std::unordered_set< std::shared_ptr< Window > > windows_
std::list< Task > running_tasks_
void CleanupAfterRunning()
std::unordered_set< std::shared_ptr< Window > > windows_to_be_destroyed_
std::shared_ptr< WindowSystem > window_system_