Fix #115937: Context override fails to restore screen

The recent addition screen override [0] failed to account for the
override windows screen being overridden when restoring the context.

When overriding the window and screen, it's incorrect to use the
original contexts screen when restoring the screen of the overriding
window.

This window's screen may not be in the original context or the
overriding screen. Resolve the bug by storing this separately
and use it when restoring the context.

[0]: 6af92e1360
This commit is contained in:
Campbell Barton 2023-12-10 23:01:10 +11:00
parent 22d65bad95
commit 12c4d22e6d
1 changed files with 27 additions and 2 deletions

View File

@ -114,6 +114,15 @@ struct BPyContextTempOverride {
ContextStore ctx_init;
ContextStore ctx_temp;
struct {
/**
* The original screen of `ctx_temp.win`, needed when restoring this windows screen as it
* won't be `ctx_init.screen` (when switching the window as well as the screen), see #115937.
*/
bScreen *screen;
} ctx_temp_orig;
/** Bypass Python overrides set when calling an operator from Python. */
bContext_PyState py_state;
/**
@ -248,6 +257,12 @@ static PyObject *bpy_rna_context_temp_override_enter(BPyContextTempOverride *sel
}
}
/* Manipulate the context (setup). */
if (self->ctx_temp.screen_is_set) {
self->ctx_temp_orig.screen = WM_window_get_active_screen(win);
bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp.screen);
}
/* NOTE: always set these members, even when they are equal to the current values because
* setting the window (for e.g.) clears the area & region, setting the area clears the region.
* While it would be useful in some cases to leave the context as-is when setting members
@ -262,7 +277,6 @@ static PyObject *bpy_rna_context_temp_override_enter(BPyContextTempOverride *sel
CTX_wm_window_set(C, self->ctx_temp.win);
}
if (self->ctx_temp.screen_is_set) {
bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp.screen);
CTX_wm_screen_set(C, self->ctx_temp.screen);
}
if (self->ctx_temp.area_is_set) {
@ -282,6 +296,16 @@ static PyObject *bpy_rna_context_temp_override_exit(BPyContextTempOverride *self
Main *bmain = CTX_data_main(C);
/* Manipulate the context (restore). */
if (self->ctx_temp.screen_is_set) {
if (self->ctx_temp_orig.screen && wm_check_screen_exists(bmain, self->ctx_temp_orig.screen)) {
wmWindow *win = self->ctx_temp.win_is_set ? self->ctx_temp.win : self->ctx_init.win;
if (win && wm_check_window_exists(bmain, win)) {
bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp_orig.screen);
}
}
}
/* Account for for the window to be freed on file-read,
* in this case the window should not be restored, see: #92818.
* Also account for other windowing members to be removed on exit,
@ -330,7 +354,6 @@ static PyObject *bpy_rna_context_temp_override_exit(BPyContextTempOverride *self
if (do_restore) {
if (self->ctx_init.screen_is_set || is_container_set) {
bpy_rna_context_temp_set_screen_for_window(C, self->ctx_init.win, self->ctx_init.screen);
CTX_wm_screen_set(C, self->ctx_init.screen);
is_container_set = true;
}
@ -611,6 +634,8 @@ static PyObject *bpy_context_temp_override(PyObject *self, PyObject *args, PyObj
ret->ctx_temp = ctx_temp;
memset(&ret->ctx_init, 0, sizeof(ret->ctx_init));
ret->ctx_temp_orig.screen = nullptr;
ret->py_state_context_dict = kwds;
return (PyObject *)ret;