Skip to content

Commit 94dccba

Browse files
committed
fix(tooltip): tooltip not appearing in RunMode::Lazy
This commit adds a background thread that triggers a redraw 200ms after the last user interaction if the user was hovering a widget with tooltip at that point.
1 parent 77b4330 commit 94dccba

File tree

4 files changed

+99
-80
lines changed

4 files changed

+99
-80
lines changed

include/nanogui/common.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,18 @@ extern NANOGUI_EXPORT std::vector<std::pair<int, std::string>>
354354
extern NANOGUI_EXPORT int __nanogui_get_image(NVGcontext *ctx, std::string_view name,
355355
uint8_t *data, uint32_t size);
356356

357+
template <typename Func> struct scope_guard {
358+
scope_guard(Func &&func) : func(std::move(func)) { };
359+
~scope_guard() { func(); }
360+
scope_guard(const Func &) = delete;
361+
scope_guard() = delete;
362+
scope_guard& operator=(const Func &) = delete;
363+
scope_guard& operator=(Func&&) = delete;
364+
365+
private:
366+
Func func;
367+
};
368+
357369
NAMESPACE_END(nanogui)
358370

359371
NAMESPACE_BEGIN(drjit)

include/nanogui/screen.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,6 @@ class NANOGUI_EXPORT Screen : public Widget {
249249
void set_shutdown_glfw(bool v) { m_shutdown_glfw = v; }
250250
bool shutdown_glfw() { return m_shutdown_glfw; }
251251

252-
/// Is a tooltip currently fading in?
253-
bool tooltip_fade_in_progress() const;
254-
255252
using Widget::perform_layout;
256253

257254
/// Compute the layout of all widgets
@@ -331,6 +328,10 @@ class NANOGUI_EXPORT Screen : public Widget {
331328
std::function<void(Vector2i)> m_resize_callback;
332329
RunMode m_last_run_mode;
333330
ref<Texture> m_depth_stencil_texture;
331+
332+
double m_tooltip_delay = 0.2;
333+
bool m_tooltip_redraw_required = false;
334+
void *m_tooltip_redraw_notify_thread;
334335
#if defined(NANOGUI_USE_METAL)
335336
void *m_metal_texture = nullptr;
336337
void *m_metal_drawable = nullptr;

src/common.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -273,18 +273,6 @@ load_image_directory(NVGcontext *ctx, const std::string &path) {
273273
return result;
274274
}
275275

276-
template <typename Func> struct scope_guard {
277-
scope_guard(Func &&func) : func(std::move(func)) { };
278-
~scope_guard() { func(); }
279-
scope_guard(const Func &) = delete;
280-
scope_guard() = delete;
281-
scope_guard& operator=(const Func &) = delete;
282-
scope_guard& operator=(Func&&) = delete;
283-
284-
private:
285-
Func func;
286-
};
287-
288276
std::vector<std::string>
289277
file_dialog(Widget *parent,
290278
FileDialogType type,

src/screen.cpp

Lines changed: 83 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <algorithm>
2323
#include <cstdlib>
2424
#include <iostream>
25+
#include <thread>
2526

2627
#if defined(EMSCRIPTEN)
2728
# include <emscripten/emscripten.h>
@@ -553,6 +554,21 @@ void Screen::initialize(GLFWwindow *window, bool shutdown_glfw) {
553554
/// Fixes retina display-related font rendering issue (#185)
554555
nvgBeginFrame(m_nvg_context, m_size[0], m_size[1], m_pixel_ratio);
555556
nvgEndFrame(m_nvg_context);
557+
558+
m_tooltip_redraw_notify_thread = new std::thread([this]() {
559+
while (true) {
560+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
561+
if (glfwWindowShouldClose(m_glfw_window))
562+
break;
563+
if (m_visible && m_last_run_mode == RunMode::Lazy) {
564+
double elapsed = glfwGetTime() - m_last_interaction;
565+
if (elapsed > m_tooltip_delay && m_tooltip_redraw_required) {
566+
m_tooltip_redraw_required = false;
567+
redraw();
568+
}
569+
}
570+
}
571+
});
556572
}
557573

558574
Screen::~Screen() {
@@ -582,6 +598,13 @@ Screen::~Screen() {
582598

583599
if (m_glfw_window && m_shutdown_glfw)
584600
glfwDestroyWindow(m_glfw_window);
601+
602+
if (m_tooltip_redraw_notify_thread) {
603+
std::thread *t = (std::thread *) m_tooltip_redraw_notify_thread;
604+
if (t->joinable())
605+
t->join();
606+
delete t;
607+
}
585608
}
586609

587610
void Screen::set_visible(bool visible) {
@@ -772,72 +795,76 @@ void Screen::nvg_flush() {
772795

773796
void Screen::draw_widgets() {
774797
nvgBeginFrame(m_nvg_context, m_size[0], m_size[1], m_pixel_ratio);
798+
auto end_frame_guard = scope_guard([this] { nvgEndFrame(m_nvg_context); });
775799

776800
draw(m_nvg_context);
777801

802+
/* Draw tooltips */
803+
const Widget *widget = find_widget(m_mouse_pos);
804+
while (widget && widget->tooltip().empty())
805+
widget = widget->parent();
806+
807+
if (!widget || widget->tooltip().empty()) {
808+
m_tooltip_redraw_required = false;
809+
return;
810+
}
811+
778812
double elapsed = glfwGetTime() - m_last_interaction;
779813

780-
if (elapsed > 0.2f) {
781-
/* Draw tooltips */
782-
const Widget *widget = find_widget(m_mouse_pos);
783-
while (widget && widget->tooltip().empty())
784-
widget = widget->parent();
785-
786-
if (widget && !widget->tooltip().empty()) {
787-
int tooltip_width = 180;
788-
789-
float bounds[4];
790-
nvgFontFace(m_nvg_context, "sans");
791-
nvgFontSize(m_nvg_context, 15.0f);
792-
nvgTextAlign(m_nvg_context, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
793-
nvgTextLineHeight(m_nvg_context, 1.1f);
794-
Vector2i pos = widget->absolute_position() +
795-
Vector2i(widget->width() / 2, widget->height() + 10);
796-
797-
std::string_view tooltip = widget->tooltip();
798-
nvgTextBounds(m_nvg_context, pos.x(), pos.y(),
799-
tooltip.data(), tooltip.data() + tooltip.size(), bounds);
800-
801-
int h = (bounds[2] - bounds[0]) / 2;
802-
if (h > tooltip_width / 2) {
803-
nvgTextAlign(m_nvg_context, NVG_ALIGN_CENTER | NVG_ALIGN_TOP);
804-
nvgTextBoxBounds(m_nvg_context, pos.x(), pos.y(), tooltip_width,
805-
tooltip.data(), tooltip.data() + tooltip.size(), bounds);
806-
807-
h = (bounds[2] - bounds[0]) / 2;
808-
}
809-
int shift = 0;
810-
811-
if (pos.x() - h - 8 < 0) {
812-
/* Keep tooltips on screen */
813-
shift = pos.x() - h - 8;
814-
pos.x() -= shift;
815-
bounds[0] -= shift;
816-
bounds[2] -= shift;
817-
}
814+
if (elapsed <= m_tooltip_delay) {
815+
m_tooltip_redraw_required = true;
816+
} else {
817+
int tooltip_width = 180;
818+
819+
float bounds[4];
820+
nvgFontFace(m_nvg_context, "sans");
821+
nvgFontSize(m_nvg_context, 15.0f);
822+
nvgTextAlign(m_nvg_context, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
823+
nvgTextLineHeight(m_nvg_context, 1.1f);
824+
Vector2i pos = widget->absolute_position() +
825+
Vector2i(widget->width() / 2, widget->height() + 10);
826+
827+
std::string_view tooltip = widget->tooltip();
828+
nvgTextBounds(m_nvg_context, pos.x(), pos.y(),
829+
tooltip.data(), tooltip.data() + tooltip.size(), bounds);
830+
831+
int h = (bounds[2] - bounds[0]) / 2;
832+
if (h > tooltip_width / 2) {
833+
nvgTextAlign(m_nvg_context, NVG_ALIGN_CENTER | NVG_ALIGN_TOP);
834+
nvgTextBoxBounds(m_nvg_context, pos.x(), pos.y(), tooltip_width,
835+
tooltip.data(), tooltip.data() + tooltip.size(), bounds);
836+
837+
h = (bounds[2] - bounds[0]) / 2;
838+
}
839+
int shift = 0;
840+
841+
if (pos.x() - h - 8 < 0) {
842+
/* Keep tooltips on screen */
843+
shift = pos.x() - h - 8;
844+
pos.x() -= shift;
845+
bounds[0] -= shift;
846+
bounds[2] -= shift;
847+
}
818848

819-
nvgGlobalAlpha(m_nvg_context, 0.8f);
849+
nvgGlobalAlpha(m_nvg_context, 0.8f);
820850

821-
nvgBeginPath(m_nvg_context);
822-
nvgFillColor(m_nvg_context, Color(0, 255));
823-
nvgRoundedRect(m_nvg_context, bounds[0] - 4 - h, bounds[1] - 4,
824-
(int) (bounds[2] - bounds[0]) + 8,
825-
(int) (bounds[3] - bounds[1]) + 8, 3);
851+
nvgBeginPath(m_nvg_context);
852+
nvgFillColor(m_nvg_context, Color(0, 255));
853+
nvgRoundedRect(m_nvg_context, bounds[0] - 4 - h, bounds[1] - 4,
854+
(int) (bounds[2] - bounds[0]) + 8,
855+
(int) (bounds[3] - bounds[1]) + 8, 3);
826856

827-
int px = (int) ((bounds[2] + bounds[0]) / 2) - h + shift;
828-
nvgMoveTo(m_nvg_context, px, bounds[1] - 10);
829-
nvgLineTo(m_nvg_context, px + 7, bounds[1] + 1);
830-
nvgLineTo(m_nvg_context, px - 7, bounds[1] + 1);
831-
nvgFill(m_nvg_context);
857+
int px = (int) ((bounds[2] + bounds[0]) / 2) - h + shift;
858+
nvgMoveTo(m_nvg_context, px, bounds[1] - 10);
859+
nvgLineTo(m_nvg_context, px + 7, bounds[1] + 1);
860+
nvgLineTo(m_nvg_context, px - 7, bounds[1] + 1);
861+
nvgFill(m_nvg_context);
832862

833-
nvgFillColor(m_nvg_context, Color(255, 255));
834-
nvgFontBlur(m_nvg_context, 0.0f);
835-
nvgTextBox(m_nvg_context, pos.x() - h, pos.y(), tooltip_width,
836-
tooltip.data(), tooltip.data() + tooltip.size());
837-
}
863+
nvgFillColor(m_nvg_context, Color(255, 255));
864+
nvgFontBlur(m_nvg_context, 0.0f);
865+
nvgTextBox(m_nvg_context, pos.x() - h, pos.y(), tooltip_width,
866+
tooltip.data(), tooltip.data() + tooltip.size());
838867
}
839-
840-
nvgEndFrame(m_nvg_context);
841868
}
842869

843870
bool Screen::keyboard_event(int key, int scancode, int action, int modifiers) {
@@ -1128,15 +1155,6 @@ void Screen::move_window_to_front(Window *window) {
11281155
} while (changed);
11291156
}
11301157

1131-
bool Screen::tooltip_fade_in_progress() const {
1132-
double elapsed = glfwGetTime() - m_last_interaction;
1133-
if (elapsed < 0.25f || elapsed > 1.25f)
1134-
return false;
1135-
/* Temporarily increase the frame rate to fade in the tooltip */
1136-
const Widget *widget = find_widget(m_mouse_pos);
1137-
return widget && !widget->tooltip().empty();
1138-
}
1139-
11401158
#if defined(NANOGUI_USE_OPENGL) || defined(NANOGUI_USE_GLES)
11411159
uint32_t Screen::framebuffer_handle() const {
11421160
return applies_color_management() ? m_color_pass->framebuffer_handle() : 0;

0 commit comments

Comments
 (0)