Fix #131062: Active tool does not sync in Texture Painting workspace

Resolve an error where the brushes from the image/3D viewport
where shared but the tool was not. Causing the brush not to match
the appropriate tool when both an image & 3D viewport were displayed.

Resolve the issue by adding support for a "pending" tool,
a tool ID which is to be used.

This accounts for cases where it's not known if the requested tool
exists and uses the same code paths for initializing tools as is done
for initializing on file load for e.g.

Ref !133085
This commit is contained in:
Campbell Barton
2025-01-20 11:21:33 +11:00
parent 90b03d2344
commit d42b425ea9
2 changed files with 112 additions and 1 deletions

View File

@@ -73,6 +73,22 @@ typedef struct bToolRef {
/** Optionally use these when not interacting directly with the primary tools gizmo. */
char idname_fallback[64];
/**
* A pending request to switch to a different tool,
* this will be performed as part of the areas tool initialization.
* (see #toolsystem_ref_set_by_id_pending).
*
* Notes:
* - This can be used to synchronize tools between areas (if necessary).
* - If the tool doesn't exist, the exiting tool will left as is.
* - There is no need for a "fallback" version of this variable
* since activating the tool will also set it's fallback, if it's defined.
* - This is not stored in the run-time because it's possible (for example)
* for a request to sync to another area isn't handled if the area isn't visible.
* So store this in the file, so the pending change can be performed when the area is shown.
*/
char idname_pending[64];
/** Use to avoid initializing the same tool multiple times. */
short tag;

View File

@@ -54,6 +54,10 @@ static void toolsystem_refresh_screen_from_active_tool(Main *bmain,
bToolRef *tref);
static void toolsystem_ref_set_by_brush_type(bContext *C, const char *brush_type);
static void toolsystem_ref_set_by_id_pending(Main *bmain,
bToolRef *tref,
const char *idname_pending);
/* -------------------------------------------------------------------- */
/** \name Tool Reference API
* \{ */
@@ -429,6 +433,39 @@ static void toolsystem_brush_activate_from_toolref(Main *bmain,
}
}
/**
* Special case, the active brush data-block for the image & 3D viewport are shared.
* This means changing the active brush tool in one space must change the tool
* for the other space as well, see: #131062.
*/
static void toolsystem_brush_sync_for_texture_paint(Main *bmain,
WorkSpace *workspace,
bToolRef *tref)
{
if (tref->space_type == SPACE_VIEW3D) {
if (tref->mode == CTX_MODE_PAINT_TEXTURE) {
bToolKey tkey{};
tkey.space_type = SPACE_IMAGE;
tkey.mode = SI_MODE_PAINT;
bToolRef *tref_other = WM_toolsystem_ref_find(workspace, &tkey);
if (tref_other) {
toolsystem_ref_set_by_id_pending(bmain, tref_other, tref->idname);
}
}
}
else if (tref->space_type == SPACE_IMAGE) {
if (tref->mode == SI_MODE_PAINT) {
bToolKey tkey{};
tkey.space_type = SPACE_VIEW3D;
tkey.mode = CTX_MODE_PAINT_TEXTURE;
bToolRef *tref_other = WM_toolsystem_ref_find(workspace, &tkey);
if (tref_other) {
toolsystem_ref_set_by_id_pending(bmain, tref_other, tref->idname);
}
}
}
}
/** \} */
static void toolsystem_ref_link(Main *bmain, WorkSpace *workspace, bToolRef *tref)
@@ -454,6 +491,7 @@ static void toolsystem_ref_link(Main *bmain, WorkSpace *workspace, bToolRef *tre
if (tref_rt->flag & TOOLREF_FLAG_USE_BRUSHES) {
toolsystem_brush_activate_from_toolref(bmain, workspace, tref);
toolsystem_brush_sync_for_texture_paint(bmain, workspace, tref);
}
}
@@ -546,6 +584,9 @@ void WM_toolsystem_ref_set_from_runtime(bContext *C,
STRNCPY(tref->idname, idname);
/* This immediate request supersedes any unhandled pending requests. */
tref->idname_pending[0] = '\0';
if (tref->runtime == nullptr) {
tref->runtime = static_cast<bToolRef_Runtime *>(MEM_callocN(sizeof(*tref->runtime), __func__));
}
@@ -969,12 +1010,66 @@ static void toolsystem_ref_set_by_brush_type(bContext *C, const char *brush_type
}
}
/**
* Request a tool ID be activated in a context where it's not known if the tool exists,
* when the areas using this tool are not visible.
* In this case, set the `idname` as pending and flag tools area for updating.
*
* If the tool doesn't exist then the current tool is to be left as-is.
*/
static void toolsystem_ref_set_by_id_pending(Main *bmain,
bToolRef *tref,
const char *idname_pending)
{
BLI_assert(idname_pending[0]);
/* Check if the pending or current tool is already set to the requested value. */
const bool this_match = STREQ(idname_pending, tref->idname);
if (tref->idname_pending[0]) {
const bool next_match = STREQ(idname_pending, tref->idname_pending);
if (next_match) {
return;
}
/* Highly unlikely but possible the current active tool matches the name.
* In this case clear pending as there is nothing to do. */
if (this_match) {
tref->idname_pending[0] = '\0';
return;
}
}
else {
if (this_match) {
return;
}
}
STRNCPY(tref->idname_pending, idname_pending);
/* If there would be a convenient way to know which screens used which work-spaces,
* that could be used here. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->runtime.tool == tref) {
area->runtime.tool = nullptr;
area->runtime.is_tool_set = false;
area->flag |= AREA_FLAG_ACTIVE_TOOL_UPDATE;
}
}
}
}
static void toolsystem_reinit_with_toolref(bContext *C, WorkSpace *workspace, bToolRef *tref)
{
bToolKey tkey{};
tkey.space_type = tref->space_type;
tkey.mode = tref->mode;
WM_toolsystem_ref_set_by_id_ex(C, workspace, &tkey, tref->idname, false);
const char *idname = tref->idname_pending[0] ? tref->idname_pending : tref->idname;
WM_toolsystem_ref_set_by_id_ex(C, workspace, &tkey, idname, false);
/* Never attempt the pending name again, if it's not found, no need to keep trying. */
tref->idname_pending[0] = '\0';
}
static const char *toolsystem_default_tool(const bToolKey *tkey)