diff --git a/release/scripts/presets/keyconfig/3dsmax.py b/release/scripts/presets/keyconfig/3dsmax.py index a07a3a52caf..09c6704df1f 100644 --- a/release/scripts/presets/keyconfig/3dsmax.py +++ b/release/scripts/presets/keyconfig/3dsmax.py @@ -7,7 +7,7 @@ kc = wm.keyconfigs.new('3dsmax') # Map Window km = kc.keymaps.new('Window', space_type='EMPTY', region_type='WINDOW', modal=False) -kmi = km.keymap_items.new('wm.window_duplicate', 'W', 'PRESS', ctrl=True, alt=True) +kmi = km.keymap_items.new('wm.window_new', 'W', 'PRESS', ctrl=True, alt=True) kmi = km.keymap_items.new('wm.read_homefile', 'N', 'PRESS', ctrl=True) kmi = km.keymap_items.new('wm.save_homefile', 'U', 'PRESS', ctrl=True) kmi = km.keymap_items.new('wm.call_menu', 'O', 'PRESS', shift=True, ctrl=True) diff --git a/release/scripts/presets/keyconfig/maya.py b/release/scripts/presets/keyconfig/maya.py index cf213c1ddbd..194b9b3c283 100644 --- a/release/scripts/presets/keyconfig/maya.py +++ b/release/scripts/presets/keyconfig/maya.py @@ -8,7 +8,7 @@ kc = wm.keyconfigs.new(os.path.splitext(os.path.basename(__file__))[0]) # Map Window km = kc.keymaps.new('Window', space_type='EMPTY', region_type='WINDOW', modal=False) -kmi = km.keymap_items.new('wm.window_duplicate', 'W', 'PRESS', ctrl=True, alt=True) +kmi = km.keymap_items.new('wm.window_new', 'W', 'PRESS', ctrl=True, alt=True) kmi = km.keymap_items.new('wm.read_homefile', 'N', 'PRESS', ctrl=True) kmi = km.keymap_items.new('wm.save_homefile', 'U', 'PRESS', ctrl=True) kmi = km.keymap_items.new('wm.call_menu', 'O', 'PRESS', shift=True, ctrl=True) diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index fcb30130730..743832eb51f 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -281,7 +281,7 @@ class INFO_MT_window(Menu): layout = self.layout - layout.operator("wm.window_duplicate") + layout.operator("wm.window_new") layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER') layout.separator() diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index fe9b3054ea7..0b026750900 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -4399,7 +4399,7 @@ static void operator_enum_search_cb(const struct bContext *C, void *but, const c for (item = item_array; item->identifier; item++) { /* note: need to give the index rather than the identifier because the enum can be freed */ if (BLI_strcasestr(item->name, str)) { - if (false == UI_search_item_add(items, item->name, SET_INT_IN_POINTER(item->value), 0)) + if (false == UI_search_item_add(items, item->name, SET_INT_IN_POINTER(item->value), item->icon)) break; } } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 2809795268d..c8d3c85c5da 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -238,6 +238,7 @@ void WM_operator_view3d_unit_defaults(struct bContext *C, struct wmOperator *op int WM_operator_smooth_viewtx_get(const struct wmOperator *op); int WM_menu_invoke_ex(struct bContext *C, struct wmOperator *op, int opcontext); int WM_menu_invoke (struct bContext *C, struct wmOperator *op, const struct wmEvent *event); +int WM_enum_search_invoke_previews(struct bContext *C, struct wmOperator *op, short prv_cols, short prv_rows); int WM_enum_search_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); /* invoke callback, confirm menu + exec */ int WM_operator_confirm (struct bContext *C, struct wmOperator *op, const struct wmEvent *event); diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 3cbcaf2810f..2cf4e16155c 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1107,46 +1107,70 @@ int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_REGION_WIN); } +struct EnumSearchMenu { + wmOperator *op; /* the operator that will be executed when selecting an item */ + + bool use_previews; + short prv_cols, prv_rows; +}; /* generic enum search invoke popup */ -static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op) +static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg) { - static char search[256] = ""; - wmEvent event; + struct EnumSearchMenu *search_menu = arg; wmWindow *win = CTX_wm_window(C); + wmOperator *op = search_menu->op; + /* template_ID uses 4 * widget_unit for width, we use a bit more, some items may have a suffix to show */ + const int width = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_cols : UI_searchbox_size_x(); + const int height = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_rows : UI_searchbox_size_y(); + static char search[256] = ""; uiBlock *block; uiBut *but; - wmOperator *op = (wmOperator *)arg_op; block = UI_block_begin(C, ar, "_popup", UI_EMBOSS); UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU); search[0] = '\0'; + BLI_assert(search_menu->use_previews || (search_menu->prv_cols == 0 && search_menu->prv_rows == 0)); #if 0 /* ok, this isn't so easy... */ uiDefBut(block, UI_BTYPE_LABEL, 0, RNA_struct_ui_name(op->type->srna), 10, 10, UI_searchbox_size_x(), UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); #endif but = uiDefSearchButO_ptr(block, op->type, op->ptr->data, search, 0, ICON_VIEWZOOM, sizeof(search), - 10, 10, UI_searchbox_size_x(), UI_UNIT_Y, 0, 0, ""); + 10, 10, width, UI_UNIT_Y, search_menu->prv_rows, search_menu->prv_cols, ""); /* fake button, it holds space for search items */ - uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - UI_searchbox_size_y(), UI_searchbox_size_x(), UI_searchbox_size_y(), NULL, 0, 0, 0, 0, NULL); + uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - UI_searchbox_size_y(), width, height, NULL, 0, 0, 0, 0, NULL); UI_block_bounds_set_popup(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */ - - wm_event_init_from_window(win, &event); - event.type = EVT_BUT_OPEN; - event.val = KM_PRESS; - event.customdata = but; - event.customdatafree = false; - wm_event_add(win, &event); + UI_but_focus_on_enter_event(win, but); return block; } +/** + * Similar to #WM_enum_search_invoke, but draws previews. Also, this can't + * be used as invoke callback directly since it needs additional info. + */ +int WM_enum_search_invoke_previews( + bContext *C, wmOperator *op, short prv_cols, short prv_rows) +{ + static struct EnumSearchMenu search_menu; + + search_menu.op = op; + search_menu.use_previews = true; + search_menu.prv_cols = prv_cols; + search_menu.prv_rows = prv_rows; + + UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu); + + return OPERATOR_INTERFACE; +} int WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - UI_popup_block_invoke(C, wm_enum_search_menu, op); + static struct EnumSearchMenu search_menu; + search_menu.op = op; + UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu); return OPERATOR_INTERFACE; } @@ -2079,14 +2103,22 @@ static void WM_OT_window_close(wmOperatorType *ot) ot->poll = WM_operator_winactive; } -static void WM_OT_window_duplicate(wmOperatorType *ot) +static void WM_OT_window_new(wmOperatorType *ot) { - ot->name = "Duplicate Window"; - ot->idname = "WM_OT_window_duplicate"; - ot->description = "Duplicate the current Blender window"; - - ot->exec = wm_window_duplicate_exec; + PropertyRNA *prop; + + ot->name = "New Window"; + ot->idname = "WM_OT_window_new"; + ot->description = "Create a new Blender window"; + + ot->exec = wm_window_new_exec; + ot->invoke = wm_window_new_invoke; ot->poll = wm_operator_winactive_normal; + + prop = RNA_def_enum(ot->srna, "screen", DummyRNA_NULL_items, 0, "Screen", ""); + RNA_def_enum_funcs(prop, wm_window_new_screen_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; } static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot) @@ -4177,7 +4209,7 @@ void wm_operatortype_init(void) global_ops_hash = BLI_ghash_str_new_ex("wm_operatortype_init gh", 2048); WM_operatortype_append(WM_OT_window_close); - WM_operatortype_append(WM_OT_window_duplicate); + WM_operatortype_append(WM_OT_window_new); WM_operatortype_append(WM_OT_read_history); WM_operatortype_append(WM_OT_read_homefile); WM_operatortype_append(WM_OT_read_factory_settings); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index e271225e437..b6f1b431009 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -58,6 +58,7 @@ #include "RNA_access.h" +#include "RNA_define.h" #include "WM_api.h" #include "WM_types.h" @@ -71,6 +72,7 @@ #include "ED_fileselect.h" #include "UI_interface.h" +#include "UI_interface_icons.h" #include "PIL_time.h" @@ -79,6 +81,8 @@ #include "GPU_init_exit.h" #include "GPU_immediate.h" +#include "UI_resources.h" + /* for assert */ #ifndef NDEBUG # include "BLI_threads.h" @@ -244,6 +248,25 @@ wmWindow *wm_window_new(bContext *C) return win; } +/** + * A higher level version of copy that tests the new window can be added. + */ +static wmWindow *wm_window_new_test(bContext *C) +{ + wmWindow *win = wm_window_new(C); + + WM_check(C); + + if (win->ghostwin) { + WM_event_add_notifier(C, NC_WINDOW | NA_ADDED, NULL); + return win; + } + else { + wmWindowManager *wm = CTX_wm_manager(C); + wm_window_close(C, wm, win); + return NULL; + } +} /* part of wm_window.c api */ wmWindow *wm_window_copy(bContext *C, wmWindow *win_src) @@ -723,17 +746,79 @@ int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -/* operator callback */ -int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) +/* new window operator callback */ +int wm_window_new_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); wmWindow *win_src = CTX_wm_window(C); - bool ok; + const int screen_id = RNA_enum_get(op->ptr, "screen"); + bScreen *screen = BLI_findlink(&bmain->screen, screen_id); + wmWindow *win_dst; - ok = (wm_window_copy_test(C, win_src) != NULL); + if (screen->winid) { + /* Screen is already used, duplicate window and screen */ + win_dst = wm_window_copy_test(C, win_src); + } + else if ((win_dst = wm_window_new_test(C))) { + /* New window with a different screen */ + win_dst->screen = screen; + screen->winid = win_dst->winid; + CTX_wm_window_set(C, win_dst); + ED_screen_refresh(CTX_wm_manager(C), win_dst); + } - return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + return (win_dst != NULL) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } +int wm_window_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Main *bmain = CTX_data_main(C); + + if (BLI_listbase_count_ex(&bmain->screen, 2) == 1) { + RNA_enum_set(op->ptr, "screen", 0); + return wm_window_new_exec(C, op); + } + else { + return WM_enum_search_invoke_previews(C, op, 6, 2); + } +} + +struct EnumPropertyItem *wm_window_new_screen_itemf( + bContext *C, struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), bool *r_free) +{ + Main *bmain = CTX_data_main(C); + EnumPropertyItem *item = NULL; + EnumPropertyItem tmp = {0, "", 0, "", ""}; + int value = 0, totitem = 0; + int count_act_screens = 0; + /* XXX setting max number of windows to 20. We'd need support + * for dynamic strings in EnumPropertyItem.name to avoid this. */ + static char active_screens[20][MAX_NAME + 12]; + + for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) { + if (screen->winid) { + BLI_snprintf(active_screens[count_act_screens], sizeof(*active_screens), "%s (Duplicate)", + screen->id.name + 2); + tmp.name = active_screens[count_act_screens++]; + } + else { + tmp.name = screen->id.name + 2; + } + + tmp.value = value; + tmp.identifier = screen->id.name; + UI_id_icon_render(C, CTX_data_scene(C), &screen->id, true, false); + tmp.icon = BKE_icon_id_ensure(&screen->id); + + RNA_enum_item_add(&item, &totitem, &tmp); + value++; + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} /* fullscreen operator callback */ int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h index c106f9d7851..5a45cf718ec 100644 --- a/source/blender/windowmanager/wm_window.h +++ b/source/blender/windowmanager/wm_window.h @@ -32,7 +32,11 @@ #ifndef __WM_WINDOW_H__ #define __WM_WINDOW_H__ +struct EnumPropertyItem; +struct wmEvent; struct wmOperator; +struct PointerRNA; +struct PropertyRNA; /* *************** internal api ************** */ void wm_ghost_init (bContext *C); @@ -78,9 +82,12 @@ void wm_window_IME_end (wmWindow *win); /* *************** window operators ************** */ int wm_window_close_exec(bContext *C, struct wmOperator *op); -int wm_window_duplicate_exec(bContext *C, struct wmOperator *op); int wm_window_fullscreen_toggle_exec(bContext *C, struct wmOperator *op); +struct EnumPropertyItem *wm_window_new_screen_itemf(bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); +int wm_window_new_exec(bContext *C, struct wmOperator *op); +int wm_window_new_invoke(bContext *C, struct wmOperator *op, const struct wmEvent *event); + /* Initial (unmaximized) size to start with for * systems that can't find it for themselves (X11). * Clamped by real desktop limits */