fix(gui): guard gizmo actions before deferred initialization (#16268)

GLGizmosManager can be called while its gizmo list is empty, either at startup before the canvas has loaded the gizmos or after their init() failed. reset_all_states(), open_gizmo(), open_gizmo_by_shortcut() and activate_gizmo() now return early in that case. This fixes a Debug-build assert when a new project resets the gizmos at startup, and an out-of-bounds read in release builds when a gizmo shortcut key is pressed after gizmo init failed. activate_gizmo(Undefined) still returns true, since no gizmo is open. The reset_all_states() guard was first proposed in #16074 by kenrap.

Co-authored-by: kenrap <101818165+kenrap@users.noreply.github.com>
This commit is contained in:
Downtime
2026-10-12 18:25:17 -05:00
committed by GitHub
co-authored by kenrap
parent 3dd547c8be
commit a28c9c3a82
+13 -5
View File
@@ -420,7 +420,10 @@ void GLGizmosManager::refresh_on_off_state()
void GLGizmosManager::reset_all_states()
{
if (! m_enabled || m_serializing)
// The restore/new-project event can run before deferred OpenGL resource
// loading has populated the gizmo list. There is nothing to reset until
// initialization completes.
if (! m_enabled || m_serializing || m_gizmos.empty())
return;
const EType current = get_current_type();
@@ -435,6 +438,9 @@ void GLGizmosManager::reset_all_states()
bool GLGizmosManager::open_gizmo(EType type)
{
if (m_gizmos.empty())
return false;
int idx = static_cast<int>(type);
// re-open same type cause closing
@@ -522,7 +528,7 @@ bool GLGizmosManager::is_running() const
bool GLGizmosManager::open_gizmo_by_shortcut(Shortcut shortcut)
{
if (!m_enabled)
if (!m_enabled || m_gizmos.empty())
return false;
// The text tool opens without a selection because it creates its own object.
@@ -1427,10 +1433,12 @@ void GLGizmosManager::update_hover_state(const EType &type)
bool GLGizmosManager::activate_gizmo(EType type)
{
assert(!m_gizmos.empty());
// already activated
if (m_current == type) return true;
if (m_current == type)
return true;
if (m_gizmos.empty())
return false;
if (m_current != Undefined) {
// clean up previous gizmo