New Window Operator (to replace Duplicate Window)
authorDalai Felinto <dfelinto@gmail.com>
Mon, 13 Mar 2017 09:43:49 +0000 (10:43 +0100)
committerDalai Felinto <dfelinto@gmail.com>
Mon, 13 Mar 2017 09:45:15 +0000 (10:45 +0100)
A user doesn't want to necessarily create a new Screen only because she
wants a new window.

This patch allows the user to pick the screen to use for the new Window.
If the screen picked is the active one, it duplicates it (as the old
behaviour in Blender).

Patch with contributions and fixes by Julian Eisel (Severin)

Subscribers: venomgfx

Differential Revision: https://developer.blender.org/D2555

release/scripts/presets/keyconfig/3dsmax.py
release/scripts/presets/keyconfig/maya.py
release/scripts/startup/bl_ui/space_info.py
source/blender/editors/interface/interface.c
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/intern/wm_operators.c
source/blender/windowmanager/intern/wm_window.c
source/blender/windowmanager/wm_window.h

index a07a3a52caf1653899f9cb3a34d9a3ccb6f354fb..09c6704df1fea65450015b78eccf4485a35255a4 100644 (file)
@@ -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)
index cf213c1ddbddf82f1e1788df9eb7c81984760b6a..194b9b3c28323fa3f1b5d7b8605c9afde4cff7a2 100644 (file)
@@ -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)
index fcb301307309ac937fc8ccdd200069cc7cdd90af..743832eb51f74eb004b6a701be3b94eca184037d 100644 (file)
@@ -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()
index fe9b3054ea76305eb0dc9edb453c548bdd7f5d60..0b026750900cef117939c81431bf5db2e0793093 100644 (file)
@@ -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;
                        }
                }
index 2809795268dd00233ddbd5609668ad49dc80923a..c8d3c85c5dad7453af6ab6b33701f72bb120b4d8 100644 (file)
@@ -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);
index 3cbcaf2810fa561a3821298489deec8fa2a8bf54..2cf4e16155ca4e874d991bccbc895e5b9b6d9124 100644 (file)
@@ -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);
index e271225e43762932585208b337ef98ebb8d184c1..b6f1b431009f56da16d44eeac22060c9302bb20d 100644 (file)
@@ -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))
index c106f9d785172db8398c56ebedcd15083c4cbf20..5a45cf718ec6b196d5696c518392315e5fdbd021 100644 (file)
 #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 */