Pie Menus C code backend.
authorAntony Riakiotakis <kalast@gmail.com>
Mon, 11 Aug 2014 08:39:59 +0000 (10:39 +0200)
committerAntony Riakiotakis <kalast@gmail.com>
Mon, 11 Aug 2014 09:02:26 +0000 (11:02 +0200)
This commit merges the code in the pie-menu branch.

As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.

Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/

Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments

Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.

Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.

36 files changed:
intern/ghost/intern/GHOST_SystemX11.cpp
intern/ghost/intern/GHOST_SystemX11.h
release/scripts/modules/bpy_types.py
release/scripts/startup/bl_operators/wm.py
release/scripts/startup/bl_ui/space_userpref.py
release/scripts/templates_py/ui_pie_menu.py [new file with mode: 0644]
source/blender/blenkernel/BKE_blender.h
source/blender/editors/include/ED_space_api.h
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface.c
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_intern.h
source/blender/editors/interface/interface_layout.c
source/blender/editors/interface/interface_panel.c
source/blender/editors/interface/interface_regions.c
source/blender/editors/interface/interface_widgets.c
source/blender/editors/interface/resources.c
source/blender/editors/space_api/spacetypes.c
source/blender/editors/space_view3d/view3d_edit.c
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_access.c
source/blender/makesrna/intern/rna_object.c
source/blender/makesrna/intern/rna_space.c
source/blender/makesrna/intern/rna_ui_api.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/makesrna/intern/rna_wm.c
source/blender/makesrna/intern/rna_wm_api.c
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/WM_keymap.h
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/intern/wm_init_exit.c
source/blender/windowmanager/intern/wm_keymap.c
source/blender/windowmanager/intern/wm_operators.c
source/blender/windowmanager/wm_event_system.h
source/blenderplayer/bad_level_call_stubs/stubs.c

index e347661ebc66aebc1e4e93ea5788bd1d4fa284e1..6d948b2135f60ba40dcc1936b434ccf110dabaf5 100644 (file)
@@ -525,6 +525,16 @@ processEvents(
                                continue;
                        }
 #endif
+                       /* when using autorepeat, some keypress events can actually come *after* the
+                        * last keyrelease. The next code takes care of that */
+                       if (xevent.type == KeyRelease) {
+                               m_last_release_keycode = xevent.xkey.keycode;
+                               m_last_release_time = xevent.xkey.time;
+                       }
+                       else if (xevent.type == KeyPress) {
+                               if ((xevent.xkey.keycode == m_last_release_keycode) && ((xevent.xkey.time <= m_last_release_time)))
+                                       continue;
+                       }
 
                        processEvent(&xevent);
                        anyProcessed = true;
index 1f95e4b1ce2f69fd626c20765dbdd5cebd3e6589..a21300e36f03c9deed8dc4d1abb8b3d699d6815e 100644 (file)
@@ -356,6 +356,10 @@ private:
         *  and stop accumulating all events generated before that */
        Time m_last_warp;
 
+       /* detect autorepeat glitch */
+       unsigned int m_last_release_keycode;
+       Time m_last_release_time;
+
        /**
         * Return the ghost window associated with the
         * X11 window xwind
index d26834719154170f41427a85849b70d6a9c1f79e..bc2e9368b71cb1933b31e6a99fa140904dd52ef1 100644 (file)
@@ -140,6 +140,15 @@ class WindowManager(bpy_types.ID):
         finally:
             self.pupmenu_end__internal(popup)
 
+    def popup_menu_pie(self, event, draw_func, title="", icon='NONE'):
+        import bpy
+        pie = self.piemenu_begin__internal(title, icon, event)
+
+        try:
+            draw_func(pie, bpy.context)
+        finally:
+            self.piemenu_end__internal(pie)
+
 
 class _GenericBone:
     """
index 4281c908afd11c78b68414c34ab9d275d3fa33ba..15e482a5e6327e07a88683f18d2eae850e7b2558 100644 (file)
@@ -527,7 +527,33 @@ class WM_OT_context_menu_enum(Operator):
 
         context.window_manager.popup_menu(draw_func=draw_cb, title=prop.name, icon=prop.icon)
 
-        return {'PASS_THROUGH'}
+        return {'FINISHED'}
+
+
+class WM_OT_context_pie_enum(Operator):
+    bl_idname = "wm.context_pie_enum"
+    bl_label = "Context Enum Pie"
+    bl_options = {'UNDO', 'INTERNAL'}
+    data_path = rna_path_prop
+
+    def invoke(self, context, event):
+        data_path = self.data_path
+        value = context_path_validate(context, data_path)
+
+        if value is Ellipsis:
+            return {'PASS_THROUGH'}
+
+        base_path, prop_string = data_path.rsplit(".", 1)
+        value_base = context_path_validate(context, base_path)
+        prop = value_base.bl_rna.properties[prop_string]
+
+        def draw_cb(self, context):
+            layout = self.layout
+            layout.prop(value_base, prop_string, expand=True)
+
+        context.window_manager.popup_menu_pie(draw_func=draw_cb, title=prop.name, icon=prop.icon, event=event)
+
+        return {'FINISHED'}
 
 
 class WM_OT_context_set_id(Operator):
index b325e8c975903d2fda561990a44338bfaf6154db..999e41e17f7ab75c42578542662cadea980e8220 100644 (file)
@@ -216,6 +216,13 @@ class USERPREF_PT_interface(Panel):
         sub.prop(view, "open_toplevel_delay", text="Top Level")
         sub.prop(view, "open_sublevel_delay", text="Sub Level")
 
+        col.separator()
+        col.label(text="Pie Menus:")
+        sub = col.column(align=True)
+        sub.prop(view, "pie_animation_timeout")
+        sub.prop(view, "pie_initial_timeout")
+        sub.prop(view, "pie_menu_radius")
+        sub.prop(view, "pie_menu_threshold")
         col.separator()
         col.separator()
         col.separator()
@@ -681,6 +688,9 @@ class USERPREF_PT_theme(Panel):
             col.label(text="Menu:")
             self._theme_widget_style(col, ui.wcol_menu)
 
+            col.label(text="Pie Menu:")
+            self._theme_widget_style(col, ui.wcol_pie_menu)
+
             col.label(text="Pulldown:")
             self._theme_widget_style(col, ui.wcol_pulldown)
 
diff --git a/release/scripts/templates_py/ui_pie_menu.py b/release/scripts/templates_py/ui_pie_menu.py
new file mode 100644 (file)
index 0000000..87500b6
--- /dev/null
@@ -0,0 +1,32 @@
+import bpy
+from bpy.types import Menu
+
+# spawn an edit mode selection pie (run while object is in edit mode to get a valid output)
+
+
+class VIEW3D_PIE_template(Menu):
+    # label is displayed at the center of the pie menu.
+    bl_label = "Select Mode"
+
+    def draw(self, context):
+        layout = self.layout
+
+        pie = layout.menu_pie()
+        # operator_enum will just spread all available options
+        # for the type enum of the operator on the pie
+        pie.operator_enum("mesh.select_mode", "type")
+
+
+def register():
+    bpy.utils.register_class(VIEW3D_PIE_template)
+
+
+def unregister():
+    bpy.utils.unregister_class(VIEW3D_PIE_template)
+
+
+if __name__ == "__main__":
+    register()
+
+    bpy.ops.wm.call_menu_pie(name="VIEW3D_PIE_template")
+
index 50639ba7ed72ffe58ba7b44406bea24ba842eb4d..ea904983eca8fbcde6825fd4200c93eb95ecc794 100644 (file)
@@ -42,7 +42,7 @@ extern "C" {
  * and keep comment above the defines.
  * Use STRINGIFY() rather than defining with quotes */
 #define BLENDER_VERSION         271
-#define BLENDER_SUBVERSION      3
+#define BLENDER_SUBVERSION      4
 /* 262 was the last editmesh release but it has compatibility code for bmesh data */
 #define BLENDER_MINVERSION      270
 #define BLENDER_MINSUBVERSION   5
index 4fbe01a5fc7811f64eb9f1c62d91d3ab77261b30..d268c578cf20ba813e1deb9fc346a9dc63739770 100644 (file)
@@ -35,6 +35,7 @@ struct ARegionType;
 struct bContext;
 
 void ED_spacetypes_init(void);
+void ED_spacemacros_init(void);
 
 /* the pluginnable API for export to editors */
 
index d7b4f7538108bea22113201f78894c6f0eebc860..c02ab1e4cac522d229312bd716db2dab4d847fec 100644 (file)
@@ -102,6 +102,7 @@ typedef struct uiLayout uiLayout;
 #define UI_EMBOSSN      1   /* Nothing, only icon and/or text */
 #define UI_EMBOSSP      2   /* Pulldown menu style */
 #define UI_EMBOSST      3   /* Table */
+#define UI_EMBOSSR      4   /* Pie Menu */
 
 /* uiBlock->direction */
 #define UI_DIRECTION       (UI_TOP | UI_DOWN | UI_LEFT | UI_RIGHT)
@@ -137,6 +138,7 @@ typedef struct uiLayout uiLayout;
 /* block->flag bits 14-17 are identical to but->drawflag bits */
 
 #define UI_BLOCK_LIST_ITEM   (1 << 19)
+#define UI_BLOCK_RADIAL      (1 << 20)
 
 /* uiPopupBlockHandle->menuretval */
 #define UI_RETURN_CANCEL     (1 << 0)   /* cancel all menus cascading */
@@ -360,6 +362,17 @@ struct uiLayout *uiPupMenuLayout(uiPopupMenu *head);
 void uiPupMenuReports(struct bContext *C, struct ReportList *reports) ATTR_NONNULL();
 bool uiPupMenuInvoke(struct bContext *C, const char *idname, struct ReportList *reports) ATTR_NONNULL(1, 2);
 
+/* Pie menus */
+typedef struct uiPieMenu uiPieMenu;
+
+void uiPieMenuInvoke(struct bContext *C, const char *idname, const struct wmEvent *event);
+void uiPieOperatorEnumInvoke(struct bContext *C, const char *title, const char *opname,
+                             const char *propname, const struct wmEvent *event);
+void uiPieEnumInvoke(struct bContext *C, const char *title, const char *path, const struct wmEvent *event);
+
+struct uiPieMenu *uiPieMenuBegin(struct bContext *C, const char *title, int icon, const struct wmEvent *event) ATTR_NONNULL();
+void uiPieMenuEnd(struct bContext *C, uiPieMenu *pie);
+struct uiLayout *uiPieMenuLayout(struct uiPieMenu *pie);
 /* Popup Blocks
  *
  * Functions used to create popup blocks. These are like popup menus
@@ -417,7 +430,8 @@ typedef enum {
        UI_BLOCK_BOUNDS_TEXT,
        UI_BLOCK_BOUNDS_POPUP_MOUSE,
        UI_BLOCK_BOUNDS_POPUP_MENU,
-       UI_BLOCK_BOUNDS_POPUP_CENTER
+       UI_BLOCK_BOUNDS_POPUP_CENTER,
+       UI_BLOCK_BOUNDS_PIE_CENTER,
 } eBlockBoundsCalc;
 
 void uiBoundsBlock(struct uiBlock *block, int addval);
@@ -705,7 +719,7 @@ void                       UI_panel_category_draw_all(struct ARegion *ar, const
  * as screen/ if ED_KEYMAP_UI is set, or internally in popup functions. */
 
 void UI_add_region_handlers(struct ListBase *handlers);
-void UI_add_popup_handlers(struct bContext *C, struct ListBase *handlers, uiPopupBlockHandle *popup);
+void UI_add_popup_handlers(struct bContext *C, struct ListBase *handlers, uiPopupBlockHandle *popup, const bool accept_dbl_click);
 void UI_remove_popup_handlers(struct ListBase *handlers, uiPopupBlockHandle *popup);
 void UI_remove_popup_handlers_all(struct bContext *C, struct ListBase *handlers);
 
@@ -737,6 +751,7 @@ void UI_exit(void);
 #define UI_LAYOUT_HEADER        1
 #define UI_LAYOUT_MENU          2
 #define UI_LAYOUT_TOOLBAR       3
+#define UI_LAYOUT_PIEMENU       4
 
 #define UI_UNIT_X               ((void)0, U.widget_unit)
 #define UI_UNIT_Y               ((void)0, U.widget_unit)
@@ -827,8 +842,8 @@ uiLayout *uiLayoutListBox(uiLayout *layout, struct uiList *ui_list, struct Point
 uiLayout *uiLayoutAbsolute(uiLayout *layout, int align);
 uiLayout *uiLayoutSplit(uiLayout *layout, float percentage, int align);
 uiLayout *uiLayoutOverlap(uiLayout *layout);
-
 uiBlock *uiLayoutAbsoluteBlock(uiLayout *layout);
+uiLayout *uiLayoutRadial(uiLayout *layout);
 
 /* templates */
 void uiTemplateHeader(uiLayout *layout, struct bContext *C);
index 0a45ffc4c139c0cb0c04393815c4f5debc3ecbbe..73eb5f63aeaf0c6d56c3fec9010c9dafe6b3c65d 100644 (file)
@@ -321,6 +321,20 @@ static void ui_centered_bounds_block(wmWindow *window, uiBlock *block)
        ui_bounds_block(block);
        
 }
+
+static void ui_centered_pie_bounds_block(uiBlock *block)
+{
+       const int xy[2] = {
+           block->pie_data.pie_center_spawned[0],
+           block->pie_data.pie_center_spawned[1]
+       };
+
+       ui_block_translate(block, xy[0], xy[1]);
+
+       /* now recompute bounds and safety */
+       ui_bounds_block(block);
+}
+
 static void ui_popup_bounds_block(wmWindow *window, uiBlock *block,
                                   eBlockBoundsCalc bounds_calc, const int xy[2])
 {
@@ -1062,6 +1076,42 @@ static bool ui_but_event_property_operator_string(const bContext *C, uiBut *but,
        return found;
 }
 
+/* this goes in a seemingly weird pattern:
+ *
+ *     4
+ *  5     6
+ * 1       2
+ *  7     8
+ *     3
+ *
+ * but it's actually quite logical. It's designed to be 'upwards compatible'
+ * for muscle memory so that the menu item locations are fixed and don't move
+ * as new items are added to the menu later on. It also optimises efficiency -
+ * a radial menu is best kept symmetrical, with as large an angle between
+ * items as possible, so that the gestural mouse movements can be fast and inexact.
+
+ * It starts off with two opposite sides for the first two items
+ * then joined by the one below for the third (this way, even with three items,
+ * the menu seems to still be 'in order' reading left to right). Then the fourth is
+ * added to complete the compass directions. From here, it's just a matter of
+ * subdividing the rest of the angles for the last 4 items.
+ *
+ * --Matt 07/2006
+ */
+const char ui_radial_dir_order[8] = {
+    UI_RADIAL_W,  UI_RADIAL_E,  UI_RADIAL_S,  UI_RADIAL_N,
+    UI_RADIAL_NW, UI_RADIAL_NE, UI_RADIAL_SW, UI_RADIAL_SE};
+
+const char  ui_radial_dir_to_numpad[8] = {8, 9, 6, 3, 2, 1, 4, 7};
+const short ui_radial_dir_to_angle_visual[8] = {90, 40, 0, 320, 270, 220, 180, 140};
+const short ui_radial_dir_to_angle[8] =        {90, 45, 0, 315, 270, 225, 180, 135};
+
+static void ui_but_pie_direction_string(uiBut *but, char *buf, int size)
+{
+       BLI_assert(but->pie_dir < ARRAY_SIZE(ui_radial_dir_to_numpad));
+       BLI_snprintf(buf, size, "%d", ui_radial_dir_to_numpad[but->pie_dir]);
+}
+
 static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
 {
        uiBut *but;
@@ -1071,13 +1121,23 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
        if (block->rect.xmin != block->rect.xmax)
                return;
 
-       for (but = block->buttons.first; but; but = but->next) {
-
-               if (ui_but_event_operator_string(C, but, buf, sizeof(buf))) {
-                       ui_but_add_shortcut(but, buf, false);
+       if (block->flag & UI_BLOCK_RADIAL) {
+               for (but = block->buttons.first; but; but = but->next) {
+                       if (but->pie_dir != UI_RADIAL_NONE) {
+                               ui_but_pie_direction_string(but, buf, sizeof(buf));
+                               ui_but_add_shortcut(but, buf, false);
+                       }
                }
-               else if (ui_but_event_property_operator_string(C, but, buf, sizeof(buf))) {
-                       ui_but_add_shortcut(but, buf, false);
+       }
+       else {
+               for (but = block->buttons.first; but; but = but->next) {
+
+                       if (ui_but_event_operator_string(C, but, buf, sizeof(buf))) {
+                               ui_but_add_shortcut(but, buf, false);
+                       }
+                       else if (ui_but_event_property_operator_string(C, but, buf, sizeof(buf))) {
+                               ui_but_add_shortcut(but, buf, false);
+                       }
                }
        }
 }
@@ -1173,6 +1233,9 @@ void uiEndBlock_ex(const bContext *C, uiBlock *block, const int xy[2])
                case UI_BLOCK_BOUNDS_POPUP_CENTER:
                        ui_centered_bounds_block(window, block);
                        break;
+               case UI_BLOCK_BOUNDS_PIE_CENTER:
+                       ui_centered_pie_bounds_block(block);
+                       break;
 
                        /* fallback */
                case UI_BLOCK_BOUNDS_POPUP_MOUSE:
@@ -1244,6 +1307,10 @@ void uiDrawBlock(const bContext *C, uiBlock *block)
        rcti rect;
        int multisample_enabled;
        
+       /* early exit if cancelled */
+       if ((block->flag & UI_BLOCK_RADIAL) && (block->pie_data.flags & UI_PIE_FINISHED))
+               return;
+
        /* get menu region or area region */
        ar = CTX_wm_menu(C);
        if (!ar)
@@ -1279,7 +1346,9 @@ void uiDrawBlock(const bContext *C, uiBlock *block)
        wmOrtho2(-0.01f, ar->winx - 0.01f, -0.01f, ar->winy - 0.01f);
        
        /* back */
-       if (block->flag & UI_BLOCK_LOOP)
+       if (block->flag & UI_BLOCK_RADIAL)
+               ui_draw_pie_center(block);
+       else if (block->flag & UI_BLOCK_LOOP)
                ui_draw_menu_back(&style, block, &rect);
        else if (block->panel)
                ui_draw_aligned_panel(&style, block, &rect, UI_panel_category_is_visible(ar));
@@ -3002,6 +3071,7 @@ static uiBut *ui_def_but(uiBlock *block, int type, int retval, const char *str,
        but->lock = block->lock;
        but->lockstr = block->lockstr;
        but->dt = block->dt;
+       but->pie_dir = UI_RADIAL_NONE;
 
        but->block = block;  /* pointer back, used for frontbuffer status, and picker */
 
@@ -3028,8 +3098,11 @@ static uiBut *ui_def_but(uiBlock *block, int type, int retval, const char *str,
                }
        }
 
-       if ((block->flag & UI_BLOCK_LOOP) ||
-           ELEM(but->type, MENU, TEX, LABEL, BLOCK, BUTM, SEARCH_MENU, PROGRESSBAR, SEARCH_MENU_UNLINK))
+       if (block->flag & UI_BLOCK_RADIAL) {
+               but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT);
+       }
+       else if ((block->flag & UI_BLOCK_LOOP) ||
+                ELEM(but->type, MENU, TEX, LABEL, BLOCK, BUTM, SEARCH_MENU, PROGRESSBAR, SEARCH_MENU_UNLINK))
        {
                but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT);
        }
index 68148d30136a949cd5aead929a55d9f1b19ddde3..33938615d7299eaaf66709527f5e3a037a8e2fef 100644 (file)
@@ -116,6 +116,7 @@ static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEve
 #define BUTTON_TOOLTIP_DELAY        0.500
 #define BUTTON_FLASH_DELAY          0.020
 #define MENU_SCROLL_INTERVAL        0.1
+#define PIE_MENU_INTERVAL           0.01
 #define BUTTON_AUTO_OPEN_THRESH     0.3
 #define BUTTON_MOUSE_TOWARDS_THRESH 1.0
 /* pixels to move the cursor to get out of keyboard navigation */
@@ -1238,7 +1239,7 @@ static bool ui_but_start_drag(bContext *C, uiBut *but, uiHandleButtonData *data,
                        WM_event_add_ui_handler(C, &data->window->modalhandlers,
                                                ui_handler_region_drag_toggle,
                                                ui_handler_region_drag_toggle_remove,
-                                               drag_info);
+                                               drag_info, false);
 
                        CTX_wm_region_set(C, ar_prev);
                }
@@ -6377,6 +6378,43 @@ static bool ui_but_contains_pt(uiBut *but, float mx, float my)
        return BLI_rctf_isect_pt(&but->rect, mx, my);
 }
 
+static void ui_but_pie_dir__internal(RadialDirection dir, float vec[2], const short angles[8])
+{
+       float angle;
+
+       BLI_assert(dir != UI_RADIAL_NONE);
+
+       angle = DEG2RADF((float)angles[dir]);
+       vec[0] = cosf(angle);
+       vec[1] = sinf(angle);
+}
+
+void ui_but_pie_dir_visual(RadialDirection dir, float vec[2])
+{
+       ui_but_pie_dir__internal(dir, vec, ui_radial_dir_to_angle_visual);
+}
+
+void ui_but_pie_dir(RadialDirection dir, float vec[2])
+{
+       ui_but_pie_dir__internal(dir, vec, ui_radial_dir_to_angle);
+}
+
+static bool ui_but_isect_pie_seg(uiBlock *block, uiBut *but)
+{
+       const float angle_range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_4 : M_PI_4 / 2.0;
+       float vec[2];
+
+       if (block->pie_data.flags & UI_PIE_INVALID_DIR)
+               return false;
+
+       ui_but_pie_dir(but->pie_dir, vec);
+
+       if (saacos(dot_v2v2(vec, block->pie_data.pie_dir)) < angle_range)
+               return true;
+
+       return false;
+}
+
 uiBut *ui_but_find_activated(ARegion *ar)
 {
        uiBlock *block;
@@ -6495,6 +6533,7 @@ static bool ui_mouse_inside_region(ARegion *ar, int x, int y)
 
 static bool ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y)
 {
+       uiBlock *block = but->block;
        float mx, my;
        if (!ui_mouse_inside_region(ar, x, y))
                return false;
@@ -6502,10 +6541,16 @@ static bool ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y)
        mx = x;
        my = y;
 
-       ui_window_to_block_fl(ar, but->block, &mx, &my);
+       ui_window_to_block_fl(ar, block, &mx, &my);
 
-       if (!ui_but_contains_pt(but, mx, my))
+       if (but->dt == UI_EMBOSSR) {
+               if (!ui_but_isect_pie_seg(block, but)) {
+                       return false;
+               }
+       }
+       else if (!ui_but_contains_pt(but, mx, my)) {
                return false;
+       }
        
        return true;
 }
@@ -6559,7 +6604,13 @@ static uiBut *ui_but_find_mouse_over_ex(ARegion *ar, const int x, const int y, c
 
                for (but = block->buttons.last; but; but = but->prev) {
                        if (ui_is_but_interactive(but, labeledit)) {
-                               if (ui_but_contains_pt(but, mx, my)) {
+                               if (but->pie_dir != UI_RADIAL_NONE) {
+                                       if (ui_but_isect_pie_seg(block, but)) {
+                                               butover = but;
+                                               break;
+                                       }
+                               }
+                               else if (ui_but_contains_pt(but, mx, my)) {
                                        butover = but;
                                        break;
                                }
@@ -6764,7 +6815,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
        if (!(but->block->handle && but->block->handle->popup)) {
                if (button_modal_state(state)) {
                        if (!button_modal_state(data->state))
-                               WM_event_add_ui_handler(C, &data->window->modalhandlers, ui_handler_region_menu, NULL, data);
+                               WM_event_add_ui_handler(C, &data->window->modalhandlers, ui_handler_region_menu, NULL, data, false);
                }
                else {
                        if (button_modal_state(data->state)) {
@@ -7919,6 +7970,30 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock
        return retval;
 }
 
+void ui_block_calculate_pie_segment(uiBlock *block, const float event_xy[2])
+{
+       float seg1[2];
+       float seg2[2];
+       float len;
+
+       if (block->pie_data.flags & UI_PIE_INITIAL_DIRECTION) {
+               copy_v2_v2(seg1, block->pie_data.pie_center_init);
+       }
+       else {
+               copy_v2_v2(seg1, block->pie_data.pie_center_spawned);
+       }
+
+       sub_v2_v2v2(seg2, event_xy, seg1);
+
+       len = normalize_v2_v2(block->pie_data.pie_dir, seg2);
+
+       /* ten pixels for now, a bit arbitrary */
+       if (len < U.pie_menu_threshold * U.pixelsize)
+               block->pie_data.flags |= UI_PIE_INVALID_DIR;
+       else
+               block->pie_data.flags &= ~UI_PIE_INVALID_DIR;
+}
+
 static int ui_handle_menu_event(
         bContext *C, const wmEvent *event, uiPopupBlockHandle *menu,
         int level, const bool is_parent_inside, const bool is_parent_menu, const bool is_floating)
@@ -8420,10 +8495,311 @@ static int ui_handle_menu_return_submenu(bContext *C, const wmEvent *event, uiPo
                ui_mouse_motion_towards_reinit(menu, &event->x);
        }
 
-       if (menu->menuretval)
+       if (menu->menuretval) {
+               /* pie menus should not close but wait for release instead */
+               if ((block->flag & UI_BLOCK_RADIAL) &&
+                   !(block->pie_data.flags & UI_PIE_CLICK_STYLE))
+               {
+                       menu->menuretval = 0;
+                       block->pie_data.flags |= UI_PIE_FINISHED;
+               }
+
                return WM_UI_HANDLER_CONTINUE;
-       else
+       }
+       else {
+               return WM_UI_HANDLER_BREAK;
+       }
+}
+
+static bool ui_but_pie_menu_supported_apply(uiBut *but)
+{
+       return (but->type != NUMSLI);
+}
+
+static int ui_but_pie_menu_apply(bContext *C, uiPopupBlockHandle *menu, uiBut *but, bool force_close, bool click_style)
+{
+       int retval = WM_UI_HANDLER_BREAK;
+
+       if (but && ui_but_pie_menu_supported_apply(but)) {
+               if (but->type == MENU) {
+                       /* forcing the pie menu to close will not handle menus */
+                       if (!force_close) {
+                               uiBut *active_but = ui_but_find_activated(menu->region);
+
+                               if (active_but) {
+                                       button_activate_exit(C, active_but, active_but->active, false, false);
+                               }
+
+                               button_activate_init(C, menu->region, but, BUTTON_ACTIVATE_OPEN);
+                               return retval;
+                       }
+                       else {
+                               menu->menuretval = UI_RETURN_CANCEL;
+                       }
+               }
+               else {
+                       ui_apply_button(C, but->block, but, but->active, false);
+                       button_activate_exit((bContext *)C, but, but->active, false, true);
+
+                       if (!(click_style || force_close)) {
+                               but->block->pie_data.flags |= UI_PIE_FINISHED;
+                               menu->menuretval = 0;
+                       }
+                       else {
+                               menu->menuretval = UI_RETURN_OK;
+                       }
+               }
+       }
+       else {
+               uiBlock *block = menu->region->uiblocks.first;
+
+               if (!(click_style || force_close)) {
+                       block->pie_data.flags |= UI_PIE_FINISHED;
+               }
+               else {
+                       menu->menuretval = UI_RETURN_CANCEL;
+               }
+
+               ED_region_tag_redraw(menu->region);
+       }
+
+       return retval;
+}
+
+static uiBut *ui_block_pie_dir_activate(uiBlock *block, const wmEvent *event, RadialDirection dir)
+{
+       uiBut *but;
+
+       if ((block->flag & UI_BLOCK_NUMSELECT) && event->val == KM_PRESS) {
+               for (but = block->buttons.first; but; but = but->next) {
+                       if (but->pie_dir == dir && !ELEM(but->type, SEPR, SEPRLINE)) {
+                               return but;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+static int ui_but_pie_button_activate(bContext *C, uiBut *but, uiPopupBlockHandle *menu, bool is_click_style)
+{
+       uiBut *active_but;
+
+       if (but == NULL)
+               return WM_UI_HANDLER_BREAK;
+
+       active_but = ui_but_find_activated(menu->region);
+
+       if (active_but)
+               button_activate_exit(C, active_but, active_but->active, false, false);
+
+       button_activate_init(C, menu->region, but, BUTTON_ACTIVATE_OVER);
+       return ui_but_pie_menu_apply(C, menu, but, false, is_click_style);
+}
+
+static int ui_handler_pie(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu)
+{
+       ARegion *ar;
+       uiBlock *block;
+       uiBut *but;
+       float event_xy[2];
+       double duration;
+       bool is_click_style;
+
+       /* we block all events, this is modal interaction, except for drop events which is described below */
+       int retval = WM_UI_HANDLER_BREAK;
+
+       if (event->type == EVT_DROP) {
+               /* may want to leave this here for later if we support pie ovens */
+
+               retval = WM_UI_HANDLER_CONTINUE;
+       }
+
+       ar = menu->region;
+       block = ar->uiblocks.first;
+
+       is_click_style = (block->pie_data.flags & UI_PIE_CLICK_STYLE);
+
+       if (menu->scrolltimer == NULL) {
+               menu->scrolltimer =
+                   WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, PIE_MENU_INTERVAL);
+               menu->scrolltimer->duration = 0.0;
+       }
+
+       duration = menu->scrolltimer->duration;
+
+       if (event->type == TIMER) {
+               if (event->customdata == menu->scrolltimer) {
+                       /* deactivate initial direction after a while */
+                       if (duration > 0.01 * U.pie_initial_timeout) {
+                               block->pie_data.flags &= ~UI_PIE_INITIAL_DIRECTION;
+                       }
+
+                       /* handle animation */
+                       if (!(block->pie_data.flags & UI_PIE_ANIMATION_FINISHED)) {
+                               uiBut *but;
+                               double final_time = 0.01 * U.pie_animation_timeout;
+                               float fac = duration / final_time;
+                               float pie_radius = U.pie_menu_radius * UI_DPI_FAC;
+
+                               if (fac > 1.0f) {
+                                       fac = 1.0f;
+                                       block->pie_data.flags |= UI_PIE_ANIMATION_FINISHED;
+                               }
+
+                               pie_radius *= fac;
+
+                               for (but = block->buttons.first; but; but = but->next) {
+                                       if (but->pie_dir != UI_RADIAL_NONE) {
+                                               float dir[2];
+
+                                               ui_but_pie_dir_visual(but->pie_dir, dir);
+
+                                               mul_v2_fl(dir, pie_radius );
+                                               add_v2_v2(dir, block->pie_data.pie_center_spawned);
+                                               BLI_rctf_recenter(&but->rect, dir[0], dir[1]);
+                                       }
+                               }
+                               block->pie_data.alphafac = fac;
+
+                               ED_region_tag_redraw(ar);
+                       }
+               }
+       }
+
+       event_xy[0] = event->x;
+       event_xy[1] = event->y;
+
+       ui_window_to_block_fl(ar, block, &event_xy[0], &event_xy[1]);
+
+       ui_block_calculate_pie_segment(block, event_xy);
+
+       if (block->pie_data.flags & UI_PIE_FINISHED) {
+               if ((event->type == block->pie_data.event && event->val == KM_RELEASE) ||
+                   ((event->type == RIGHTMOUSE || event->type == ESCKEY) && (event->val == KM_PRESS)))
+               {
+                       menu->menuretval = UI_RETURN_OK;
+               }
+
+               ED_region_tag_redraw(ar);
                return WM_UI_HANDLER_BREAK;
+       }
+
+       if (event->type == block->pie_data.event) {
+               if (event->val != KM_RELEASE) {
+                       ui_handle_menu_button(C, event, menu);
+
+                       /* why redraw here? It's simple, we are getting many double click events here.
+                        * Those operate like mouse move events almost */
+                       ED_region_tag_redraw(ar);
+               }
+               else {
+                       /* distance from initial point */
+                       if (len_squared_v2v2(event_xy, block->pie_data.pie_center_init) < PIE_CLICK_THRESHOLD_SQ) {
+                               block->pie_data.flags |= UI_PIE_CLICK_STYLE;
+                       }
+                       else if (!is_click_style) {
+                               uiBut *but = ui_but_find_activated(menu->region);
+
+                               retval = ui_but_pie_menu_apply(C, menu, but, true, is_click_style);
+                       }
+               }
+       }
+       else {
+               /* direction from numpad */
+               RadialDirection num_dir = UI_RADIAL_NONE;
+
+               switch (event->type) {
+                       case MOUSEMOVE:
+                               /* mouse move should always refresh the area for pie menus */
+                               ui_handle_menu_button(C, event, menu);
+                               ED_region_tag_redraw(ar);
+                               break;
+
+                       case LEFTMOUSE:
+                               if (event->val == KM_PRESS) {
+                                       uiBut *but = ui_but_find_activated(menu->region);
+                                       retval = ui_but_pie_menu_apply(C, menu, but, false, is_click_style);
+                               }
+                               break;
+
+                       case ESCKEY:
+                       case RIGHTMOUSE:
+                               if (!is_click_style) {
+                                       block->pie_data.flags |= UI_PIE_FINISHED;
+                                       menu->menuretval = 0;
+                                       ED_region_tag_redraw(ar);
+                               }
+                               else
+                                       menu->menuretval = UI_RETURN_CANCEL;
+                               break;
+
+                       case AKEY:
+                       case BKEY:
+                       case CKEY:
+                       case DKEY:
+                       case EKEY:
+                       case FKEY:
+                       case GKEY:
+                       case HKEY:
+                       case IKEY:
+                       case JKEY:
+                       case KKEY:
+                       case LKEY:
+                       case MKEY:
+                       case NKEY:
+                       case OKEY:
+                       case PKEY:
+                       case QKEY:
+                       case RKEY:
+                       case SKEY:
+                       case TKEY:
+                       case UKEY:
+                       case VKEY:
+                       case WKEY:
+                       case XKEY:
+                       case YKEY:
+                       case ZKEY:
+                       {
+                               if ((event->val  == KM_PRESS || event->val == KM_DBL_CLICK) &&
+                                   (event->shift == 0) &&
+                                   (event->ctrl  == 0) &&
+                                   (event->oskey == 0))
+                               {
+                                       for (but = block->buttons.first; but; but = but->next) {
+                                               if (but->menu_key == event->type) {
+                                                       ui_but_pie_button_activate(C, but, menu, is_click_style);
+                                               }
+                                       }
+                               }
+                               break;
+                       }
+
+#define CASE_NUM_TO_DIR(n, d) \
+                       case (ZEROKEY + n): case (PAD0 + n): \
+                               { if (num_dir == UI_RADIAL_NONE) num_dir = d; } (void)0
+
+                       CASE_NUM_TO_DIR(1, UI_RADIAL_SW);
+                       CASE_NUM_TO_DIR(2, UI_RADIAL_S);
+                       CASE_NUM_TO_DIR(3, UI_RADIAL_SE);
+                       CASE_NUM_TO_DIR(4, UI_RADIAL_W);
+                       CASE_NUM_TO_DIR(6, UI_RADIAL_E);
+                       CASE_NUM_TO_DIR(7, UI_RADIAL_NW);
+                       CASE_NUM_TO_DIR(8, UI_RADIAL_N);
+                       CASE_NUM_TO_DIR(9, UI_RADIAL_NE);
+                       {
+                               but = ui_block_pie_dir_activate(block, event, num_dir);
+                               retval = ui_but_pie_button_activate(C, but, menu, is_click_style);
+                               break;
+                       }
+#undef CASE_NUM_TO_DIR
+                       default:
+                               retval = ui_handle_menu_button(C, event, menu);
+                               break;
+               }
+       }
+
+       return retval;
 }
 
 static int ui_handle_menus_recursive(
@@ -8445,17 +8821,21 @@ static int ui_handle_menus_recursive(
                uiBlock *block = menu->region->uiblocks.first;
                const bool is_menu = ui_block_is_menu(block);
                bool inside = false;
+               /* root pie menus accept the key that spawned them as double click to improve responsiveness */
+               bool do_recursion = (!(block->flag & UI_BLOCK_RADIAL) || event->type != block->pie_data.event);
 
-               if (is_parent_inside == false) {
-                       int mx, my;
+               if (do_recursion) {
+                       if (is_parent_inside == false) {
+                               int mx, my;
 
-                       mx = event->x;
-                       my = event->y;
-                       ui_window_to_block(menu->region, block, &mx, &my);
-                       inside = BLI_rctf_isect_pt(&block->rect, mx, my);
-               }
+                               mx = event->x;
+                               my = event->y;
+                               ui_window_to_block(menu->region, block, &mx, &my);
+                               inside = BLI_rctf_isect_pt(&block->rect, mx, my);
+                       }
 
-               retval = ui_handle_menus_recursive(C, event, submenu, level + 1, is_parent_inside || inside, is_menu, false);
+                       retval = ui_handle_menus_recursive(C, event, submenu, level + 1, is_parent_inside || inside, is_menu, false);
+               }
        }
 
        /* now handle events for our own menu */
@@ -8488,7 +8868,12 @@ static int ui_handle_menus_recursive(
                        }
                }
                else {
-                       retval = ui_handle_menu_event(C, event, menu, level, is_parent_inside, is_parent_menu, is_floating);
+                       uiBlock *block = menu->region->uiblocks.first;
+
+                       if (block->flag & UI_BLOCK_RADIAL)
+                               retval = ui_handler_pie(C, event, menu);
+                       else if (event->type == LEFTMOUSE || event->val != KM_DBL_CLICK)
+                               retval = ui_handle_menu_event(C, event, menu, level, is_parent_inside, is_parent_menu, is_floating);
                }
        }
 
@@ -8703,12 +9088,12 @@ static void ui_handler_remove_popup(bContext *C, void *userdata)
 void UI_add_region_handlers(ListBase *handlers)
 {
        WM_event_remove_ui_handler(handlers, ui_handler_region, ui_handler_remove_region, NULL, false);
-       WM_event_add_ui_handler(NULL, handlers, ui_handler_region, ui_handler_remove_region, NULL);
+       WM_event_add_ui_handler(NULL, handlers, ui_handler_region, ui_handler_remove_region, NULL, false);
 }
 
-void UI_add_popup_handlers(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup)
+void UI_add_popup_handlers(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup, const bool accept_dbl_click)
 {
-       WM_event_add_ui_handler(C, handlers, ui_handler_popup, ui_handler_remove_popup, popup);
+       WM_event_add_ui_handler(C, handlers, ui_handler_popup, ui_handler_remove_popup, popup, accept_dbl_click);
 }
 
 void UI_remove_popup_handlers(ListBase *handlers, uiPopupBlockHandle *popup)
index 7d03aaea6b3a9532faa1de6e8831662d1059da5c..d3ff1c7063f1f16b6863f50c492518703226e11f 100644 (file)
@@ -88,6 +88,7 @@ typedef enum {
        
        UI_WTYPE_PULLDOWN,
        UI_WTYPE_MENU_ITEM,
+       UI_WTYPE_MENU_ITEM_RADIAL,
        UI_WTYPE_MENU_BACK,
 
        /* specials */
@@ -121,6 +122,24 @@ enum {
        /* warn: rest of uiBut->flag in UI_interface.h */
 };
 
+/* but->pie_dir */
+typedef enum RadialDirection {
+       UI_RADIAL_NONE  = -1,
+       UI_RADIAL_N     =  0,
+       UI_RADIAL_NE    =  1,
+       UI_RADIAL_E     =  2,
+       UI_RADIAL_SE    =  3,
+       UI_RADIAL_S     =  4,
+       UI_RADIAL_SW    =  5,
+       UI_RADIAL_W     =  6,
+       UI_RADIAL_NW    =  7,
+} RadialDirection;
+
+extern const char  ui_radial_dir_order[8];
+extern const char  ui_radial_dir_to_numpad[8];
+extern const short ui_radial_dir_to_angle_visual[8];
+extern const short ui_radial_dir_to_angle[8];
+
 /* internal panel drawing defines */
 #define PNL_GRID    (UI_UNIT_Y / 5) /* 4 default */
 #define PNL_HEADER  (UI_UNIT_Y + 4) /* 24 default */
@@ -144,6 +163,19 @@ enum {
 /* split numbuts by ':' and align l/r */
 #define USE_NUMBUTS_LR_ALIGN
 
+/* PieMenuData->flags */
+enum {
+       UI_PIE_DEGREES_RANGE_LARGE  = (1 << 0),  /* pie menu item collision is detected at 90 degrees */
+       UI_PIE_INITIAL_DIRECTION    = (1 << 1),  /* use initial center of pie menu to calculate direction */
+       UI_PIE_3_ITEMS              = (1 << 2),  /* pie menu has only 3 items, careful when centering */
+       UI_PIE_INVALID_DIR          = (1 << 3),  /* mouse not far enough from center position  */
+       UI_PIE_FINISHED             = (1 << 4),  /* pie menu finished but we still wait for a release event  */
+       UI_PIE_CLICK_STYLE          = (1 << 5),  /* pie menu changed to click style, click to confirm  */
+       UI_PIE_ANIMATION_FINISHED   = (1 << 6),  /* pie animation finished, do not calculate any more motio  */
+};
+
+#define PIE_CLICK_THRESHOLD_SQ 50.0f
+
 typedef struct uiLinkLine {  /* only for draw/edit */
        struct uiLinkLine *next, *prev;
        struct uiBut *from, *to;
@@ -227,6 +259,7 @@ struct uiBut {
        BIFIconID icon;
        bool lock;
        char dt; /* drawtype: UI_EMBOSS, UI_EMBOSSN ... etc, copied from the block */
+       signed char pie_dir; /* direction in a pie menu, used for collision detection (RadialDirection) */
        char changed; /* could be made into a single flag */
        unsigned char unit_type; /* so buttons can support unit systems which are not RNA */
        short modifier_key;
@@ -274,6 +307,15 @@ struct uiBut {
        uiBlock *block;
 };
 
+struct PieMenuData {
+       float pie_dir[2];
+       float pie_center_init[2];
+       float pie_center_spawned[2];
+       int flags;
+       int event; /* initial event used to fire the pie menu, store here so we can query for release */
+       float alphafac;
+};
+
 struct uiBlock {
        uiBlock *next, *prev;
 
@@ -356,6 +398,7 @@ struct uiBlock {
        char display_device[64]; /* display device name used to display this block,
                                  * used by color widgets to transform colors from/to scene linear
                                  */
+       struct PieMenuData pie_data;
 };
 
 typedef struct uiSafetyRct {
@@ -561,6 +604,9 @@ extern int ui_button_open_menu_direction(uiBut *but);
 extern void ui_button_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but, const bool restore);
 extern uiBut *ui_but_find_activated(struct ARegion *ar);
 bool ui_but_is_editable(const uiBut *but);
+void ui_but_pie_dir_visual(RadialDirection dir, float vec[2]);
+void ui_but_pie_dir(RadialDirection dir, float vec[2]);
+void ui_block_calculate_pie_segment(struct uiBlock *block, const float event_xy[2]);
 
 void ui_button_clipboard_free(void);
 void ui_panel_menu(struct bContext *C, ARegion *ar, Panel *pa);
@@ -571,6 +617,7 @@ uiBut *ui_but_find_new(uiBlock *block_old, const uiBut *but_new);
 void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3);
 void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy, float rad, bool use_alpha);
 void ui_draw_menu_back(struct uiStyle *style, uiBlock *block, rcti *rect);
+void ui_draw_pie_center(uiBlock *block);
 uiWidgetColors *ui_tooltip_get_theme(void);
 void ui_draw_tooltip_background(uiStyle *UNUSED(style), uiBlock *block, rcti *rect);
 void ui_draw_search_back(struct uiStyle *style, uiBlock *block, rcti *rect);
index 645eb6070315e847f704f1b8d8524dd7bdff5de6..a2d8ce06e5f66d0714cab2e05c76a13d78eb0ab1 100644 (file)
@@ -106,6 +106,7 @@ typedef enum uiItemType {
        ITEM_LAYOUT_ABSOLUTE,
        ITEM_LAYOUT_SPLIT,
        ITEM_LAYOUT_OVERLAP,
+       ITEM_LAYOUT_RADIAL,
 
        ITEM_LAYOUT_ROOT
 #if 0
@@ -218,7 +219,9 @@ static int ui_item_fit(int item, int pos, int all, int available, int last, int
 
 static int ui_layout_vary_direction(uiLayout *layout)
 {
-       return (layout->root->type == UI_LAYOUT_HEADER || layout->alignment != UI_LAYOUT_ALIGN_EXPAND) ? UI_ITEM_VARY_X : UI_ITEM_VARY_Y;
+       return ((ELEM(layout->root->type, UI_LAYOUT_HEADER, UI_LAYOUT_PIEMENU) ||
+               (layout->alignment != UI_LAYOUT_ALIGN_EXPAND)) ?
+               UI_ITEM_VARY_X : UI_ITEM_VARY_Y);
 }
 
 /* estimated size of text + icon */
@@ -553,15 +556,24 @@ static void ui_item_enum_expand(uiLayout *layout, uiBlock *block, PointerRNA *pt
         */
 
        uiBut *but;
+       uiLayout *layout_radial = NULL;
        EnumPropertyItem *item, *item_array;
        const char *name;
        int itemw, icon, value;
        bool free;
+       bool radial = (layout->root->type == UI_LAYOUT_PIEMENU);
 
-       RNA_property_enum_items_gettexted(block->evil_C, ptr, prop, &item_array, NULL, &free);
+       if (radial)
+               RNA_property_enum_items_gettexted_all(block->evil_C, ptr, prop, &item_array, NULL, &free);
+       else
+               RNA_property_enum_items_gettexted(block->evil_C, ptr, prop, &item_array, NULL, &free);
 
        /* we dont want nested rows, cols in menus */
-       if (layout->root->type != UI_LAYOUT_MENU) {
+       if (radial) {
+               layout_radial = uiLayoutRadial(layout);
+               uiBlockSetCurLayout(block, layout_radial);
+       }
+       else if (layout->root->type != UI_LAYOUT_MENU) {
                uiBlockSetCurLayout(block, ui_item_local_sublayout(layout, layout, 1));
        }
        else {
@@ -569,8 +581,11 @@ static void ui_item_enum_expand(uiLayout *layout, uiBlock *block, PointerRNA *pt
        }
 
        for (item = item_array; item->identifier; item++) {
-               if (!item->identifier[0])
+               if (!item->identifier[0]) {
+                       if (radial)
+                               uiItemS(layout_radial);
                        continue;
+               }
 
                name = (!uiname || uiname[0]) ? item->name : "";
                icon = item->icon;
@@ -869,6 +884,8 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname
        PointerRNA ptr;
        PropertyRNA *prop;
        uiBlock *block = layout->root->block;
+       const bool radial = (layout->item.type == ITEM_LAYOUT_RADIAL) ||
+                            ((layout->item.type == ITEM_LAYOUT_ROOT) && (layout->root->type == UI_LAYOUT_PIEMENU));
 
        if (!ot || !ot->srna) {
                ui_item_disabled(layout, opname);
@@ -887,10 +904,24 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname
        if (prop && RNA_property_type(prop) == PROP_ENUM) {
                EnumPropertyItem *item, *item_array = NULL;
                bool free;
-               uiLayout *split = uiLayoutSplit(layout, 0.0f, false);
-               uiLayout *column = uiLayoutColumn(split, layout->align);
+               uiLayout *split;
+               uiLayout *target;
+
+               if (radial) {
+                       target = uiLayoutRadial(layout);
+               }
+               else {
+                       split = uiLayoutSplit(layout, 0.0f, false);
+                       target = uiLayoutColumn(split, layout->align);
+               }
+
+               if (radial) {
+                       RNA_property_enum_items_gettexted_all(block->evil_C, &ptr, prop, &item_array, NULL, &free);
+               }
+               else {
+                       RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, NULL, &free);
+               }
 
-               RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, NULL, &free);
                for (item = item_array; item->identifier; item++) {
                        if (item->identifier[0]) {
                                PointerRNA tptr;
@@ -905,20 +936,24 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname
                                }
                                RNA_property_enum_set(&tptr, prop, item->value);
 
-                               uiItemFullO_ptr(column, ot, item->name, item->icon, tptr.data, context, flag);
+                               uiItemFullO_ptr(target, ot, item->name, item->icon, tptr.data, context, flag);
+
                                ui_but_tip_from_enum_item(block->buttons.last, item);
                        }
                        else {
                                if (item->name) {
                                        uiBut *but;
-                                       if (item != item_array) {
-                                               column = uiLayoutColumn(split, layout->align);
+
+                                       if (item != item_array && !radial) {
+                                               target = uiLayoutColumn(split, layout->align);
+
                                                /* inconsistent, but menus with labels do not look good flipped */
                                                block->flag |= UI_BLOCK_NO_FLIP;
                                        }
 
-                                       if (item->icon) {
-                                               uiItemL(column, item->name, item->icon);
+                                       if (item->icon || radial) {
+                                               uiItemL(target, item->name, item->icon);
+
                                                but = block->buttons.last;
                                        }
                                        else {
@@ -928,8 +963,14 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname
                                        }
                                        ui_but_tip_from_enum_item(but, item);
                                }
-                               else {  /* XXX bug here, colums draw bottom item badly */
-                                       uiItemS(column);
+                               else {
+                                       if (radial) {
+                                               uiItemS(target);
+                                       }
+                                       else {
+                                               /* XXX bug here, colums draw bottom item badly */
+                                               uiItemS(target);
+                                       }
                                }
                        }
                }
@@ -2072,16 +2113,135 @@ static void ui_litem_layout_column(uiLayout *litem)
        litem->y = y;
 }
 
+/* calculates the angle of a specified button in a radial menu,
+ * stores a float vector in unit circle */
+static RadialDirection ui_get_radialbut_vec(float vec[2], short itemnum)
+{
+       RadialDirection dir;
+       BLI_assert(itemnum < 8);
+
+       dir = ui_radial_dir_order[itemnum];
+       ui_but_pie_dir_visual(dir, vec);
+
+       return dir;
+}
+
+static bool ui_item_is_radial_displayable(uiItem *item)
+{
+
+       if ((item->type == ITEM_BUTTON) && (((uiButtonItem *)item)->but->type == LABEL))
+               return false;
+
+       return true;
+}
+
+static bool ui_item_is_radial_drawable(uiButtonItem *bitem)
+{
+
+       if (ELEM(bitem->but->type, SEPR, SEPRLINE))
+               return false;
+
+       return true;
+}
+
+static void ui_litem_layout_radial(uiLayout *litem)
+{
+       uiItem *item;
+       int itemh, itemw, x, y;
+       int itemnum = 0;
+       int totitems = 0;
+
+       int minx, miny, maxx, maxy;
+       /* For the radial layout we will use Matt Ebb's design
+        * for radiation, see http://mattebb.com/weblog/radiation/
+        * also the old code at http://developer.blender.org/T5103
+        */
+
+       int pie_radius = U.pie_menu_radius * UI_DPI_FAC;
+
+       x = litem->x;
+       y = litem->y;
+
+       minx = x, miny = y, maxx = x, maxy = y;
+
+       /* first count total items */
+       for (item = litem->items.first; item; item = item->next)
+               totitems++;
+
+       if (totitems < 5)
+               litem->root->block->pie_data.flags |= UI_PIE_DEGREES_RANGE_LARGE;
+
+       if (totitems == 3)
+               litem->root->block->pie_data.flags |= UI_PIE_3_ITEMS;
+
+       for (item = litem->items.first; item; item = item->next) {
+               /* not all button types are drawn in a radial menu, do filtering here */
+               if (ui_item_is_radial_displayable(item)) {
+                       RadialDirection dir;
+                       float vec[2];
+
+                       dir = ui_get_radialbut_vec(vec, itemnum);
+
+                       itemnum++;
+
+                       if (item->type == ITEM_BUTTON) {
+                               uiButtonItem *bitem = (uiButtonItem *) item;
+
+                               bitem->but->pie_dir = dir;
+                               /* scale the buttons */
+                               bitem->but->rect.ymax *= 1.5f;
+                               /* add a little bit more here to include number */
+                               bitem->but->rect.xmax += 1.5f * UI_UNIT_X;
+                               /* enable drawing as pie item if supported by widget */
+                               if (ui_item_is_radial_drawable(bitem))
+                                       bitem->but->dt = UI_EMBOSSR;
+                       }
+
+                       ui_item_size(item, &itemw, &itemh);
+
+                       ui_item_position(item, x + vec[0] * pie_radius - itemw / 2, y + vec[1] * pie_radius - itemh / 2, itemw, itemh);
+
+                       minx = min_ii(minx, x + vec[0] * pie_radius - itemw / 2);
+                       maxx = max_ii(maxx, x + vec[0] * pie_radius + itemw / 2);
+                       miny = min_ii(miny, y + vec[1] * pie_radius - itemh / 2);
+                       maxy = max_ii(maxy, y + vec[1] * pie_radius + itemh / 2);
+               }
+       }
+
+       litem->x = minx;
+       litem->y = miny;
+       litem->w = maxx - minx;
+       litem->h = maxy - miny;
+}
+
 /* root layout */
 static void ui_litem_estimate_root(uiLayout *UNUSED(litem))
 {
        /* nothing to do */
 }
 
+static void ui_litem_layout_root_radial(uiLayout *litem)
+{
+       /* first item is pie menu title, align on center of menu */
+       uiItem *item = litem->items.first;
+
+       if (item->type == ITEM_BUTTON) {
+               int itemh, itemw, x, y;
+               x = litem->x;
+               y = litem->y;
+
+               ui_item_size(item, &itemw, &itemh);
+
+               ui_item_position(item, x - itemw / 2, y + 2 * UI_UNIT_Y, itemw, itemh);
+       }
+}
+
 static void ui_litem_layout_root(uiLayout *litem)
 {
        if (litem->root->type == UI_LAYOUT_HEADER)
                ui_litem_layout_row(litem);
+       else if (litem->root->type == UI_LAYOUT_PIEMENU)
+               ui_litem_layout_root_radial(litem);
        else
                ui_litem_layout_column(litem);
 }
@@ -2497,6 +2657,40 @@ static uiLayoutItemBx *ui_layout_box(uiLayout *layout, int type)
        return box;
 }
 
+uiLayout *uiLayoutRadial(uiLayout *layout)
+{
+       uiLayout *litem;
+       uiItem *item;
+
+       /* radial layouts are only valid for radial menus */
+       if (layout->root->type != UI_LAYOUT_PIEMENU)
+               return ui_item_local_sublayout(layout, layout, 0);
+
+       /* only one radial wheel per root layout is allowed, so check and return that, if it exists */
+       for (item = layout->root->layout->items.first; item; item = item->next) {
+               litem = (uiLayout *)item;
+               if (litem->item.type == ITEM_LAYOUT_RADIAL) {
+                       uiBlockSetCurLayout(layout->root->block, litem);
+                       return litem;
+               }
+       }
+
+       litem = MEM_callocN(sizeof(uiLayout), "uiLayoutRadial");
+       litem->item.type = ITEM_LAYOUT_RADIAL;
+       litem->root = layout->root;
+       litem->active = true;
+       litem->enabled = true;
+       litem->context = layout->context;
+       litem->redalert = layout->redalert;
+       litem->w = layout->w;
+       BLI_addtail(&layout->root->layout->items, litem);
+
+       uiBlockSetCurLayout(layout->root->block, litem);
+
+       return litem;
+}
+
+
 uiLayout *uiLayoutBox(uiLayout *layout)
 {
        return (uiLayout *)ui_layout_box(layout, ROUNDBOX);
@@ -2843,6 +3037,9 @@ static void ui_item_layout(uiItem *item)
                        case ITEM_LAYOUT_OVERLAP:
                                ui_litem_layout_overlap(litem);
                                break;
+                       case ITEM_LAYOUT_RADIAL:
+                               ui_litem_layout_radial(litem);
+                               break;
                        default:
                                break;
                }
@@ -2916,7 +3113,7 @@ uiLayout *uiBlockLayout(uiBlock *block, int dir, int type, int x, int y, int siz
        layout->enabled = 1;
        layout->context = NULL;
 
-       if (type == UI_LAYOUT_MENU)
+       if (type == UI_LAYOUT_MENU || type == UI_LAYOUT_PIEMENU)
                layout->space = 0;
 
        if (dir == UI_LAYOUT_HORIZONTAL) {
index d0909e9413c1cfa6ecb08b75415c6bf230401544..78f67beacff4626679a3abf4cee9ee9127f0d7c8 100644 (file)
@@ -1912,7 +1912,7 @@ static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelStat
                        data = MEM_callocN(sizeof(uiHandlePanelData), "uiHandlePanelData");
                        pa->activedata = data;
 
-                       WM_event_add_ui_handler(C, &win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, pa);
+                       WM_event_add_ui_handler(C, &win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, pa, false);
                }
 
                if (ELEM(state, PANEL_STATE_ANIMATION, PANEL_STATE_DRAG))
index 3629c72ce49f9cbf969186a667a3845c87e4ceac..084b0c0ac67400f17612feb41ee963e3dbb0029b 100644 (file)
@@ -43,6 +43,8 @@
 #include "BLI_utildefines.h"
 #include "BLI_ghash.h"
 
+#include "PIL_time.h"
+
 #include "BKE_context.h"
 #include "BKE_screen.h"
 #include "BKE_report.h"
@@ -1704,18 +1706,69 @@ uiBlock *ui_popup_block_refresh(
                BLI_addhead(&block->saferct, saferct);
        }
 
-       /* clip block with window boundary */
-       ui_popup_block_clip(window, block);
-       
-       /* the block and buttons were positioned in window space as in 2.4x, now
-        * these menu blocks are regions so we bring it back to region space.
-        * additionally we add some padding for the menu shadow or rounded menus */
-       ar->winrct.xmin = block->rect.xmin - width;
-       ar->winrct.xmax = block->rect.xmax + width;
-       ar->winrct.ymin = block->rect.ymin - width;
-       ar->winrct.ymax = block->rect.ymax + MENU_TOP;
-       
-       ui_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin);
+       if (block->flag & UI_BLOCK_RADIAL) {
+               uiBut *but;
+               int win_width = UI_SCREEN_MARGIN;
+               int winx, winy;
+
+               int x_offset = 0, y_offset = 0;
+
+               winx = WM_window_pixels_x(window);
+               winy = WM_window_pixels_y(window);
+
+               copy_v2_v2(block->pie_data.pie_center_init, block->pie_data.pie_center_spawned);
+
+               /* only try translation if area is large enough */
+               if (BLI_rctf_size_x(&block->rect) < winx - (2.0f * win_width)) {
+                       if (block->rect.xmin < win_width )   x_offset += win_width - block->rect.xmin;
+                       if (block->rect.xmax > winx - win_width) x_offset += winx - win_width - block->rect.xmax;
+               }
+
+               if (BLI_rctf_size_y(&block->rect) < winy - (2.0f * win_width)) {
+                       if (block->rect.ymin < win_width )   y_offset += win_width - block->rect.ymin;
+                       if (block->rect.ymax > winy - win_width) y_offset += winy - win_width - block->rect.ymax;
+               }
+               /* if we are offsetting set up initial data for timeout functionality */
+
+               if ((x_offset != 0) || (y_offset != 0)) {
+                       block->pie_data.pie_center_spawned[0] += x_offset;
+                       block->pie_data.pie_center_spawned[1] += y_offset;
+
+                       ui_block_translate(block, x_offset, y_offset);
+
+                       if (U.pie_initial_timeout > 0)
+                               block->pie_data.flags |= UI_PIE_INITIAL_DIRECTION;
+               }
+
+               ar->winrct.xmin = 0;
+               ar->winrct.xmax = winx;
+               ar->winrct.ymin = 0;
+               ar->winrct.ymax = winy;
+
+               ui_block_calculate_pie_segment(block, block->pie_data.pie_center_init);
+
+               /* lastly set the buttons at the center of the pie menu, ready for animation */
+               if (U.pie_animation_timeout > 0) {
+                       for (but = block->buttons.first; but; but = but->next) {
+                               if (but->pie_dir != UI_RADIAL_NONE) {
+                                       BLI_rctf_recenter(&but->rect, UNPACK2(block->pie_data.pie_center_spawned));
+                               }
+                       }
+               }
+       }
+       else {
+               /* clip block with window boundary */
+               ui_popup_block_clip(window, block);
+               /* the block and buttons were positioned in window space as in 2.4x, now
+                * these menu blocks are regions so we bring it back to region space.
+                * additionally we add some padding for the menu shadow or rounded menus */
+               ar->winrct.xmin = block->rect.xmin - width;
+               ar->winrct.xmax = block->rect.xmax + width;
+               ar->winrct.ymin = block->rect.ymin - width;
+               ar->winrct.ymax = block->rect.ymax + MENU_TOP;
+
+               ui_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin);
+       }
 
        if (block_old) {
                block->oldblock = block_old;
@@ -2353,6 +2406,12 @@ struct uiPopupMenu {
        void *menu_arg;
 };
 
+struct uiPieMenu {
+       uiBlock *block_radial; /* radial block of the pie menu (more could be added later) */
+       uiLayout *layout;
+       int mx, my;
+};
+
 static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
 {
        uiBlock *block;
@@ -2526,7 +2585,7 @@ uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut
        if (!but) {
                handle->popup = true;
 
-               UI_add_popup_handlers(C, &window->modalhandlers, handle);
+               UI_add_popup_handlers(C, &window->modalhandlers, handle, false);
                WM_event_add_mousemove(C);
        }
        
@@ -2588,7 +2647,7 @@ void uiPupMenuEnd(bContext *C, uiPopupMenu *pup)
        menu = ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup);
        menu->popup = true;
        
-       UI_add_popup_handlers(C, &window->modalhandlers, menu);
+       UI_add_popup_handlers(C, &window->modalhandlers, menu, false);
        WM_event_add_mousemove(C);
        
        MEM_freeN(pup);
@@ -2599,6 +2658,175 @@ uiLayout *uiPupMenuLayout(uiPopupMenu *pup)
        return pup->layout;
 }
 
+/*************************** Pie Menus ***************************************/
+
+static uiBlock *ui_block_func_PIE(bContext *UNUSED(C), uiPopupBlockHandle *handle, void *arg_pie)
+{
+       uiBlock *block;
+       uiPieMenu *pie = arg_pie;
+       int minwidth, width, height;
+
+       minwidth = 50;
+       block = pie->block_radial;
+
+       /* in some cases we create the block before the region,
+        * so we set it delayed here if necessary */
+       if (BLI_findindex(&handle->region->uiblocks, block) == -1)
+               uiBlockSetRegion(block, handle->region);
+
+       uiBlockLayoutResolve(block, &width, &height);
+
+       uiBlockSetFlag(block, UI_BLOCK_LOOP | UI_BLOCK_REDRAW | UI_BLOCK_NUMSELECT);
+
+       block->minbounds = minwidth;
+       block->bounds = 1;
+       block->mx = 0;
+       block->my = 0;
+       block->bounds_type = UI_BLOCK_BOUNDS_PIE_CENTER;
+
+       block->pie_data.pie_center_spawned[0] = pie->mx;
+       block->pie_data.pie_center_spawned[1] = pie->my;
+
+       return pie->block_radial;
+}
+
+static float uiPieTitleWidth(const char *name, int icon)
+{
+       return (UI_GetStringWidth(name) +
+                (UI_UNIT_X * (1.50f + (icon ? 0.25f : 0.0f))));
+}
+
+uiPieMenu *uiPieMenuBegin(struct bContext *C, const char *title, int icon, const wmEvent *event)
+{
+       uiStyle *style = UI_GetStyleDraw();
+       uiPieMenu *pie = MEM_callocN(sizeof(uiPopupMenu), "pie menu");
+
+       pie->block_radial = uiBeginBlock(C, NULL, __func__, UI_EMBOSS);
+       /* may be useful later to allow spawning pies
+        * from old positions */
+       /* pie->block_radial->flag |= UI_BLOCK_POPUP_MEMORY; */
+       pie->block_radial->puphash = ui_popup_menu_hash(title);
+       pie->block_radial->flag |= UI_BLOCK_RADIAL;
+       pie->block_radial->pie_data.event = event->type;
+
+       pie->layout = uiBlockLayout(pie->block_radial, UI_LAYOUT_VERTICAL, UI_LAYOUT_PIEMENU, 0, 0, 200, 0, 0, style);
+       pie->mx = event->x;
+       pie->my = event->y;
+
+       /* create title button */
+       if (title[0]) {
+               char titlestr[256];
+               int w;
+               if (icon) {
+                       BLI_snprintf(titlestr, sizeof(titlestr), " %s", title);
+                       w = uiPieTitleWidth(titlestr, icon);
+                       uiDefIconTextBut(pie->block_radial, LABEL, 0, icon, titlestr, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
+               }
+               else {
+                       w = uiPieTitleWidth(title, 0);
+                       uiDefBut(pie->block_radial, LABEL, 0, title, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
+               }
+       }
+
+       return pie;
+}
+
+void uiPieMenuEnd(bContext *C, uiPieMenu *pie)
+{
+       wmWindow *window = CTX_wm_window(C);
+       uiPopupBlockHandle *menu;
+
+       menu = ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_PIE, pie);
+       menu->popup = true;
+       menu->towardstime = PIL_check_seconds_timer();
+
+       UI_add_popup_handlers(C, &window->modalhandlers, menu, true);
+       WM_event_add_mousemove(C);
+
+       MEM_freeN(pie);
+}
+
+uiLayout *uiPieMenuLayout(uiPieMenu *pie)
+{
+       return pie->layout;
+}
+
+void uiPieMenuInvoke(struct bContext *C, const char *idname, const wmEvent *event)
+{
+       uiPieMenu *pie;
+       uiLayout *layout;
+       Menu menu;
+       MenuType *mt = WM_menutype_find(idname, true);
+
+       if (mt == NULL) {
+               printf("%s: named menu \"%s\" not found\n", __func__, idname);
+               return;
+       }
+
+       if (mt->poll && mt->poll(C, mt) == 0)
+               return;
+
+       pie = uiPieMenuBegin(C, IFACE_(mt->label), ICON_NONE, event);
+       layout = uiPieMenuLayout(pie);
+
+       menu.layout = layout;
+       menu.type = mt;
+
+       if (G.debug & G_DEBUG_WM) {
+               printf("%s: opening menu \"%s\"\n", __func__, idname);
+       }
+
+       mt->draw(C, &menu);
+
+       uiPieMenuEnd(C, pie);
+}
+
+void uiPieOperatorEnumInvoke(struct bContext *C, const char *title, const char *opname,
+                             const char *propname, const wmEvent *event)
+{
+       uiPieMenu *pie;
+       uiLayout *layout;
+
+       pie = uiPieMenuBegin(C, IFACE_(title), ICON_NONE, event);
+       layout = uiPieMenuLayout(pie);
+
+       layout = uiLayoutRadial(layout);
+       uiItemsEnumO(layout, opname, propname);
+
+       uiPieMenuEnd(C, pie);
+}
+
+void uiPieEnumInvoke(struct bContext *C, const char *title, const char *path,
+                     const wmEvent *event)
+{
+       PointerRNA ctx_ptr;
+       PointerRNA r_ptr;
+       PropertyRNA *r_prop;
+       uiPieMenu *pie;
+       uiLayout *layout;
+
+       RNA_pointer_create(NULL, &RNA_Context, C, &ctx_ptr);
+
+       if (!RNA_path_resolve(&ctx_ptr, path, &r_ptr, &r_prop)) {
+               return;
+       }
+
+       /* invalid property, only accept enums */
+       if (RNA_property_type(r_prop) != PROP_ENUM) {
+               BLI_assert(0);
+               return;
+       }
+
+       pie = uiPieMenuBegin(C, IFACE_(title), ICON_NONE, event);
+       layout = uiPieMenuLayout(pie);
+
+       layout = uiLayoutRadial(layout);
+       uiItemFullR(layout, &r_ptr, r_prop, RNA_NO_INDEX, 0, UI_ITEM_R_EXPAND, NULL, 0);
+
+       uiPieMenuEnd(C, pie);
+}
+
+
 /*************************** Standard Popup Menus ****************************/
 
 void uiPupMenuReports(bContext *C, ReportList *reports)
@@ -2695,7 +2923,7 @@ void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, const char *opn
        handle->optype = (opname) ? WM_operatortype_find(opname, 0) : NULL;
        handle->opcontext = opcontext;
        
-       UI_add_popup_handlers(C, &window->modalhandlers, handle);
+       UI_add_popup_handlers(C, &window->modalhandlers, handle, false);
        WM_event_add_mousemove(C);
 }
 
@@ -2718,7 +2946,7 @@ void uiPupBlockEx(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_f
        handle->cancel_func = cancel_func;
        // handle->opcontext = opcontext;
        
-       UI_add_popup_handlers(C, &window->modalhandlers, handle);
+       UI_add_popup_handlers(C, &window->modalhandlers, handle, false);
        WM_event_add_mousemove(C);
 }
 
index 23f185befb90e0fb387da8e8f78530550c853756..db4ddeae659d091a5da33d52bdc23c3c339d0173 100644 (file)
@@ -1596,6 +1596,21 @@ static struct uiWidgetColors wcol_menu_back = {
        25, -20
 };
 
+/* pie menus */
+static struct uiWidgetColors wcol_pie_menu = {
+       {10, 10, 10, 200},
+       {25, 25, 25, 230},
+       {140, 140, 140, 255},
+       {45, 45, 45, 230},
+
+       {160, 160, 160, 255},
+       {255, 255, 255, 255},
+
+       1,
+       10, -10
+};
+
+
 /* tooltip color */
 static struct uiWidgetColors wcol_tooltip = {
        {0, 0, 0, 255},
@@ -1743,6 +1758,7 @@ void ui_widget_color_init(ThemeUI *tui)
        tui->wcol_menu = wcol_menu;
        tui->wcol_pulldown = wcol_pulldown;
        tui->wcol_menu_back = wcol_menu_back;
+       tui->wcol_pie_menu = wcol_pie_menu;
        tui->wcol_tooltip = wcol_tooltip;
        tui->wcol_menu_item = wcol_menu_item;
        tui->wcol_box = wcol_box;
@@ -1891,6 +1907,34 @@ static void widget_state_pulldown(uiWidgetType *wt, int state)
                copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
 }
 
+/* special case, pie menu items */
+static void widget_state_pie_menu_item(uiWidgetType *wt, int state)
+{
+       wt->wcol = *(wt->wcol_theme);
+
+       /* active and disabled (not so common) */
+       if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) {
+               widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.5f);
+               /* draw the backdrop at low alpha, helps navigating with keys
+                * when disabled items are active */
+               copy_v4_v4_char(wt->wcol.inner, wt->wcol.item);
+               wt->wcol.inner[3] = 64;
+       }
+       /* regular disabled */
+       else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
+               widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f);
+       }
+       /* regular active */
+       else if (state & UI_SELECT) {
+               copy_v4_v4_char(wt->wcol.outline, wt->wcol.inner_sel);
+               copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
+       }
+       else if (state & UI_ACTIVE) {
+               copy_v4_v4_char(wt->wcol.inner, wt->wcol.item);
+               copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
+       }
+}
+
 /* special case, menu items */
 static void widget_state_menu_item(uiWidgetType *wt, int state)
 {
@@ -2973,6 +3017,29 @@ static void widget_menu_itembut(uiWidgetColors *wcol, rcti *rect, int UNUSED(sta
        widgetbase_draw(&wtb, wcol);
 }
 
+static void widget_menu_radial_itembut(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
+{
+       uiWidgetBase wtb;
+       float rad;
+       float fac = but->block->pie_data.alphafac;
+
+       widget_init(&wtb);
+
+       wtb.emboss = 0;
+
+       rad = 0.5f * BLI_rcti_size_y(rect);
+       round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
+
+       wcol->inner[3] *= fac;
+       wcol->inner_sel[3] *= fac;
+       wcol->item[3] *= fac;
+       wcol->text[3] *= fac;
+       wcol->text_sel[3] *= fac;
+       wcol->outline[3] *= fac;
+
+       widgetbase_draw(&wtb, wcol);
+}
+
 static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
 {
        uiWidgetBase wtb;
@@ -3291,6 +3358,12 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
                        wt.wcol_theme = &btheme->tui.wcol_progress;
                        wt.custom = widget_progressbar;
                        break;
+
+               case UI_WTYPE_MENU_ITEM_RADIAL:
+                       wt.wcol_theme = &btheme->tui.wcol_pie_menu;
+                       wt.custom = widget_menu_radial_itembut;
+                       wt.state = widget_state_pie_menu_item;
+                       break;
        }
        
        return &wt;
@@ -3397,6 +3470,9 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
                /* "nothing" */
                wt = widget_type(UI_WTYPE_ICON);
        }
+       else if (but->dt == UI_EMBOSSR) {
+               wt = widget_type(UI_WTYPE_MENU_ITEM_RADIAL);
+       }
        else {
                
                switch (but->type) {
@@ -3649,6 +3725,125 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect)
        }
 }
 
+static void draw_disk_shaded(
+        float start, float angle,
+        float radius_int, float radius_ext, int subd,
+        const char col1[4], const char col2[4],
+        bool shaded)
+{
+       const float radius_ext_scale = (0.5f / radius_ext);  /* 1 / (2 * radius_ext) */
+       int i;
+
+       float s, c;
+       float y1, y2;
+       float fac;
+       unsigned char r_col[4];
+
+       glBegin(GL_TRIANGLE_STRIP);
+
+       s = sinf(start);
+       c = cosf(start);
+
+       y1 = s * radius_int;
+       y2 = s * radius_ext;
+
+       if (shaded) {
+               fac = (y1 + radius_ext) * radius_ext_scale;
+               round_box_shade_col4_r(r_col, col1, col2, fac);
+
+               glColor4ubv(r_col);
+       }
+
+       glVertex2f(c * radius_int, s * radius_int);
+
+       if (shaded) {
+               fac = (y2 + radius_ext) * radius_ext_scale;
+               round_box_shade_col4_r(r_col, col1, col2, fac);
+
+               glColor4ubv(r_col);
+       }
+       glVertex2f(c * radius_ext, s * radius_ext);
+
+       for (i = 1; i < subd; i++) {
+               float a;
+
+               a = start + ((i) / (float)(subd - 1)) * angle;
+               s = sinf(a);
+               c = cosf(a);
+               y1 = s * radius_int;
+               y2 = s * radius_ext;
+
+               if (shaded) {
+                       fac = (y1 + radius_ext) * radius_ext_scale;
+                       round_box_shade_col4_r(r_col, col1, col2, fac);
+
+                       glColor4ubv(r_col);
+               }
+               glVertex2f(c * radius_int, s * radius_int);
+
+               if (shaded) {
+                       fac = (y2 + radius_ext) * radius_ext_scale;
+                       round_box_shade_col4_r(r_col, col1, col2, fac);
+
+                       glColor4ubv(r_col);
+               }
+               glVertex2f(c * radius_ext, s * radius_ext);
+       }
+       glEnd();
+
+}
+
+void ui_draw_pie_center(uiBlock *block)
+{
+       bTheme *btheme = UI_GetTheme();
+       float cx = block->pie_data.pie_center_spawned[0];
+       float cy = block->pie_data.pie_center_spawned[1];
+
+       float *pie_dir = block->pie_data.pie_dir;
+
+       float pie_radius_internal = U.pixelsize * U.pie_menu_threshold;
+       float pie_radius_external = U.pixelsize * (U.pie_menu_threshold + 7.0f);
+
+       int subd = 40;
+
+       float angle = atan2(pie_dir[1], pie_dir[0]);
+       float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? ((float)M_PI / 2.0f) : ((float)M_PI / 4.0f);
+
+       glPushMatrix();
+       glTranslatef(cx, cy, 0.0f);
+
+       glEnable(GL_BLEND);
+       if (btheme->tui.wcol_pie_menu.shaded) {
+               char col1[4], col2[4];
+               shadecolors4(col1, col2, btheme->tui.wcol_pie_menu.inner, btheme->tui.wcol_pie_menu.shadetop, btheme->tui.wcol_pie_menu.shadedown);
+               draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, col1, col2, true);
+       }
+       else {
+               glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.inner);
+               draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, NULL, NULL, false);
+       }
+
+       if (!(block->pie_data.flags & UI_PIE_INVALID_DIR)) {
+               if (btheme->tui.wcol_pie_menu.shaded) {
+                       char col1[4], col2[4];
+                       shadecolors4(col1, col2, btheme->tui.wcol_pie_menu.inner_sel, btheme->tui.wcol_pie_menu.shadetop, btheme->tui.wcol_pie_menu.shadedown);
+                       draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, col1, col2, true);
+               }
+               else {
+                       glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.inner_sel);
+                       draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, NULL, NULL, false);
+               }
+       }
+
+       glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.outline);
+       glutil_draw_lined_arc(0.0f, (float)M_PI * 2.0f, pie_radius_internal, subd);
+       glutil_draw_lined_arc(0.0f, (float)M_PI * 2.0f, pie_radius_external, subd);
+
+       glDisable(GL_BLEND);
+       glPopMatrix();
+}
+
+
 uiWidgetColors *ui_tooltip_get_theme(void)
 {
        uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP);
index dbb0235f40f1a3455d48191d7da86cae4a60dd14..92277cc63eac33a189d21f47e8b8f74a0df273ab 100644 (file)
@@ -2454,6 +2454,31 @@ void init_userdef_do_versions(void)
                }
        }
 
+       if (U.versionfile < 271 || (U.versionfile == 271 && U.subversionfile < 4)) {
+               bTheme *btheme;
+
+               struct uiWidgetColors wcol_pie_menu = {
+                       {10, 10, 10, 200},
+                       {25, 25, 25, 230},
+                       {140, 140, 140, 255},
+                       {45, 45, 45, 230},
+
+                       {160, 160, 160, 255},
+                       {255, 255, 255, 255},
+
+                       1,
+                       10, -10
+               };
+
+               U.pie_menu_radius = 150;
+               U.pie_menu_threshold = 12;
+               U.pie_animation_timeout = 6;
+
+               for (btheme = U.themes.first; btheme; btheme = btheme->next) {
+                       btheme->tui.wcol_pie_menu = wcol_pie_menu;
+               }
+       }
+
        if (U.pixelsize == 0.0f)
                U.pixelsize = 1.0f;
        
index b171e7a5f88661bdf6bd0fc016edddbfb424da0d..c8431d58bf5451eed9ac8e5d435dea4e8fa06f8a 100644 (file)
@@ -61,6 +61,7 @@
 #include "ED_space_api.h"
 #include "ED_sound.h"
 #include "ED_uvedit.h"
+#include "ED_view3d.h"
 #include "ED_mball.h"
 #include "ED_logic.h"
 #include "ED_clip.h"
@@ -130,8 +131,17 @@ void ED_spacetypes_init(void)
                        type->operatortypes();
        }
 
-       /* Macros's must go last since they reference other operators
-        * maybe we'll need to have them go after python operators too? */
+       /* register internal render callbacks */
+       ED_render_internal_init();
+}
+
+void ED_spacemacros_init(void)
+{
+       const ListBase *spacetypes;
+       SpaceType *type;
+
+       /* Macros's must go last since they reference other operators.
+        * We need to have them go after python operators too */
        ED_operatormacros_armature();
        ED_operatormacros_mesh();
        ED_operatormacros_metaball();
@@ -152,9 +162,6 @@ void ED_spacetypes_init(void)
                if (type->dropboxes)
                        type->dropboxes();
        }
-       
-       /* register internal render callbacks */
-       ED_render_internal_init();
 }
 
 /* called in wm.c */
index 5e2346cecca90841f5c23da80d68ae1e52f5ed1d..5988473588b4cde6d162d2dd87b775a9a6fe8228 100644 (file)
@@ -78,6 +78,7 @@
 #include "ED_view3d.h"
 #include "ED_sculpt.h"
 
+#include "UI_resources.h"
 
 #include "PIL_time.h" /* smoothview */
 
@@ -3583,13 +3584,13 @@ void VIEW3D_OT_zoom_camera_1_to_1(wmOperatorType *ot)
 /* ********************* Changing view operator ****************** */
 
 static EnumPropertyItem prop_view_items[] = {
+       {RV3D_VIEW_LEFT, "LEFT", ICON_TRIA_LEFT, "Left", "View From the Left"},
+       {RV3D_VIEW_RIGHT, "RIGHT", ICON_TRIA_RIGHT, "Right", "View From the Right"},
+       {RV3D_VIEW_BOTTOM, "BOTTOM", ICON_TRIA_DOWN, "Bottom", "View From the Bottom"},
+       {RV3D_VIEW_TOP, "TOP", ICON_TRIA_UP, "Top", "View From the Top"},
        {RV3D_VIEW_FRONT, "FRONT", 0, "Front", "View From the Front"},
        {RV3D_VIEW_BACK, "BACK", 0, "Back", "View From the Back"},
-       {RV3D_VIEW_LEFT, "LEFT", 0, "Left", "View From the Left"},
-       {RV3D_VIEW_RIGHT, "RIGHT", 0, "Right", "View From the Right"},
-       {RV3D_VIEW_TOP, "TOP", 0, "Top", "View From the Top"},
-       {RV3D_VIEW_BOTTOM, "BOTTOM", 0, "Bottom", "View From the Bottom"},
-       {RV3D_VIEW_CAMERA, "CAMERA", 0, "Camera", "View From the Active Camera"},
+       {RV3D_VIEW_CAMERA, "CAMERA", ICON_CAMERA_DATA, "Camera", "View From the Active Camera"},
        {0, NULL, 0, NULL, NULL}
 };
 
index 4240d85a74def1ab85ad36f50a3c0acdff8790b4..d1f5cc1eadd95b5da468977de1d8cbaefaa28923 100644 (file)
@@ -162,7 +162,7 @@ typedef struct ThemeUI {
        uiWidgetColors wcol_radio, wcol_option, wcol_toggle;
        uiWidgetColors wcol_num, wcol_numslider;
        uiWidgetColors wcol_menu, wcol_pulldown, wcol_menu_back, wcol_menu_item, wcol_tooltip;
-       uiWidgetColors wcol_box, wcol_scroll, wcol_progress, wcol_list_item;
+       uiWidgetColors wcol_box, wcol_scroll, wcol_progress, wcol_list_item, wcol_pie_menu;
        
        uiWidgetStateColors wcol_state;
 
@@ -534,6 +534,15 @@ typedef struct UserDef {
        float fcu_inactive_alpha;       /* opacity of inactive F-Curves in F-Curve Editor */
        float pixelsize;                        /* private, set by GHOST, to multiply DPI with */
 
+       short pie_interaction_type;     /* if keeping a pie menu spawn button pressed after this time, it turns into
+                                    * a drag/release pie menu */
+       short pie_initial_timeout;  /* direction in the pie menu will always be calculated from the initial position
+                                    * within this time limit */
+       int pie_animation_timeout;
+       int pad2;
+       short pie_menu_radius;        /* pie menu radius */
+       short pie_menu_threshold;     /* pie menu distance from center before a direction is set */
+
        struct WalkNavigation walk_navigation;
 } UserDef;
 
index ba2dd8b5d69e57c051f2cf0bf7f8a8ea01cd0512..0e091112d8faba271a8ae17e7f13ea8000478b64 100644 (file)
@@ -633,6 +633,7 @@ extern StructRNA RNA_TransformConstraint;
 extern StructRNA RNA_TransformSequence;
 extern StructRNA RNA_UILayout;
 extern StructRNA RNA_UIList;
+extern StructRNA RNA_UIPieMenu;
 extern StructRNA RNA_UIPopupMenu;
 extern StructRNA RNA_UVWarpModifier;
 extern StructRNA RNA_UVProjectModifier;
@@ -794,6 +795,8 @@ void RNA_property_enum_items(struct bContext *C, PointerRNA *ptr, PropertyRNA *p
                              EnumPropertyItem **item, int *r_totitem, bool *r_free);
 void RNA_property_enum_items_gettexted(struct bContext *C, PointerRNA *ptr, PropertyRNA *prop,
                                        EnumPropertyItem **r_item, int *r_totitem, bool *r_free);
+void RNA_property_enum_items_gettexted_all(struct bContext *C, PointerRNA *ptr, PropertyRNA *prop,
+                                       EnumPropertyItem **r_item, int *r_totitem, bool *r_free);
 bool RNA_property_enum_value(struct bContext *C, PointerRNA *ptr, PropertyRNA *prop, const char *identifier, int *r_value);
 bool RNA_property_enum_identifier(struct bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **identifier);
 bool RNA_property_enum_name(struct bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **name);
index 8c76edd2f17591c471cd93f1841f73e8ee220804..079f71d7be374b1cd31a0affa64f11eccf31f109 100644 (file)
@@ -1249,12 +1249,9 @@ void RNA_property_enum_items(bContext *C, PointerRNA *ptr, PropertyRNA *prop, En
        }
 }
 
-void RNA_property_enum_items_gettexted(bContext *C, PointerRNA *ptr, PropertyRNA *prop,
-                                       EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
-{
-       RNA_property_enum_items(C, ptr, prop, r_item, r_totitem, r_free);
-
 #ifdef WITH_INTERNATIONAL
+static void property_enum_translate(PropertyRNA *prop, EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
+{
        if (!(prop->flag & PROP_ENUM_NO_TRANSLATE)) {
                int i;
 
@@ -1300,9 +1297,71 @@ void RNA_property_enum_items_gettexted(bContext *C, PointerRNA *ptr, PropertyRNA
 
                *r_item = nitem;
        }
+}
+#endif
+
+void RNA_property_enum_items_gettexted(bContext *C, PointerRNA *ptr, PropertyRNA *prop,
+                                       EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
+{
+       RNA_property_enum_items(C, ptr, prop, r_item, r_totitem, r_free);
+
+#ifdef WITH_INTERNATIONAL
+       property_enum_translate(prop, r_item, r_totitem, r_free);
 #endif
 }
 
+void RNA_property_enum_items_gettexted_all(bContext *C, PointerRNA *ptr, PropertyRNA *prop,
+                                       EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
+{
+       EnumPropertyRNA *eprop = (EnumPropertyRNA *)rna_ensure_property(prop);
+       int mem_size = sizeof(EnumPropertyItem) * (eprop->totitem + 1);
+       /* first return all items */
+       *r_free = true;
+       *r_item = MEM_mallocN(mem_size, "enum_gettext_all");
+        memcpy(*r_item, eprop->item, mem_size);
+
+       if (r_totitem)
+               *r_totitem = eprop->totitem;
+
+       if (eprop->itemf && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
+               EnumPropertyItem *item;
+               int i;
+               bool free = false;
+
+               if (prop->flag & PROP_ENUM_NO_CONTEXT)
+                       item = eprop->itemf(NULL, ptr, prop, &free);
+               else
+                       item = eprop->itemf(C, ptr, prop, &free);
+
+               /* any callbacks returning NULL should be fixed */
+               BLI_assert(item != NULL);
+
+               for (i = 0; i < eprop->totitem; i++) {
+                       bool exists = false;
+                       int i_fixed;
+
+                       /* items that do not exist on list are returned, but have their names/identifiers NULLed out */
+                       for (i_fixed = 0; item[i_fixed].identifier; i_fixed++) {
+                               if (STREQ(item[i_fixed].identifier, (*r_item)[i].identifier)) {
+                                       exists = true;
+                                       break;
+                               }
+                       }
+
+                       if (!exists) {
+                               (*r_item)[i].name = NULL;
+                               (*r_item)[i].identifier = "";
+                       }
+               }
+
+               if (free)
+                       MEM_freeN(item);
+       }
+
+#ifdef WITH_INTERNATIONAL
+       property_enum_translate(prop, r_item, r_totitem, r_free);
+#endif
+}
 
 bool RNA_property_enum_value(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const char *identifier, int *r_value)
 {
index 5d10a0a61c1783c862c2c0d8523c7aba55efe043..216a99866b3318c9e381cb5cb935b448bbe99636 100644 (file)
 EnumPropertyItem object_mode_items[] = {
        {OB_MODE_OBJECT, "OBJECT", ICON_OBJECT_DATAMODE, "Object Mode", ""},
        {OB_MODE_EDIT, "EDIT", ICON_EDITMODE_HLT, "Edit Mode", ""},
+       {OB_MODE_POSE, "POSE", ICON_POSE_HLT, "Pose Mode", ""},
        {OB_MODE_SCULPT, "SCULPT", ICON_SCULPTMODE_HLT, "Sculpt Mode", ""},
        {OB_MODE_VERTEX_PAINT, "VERTEX_PAINT", ICON_VPAINT_HLT, "Vertex Paint", ""},
        {OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""},
        {OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""},
        {OB_MODE_PARTICLE_EDIT, "PARTICLE_EDIT", ICON_PARTICLEMODE, "Particle Edit", ""},
-       {OB_MODE_POSE, "POSE", ICON_POSE_HLT, "Pose Mode", ""},
        {0, NULL, 0, NULL, NULL}
 };
 
index 0ddc63e57cf9891d0568996fed4a7756171932f4..7dc77814ad5d51cc4db756aed445a6d935a35217 100644 (file)
@@ -1784,11 +1784,11 @@ static void rna_def_space_view3d(BlenderRNA *brna)
        PropertyRNA *prop;
 
        static EnumPropertyItem manipulators_items[] = {
-               {V3D_MANIP_TRANSLATE, "TRANSLATE", ICON_MAN_TRANS, "Manipulator Translate",
+               {V3D_MANIP_TRANSLATE, "TRANSLATE", ICON_MAN_TRANS, "Translate",
                                      "Use the manipulator for movement transformations"},
-               {V3D_MANIP_ROTATE, "ROTATE", ICON_MAN_ROT, "Manipulator Rotate",
+               {V3D_MANIP_ROTATE, "ROTATE", ICON_MAN_ROT, "Rotate",
                                   "Use the manipulator for rotation transformations"},
-               {V3D_MANIP_SCALE, "SCALE", ICON_MAN_SCALE, "Manipulator Scale",
+               {V3D_MANIP_SCALE, "SCALE", ICON_MAN_SCALE, "Scale",
                                  "Use the manipulator for scale transformations"},
                {0, NULL, 0, NULL, NULL}
        };
index da3d7b029edf9e4b14dbf72c279a89fed7f497cf..b13bdedaffdc5a659858571887c01c5ee48d1789 100644 (file)
@@ -462,6 +462,13 @@ void RNA_api_ui_layout(StructRNA *srna)
        RNA_def_float(func, "percentage", 0.0f, 0.0f, 1.0f, "Percentage", "Percentage of width to split at", 0.0f, 1.0f);
        RNA_def_boolean(func, "align", false, "", "Align buttons to each other");
 
+       /* radial/pie layout */
+       func = RNA_def_function(srna, "menu_pie", "uiLayoutRadial");
+       parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in");
+       RNA_def_function_return(func, parm);
+       RNA_def_function_ui_description(func, "Sublayout. Items placed in this sublayout are placed "
+                                       "in a radial fashion around the menu center)");
+
        /* Icon of a rna pointer */
        func = RNA_def_function(srna, "icon", "rna_ui_get_rnaptr_icon");
        parm = RNA_def_int(func, "icon_value", ICON_NONE, 0, INT_MAX, "", "Icon identifier", 0, INT_MAX);
index 12958297028c1977046c4079f60113c16f47d13c..f11780768fdb8eb006e1211b2aa41e37369fa9ed 100644 (file)
@@ -953,6 +953,12 @@ static void rna_def_userdef_theme_ui(BlenderRNA *brna)
        RNA_def_property_flag(prop, PROP_NEVER_NULL);
        RNA_def_property_ui_text(prop, "Menu Backdrop Colors", "");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+       prop = RNA_def_property(srna, "wcol_pie_menu", PROP_POINTER, PROP_NONE);
+       RNA_def_property_flag(prop, PROP_NEVER_NULL);
+       RNA_def_property_ui_text(prop, "Pie Menu Colors", "");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
+
        prop = RNA_def_property(srna, "wcol_tooltip", PROP_POINTER, PROP_NONE);
        RNA_def_property_flag(prop, PROP_NEVER_NULL);
        RNA_def_property_ui_text(prop, "Tooltip Colors", "");
@@ -3208,6 +3214,26 @@ static void rna_def_userdef_view(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Sub Level Menu Open Delay",
                                 "Time delay in 1/10 seconds before automatically opening sub level menus");
 
+       /* pie menus */
+       prop = RNA_def_property(srna, "pie_initial_timeout", PROP_INT, PROP_NONE);
+       RNA_def_property_range(prop, 0, 1000);
+       RNA_def_property_ui_text(prop, "Recenter Timeout",
+                                "Pie menus will use the initial mouse position as center for this amount of time "
+                                "(in 1/100ths of sec)");
+
+       prop = RNA_def_property(srna, "pie_animation_timeout", PROP_INT, PROP_NONE);
+       RNA_def_property_range(prop, 0, 1000);
+       RNA_def_property_ui_text(prop, "Animation Timeout",
+                                "Time needed to fully animate the pie to unfolded state (in 1/100ths of sec)");
+
+       prop = RNA_def_property(srna, "pie_menu_radius", PROP_INT, PROP_PIXEL);
+       RNA_def_property_range(prop, 0, 1000);
+       RNA_def_property_ui_text(prop, "Radius", "Pie menu size in pixels");
+
+       prop = RNA_def_property(srna, "pie_menu_threshold", PROP_INT, PROP_PIXEL);
+       RNA_def_property_range(prop, 0, 1000);
+       RNA_def_property_ui_text(prop, "Threshold", "Distance from center needed before a selection can be made");
+
        prop = RNA_def_property(srna, "use_quit_dialog", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_QUIT_PROMPT);
        RNA_def_property_ui_text(prop, "Prompt Quit",
index 996d2d2882d35de66d02431d46f319cc505704f4..c88fa020ed66189dd906f8f8ff317d87609c118e 100644 (file)
@@ -594,6 +594,17 @@ static PointerRNA rna_PopupMenu_layout_get(PointerRNA *ptr)
        return rptr;
 }
 
+static PointerRNA rna_PieMenu_layout_get(PointerRNA *ptr)
+{
+       struct uiPieMenu *pie = ptr->data;
+       uiLayout *layout = uiPieMenuLayout(pie);
+
+       PointerRNA rptr;
+       RNA_pointer_create(ptr->id.data, &RNA_UILayout, layout, &rptr);
+
+       return rptr;
+}
+
 static void rna_Window_screen_set(PointerRNA *ptr, PointerRNA value)
 {
        wmWindow *win = (wmWindow *)ptr->data;
@@ -1716,6 +1727,26 @@ static void rna_def_popupmenu(BlenderRNA *brna)
        RNA_define_verify_sdna(1); /* not in sdna */
 }
 
+static void rna_def_piemenu(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna = RNA_def_struct(brna, "UIPieMenu", NULL);
+       RNA_def_struct_ui_text(srna, "PieMenu", "");
+       RNA_def_struct_sdna(srna, "uiPieMenu");
+
+       RNA_define_verify_sdna(0); /* not in sdna */
+
+       /* could wrap more, for now this is enough */
+       prop = RNA_def_property(srna, "layout", PROP_POINTER, PROP_NONE);
+       RNA_def_property_struct_type(prop, "UILayout");
+       RNA_def_property_pointer_funcs(prop, "rna_PieMenu_layout_get",
+                                      NULL, NULL, NULL);
+
+       RNA_define_verify_sdna(1); /* not in sdna */
+}
+
 static void rna_def_window(BlenderRNA *brna)
 {
        StructRNA *srna;
@@ -2078,6 +2109,7 @@ void RNA_def_wm(BlenderRNA *brna)
        rna_def_event(brna);
        rna_def_timer(brna);
        rna_def_popupmenu(brna);
+       rna_def_piemenu(brna);
        rna_def_window(brna);
        rna_def_windowmanager(brna);
        rna_def_keyconfig(brna);
index 9b288903aa2f7fb760c2473579ac28b5e9395496..7d499521a1ab4b077d39e5db52776afc2c2e31f1 100644 (file)
@@ -310,6 +310,24 @@ static void rna_PupMenuEnd(bContext *C, PointerRNA *handle)
        uiPupMenuEnd(C, handle->data);
 }
 
+/* pie menu wrapper */
+static PointerRNA rna_PieMenuBegin(bContext *C, const char *title, int icon, PointerRNA *event)
+{
+       PointerRNA r_ptr;
+       void *data;
+
+       data = (void *)uiPieMenuBegin(C, title, icon, event->data);
+
+       RNA_pointer_create(NULL, &RNA_UIPieMenu, data, &r_ptr);
+
+       return r_ptr;
+}
+
+static void rna_PieMenuEnd(bContext *C, PointerRNA *handle)
+{
+       uiPieMenuEnd(C, handle->data);
+}
+
 #else
 
 #define WM_GEN_INVOKE_EVENT (1 << 0)
@@ -461,6 +479,26 @@ void RNA_api_wm(StructRNA *srna)
        RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT);
        parm = RNA_def_pointer(func, "menu", "UIPopupMenu", "", "");
        RNA_def_property_flag(parm, PROP_RNAPTR | PROP_NEVER_NULL);
+
+       /* wrap uiPieMenuBegin */
+       func = RNA_def_function(srna, "piemenu_begin__internal", "rna_PieMenuBegin");
+       RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT);
+       parm = RNA_def_string(func, "title", NULL, 0, "", "");
+       RNA_def_property_flag(parm, PROP_REQUIRED);
+       parm = RNA_def_property(func, "icon", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_items(parm, icon_items);
+       parm = RNA_def_pointer(func, "event", "Event", "", "");
+       RNA_def_property_flag(parm, PROP_RNAPTR | PROP_NEVER_NULL);
+       /* return */
+       parm = RNA_def_pointer(func, "menu_pie", "UIPieMenu", "", "");
+       RNA_def_property_flag(parm, PROP_RNAPTR | PROP_NEVER_NULL);
+       RNA_def_function_return(func, parm);
+
+       /* wrap uiPieMenuEnd */
+       func = RNA_def_function(srna, "piemenu_end__internal", "rna_PieMenuEnd");
+       RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT);
+       parm = RNA_def_pointer(func, "menu", "UIPieMenu", "", "");
+       RNA_def_property_flag(parm, PROP_RNAPTR | PROP_NEVER_NULL);
 }
 
 void RNA_api_operator(StructRNA *srna)
index 253976052fd84f4b9b2131cf4a9f6e23dafe58ab..35b7fb4b9c93678c27b4da6c3a5733af979e037c 100644 (file)
@@ -151,7 +151,7 @@ typedef void (*wmUIHandlerRemoveFunc)(struct bContext *C, void *userdata);
 struct wmEventHandler *WM_event_add_ui_handler(
         const struct bContext *C, ListBase *handlers,
         wmUIHandlerFunc ui_handle, wmUIHandlerRemoveFunc ui_remove,
-        void *userdata);
+        void *userdata,  const bool accept_dbl_click);
 void WM_event_remove_ui_handler(
         ListBase *handlers,
         wmUIHandlerFunc ui_handle, wmUIHandlerRemoveFunc ui_remove,
index 9645c95f62b7ccc124bb0d9aa24c230a9cc1482f..8f3b4dbb8e661dbf31612c6ee374051de8d06f2b 100644 (file)
@@ -64,6 +64,8 @@ wmKeyMapItem *WM_keymap_add_item(struct wmKeyMap *keymap, const char *idname, in
                                  int val, int modifier, int keymodifier);
 wmKeyMapItem *WM_keymap_add_menu(struct wmKeyMap *keymap, const char *idname, int type,
                                  int val, int modifier, int keymodifier);
+wmKeyMapItem *WM_keymap_add_menu_pie(struct wmKeyMap *keymap, const char *idname, int type,
+                                     int val, int modifier, int keymodifier, bool force_click);
 
 bool        WM_keymap_remove_item(struct wmKeyMap *keymap, struct wmKeyMapItem *kmi);
 int         WM_keymap_item_to_string(wmKeyMapItem *kmi, char *str, const int len);
index 7e2b7f2eb65d3dfd89586b380addb0f55ea7d918..ab4b21d5e33cd353d87e4f262b3d64572ce0ba16 100644 (file)
@@ -401,8 +401,12 @@ static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *eve
        
        /* UI code doesn't handle return values - it just always returns break. 
         * to make the DBL_CLICK conversion work, we just don't send this to UI, except mouse clicks */
-       if (event->type != LEFTMOUSE && event->val == KM_DBL_CLICK)
+       if (((handler->flag & WM_HANDLER_ACCEPT_DBL_CLICK) == 0) &&
+           (event->type != LEFTMOUSE) &&
+           (event->val == KM_DBL_CLICK))
+       {
                return WM_HANDLER_CONTINUE;
+       }
        
        /* UI is quite aggressive with swallowing events, like scrollwheel */
        /* I realize this is not extremely nice code... when UI gets keymaps it can be maybe smarter */
@@ -2563,7 +2567,7 @@ void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
 wmEventHandler *WM_event_add_ui_handler(
         const bContext *C, ListBase *handlers,
         wmUIHandlerFunc ui_handle, wmUIHandlerRemoveFunc ui_remove,
-        void *userdata)
+        void *userdata, const bool accept_dbl_click)
 {
        wmEventHandler *handler = MEM_callocN(sizeof(wmEventHandler), "event ui handler");
        handler->ui_handle = ui_handle;
@@ -2580,6 +2584,9 @@ wmEventHandler *WM_event_add_ui_handler(
                handler->ui_menu    = NULL;
        }
 
+       if (accept_dbl_click) {
+               handler->flag |= WM_HANDLER_ACCEPT_DBL_CLICK;
+       }
        
        BLI_addhead(handlers, handler);
        
index 693c48cf8b911923dd0b5e3bfa1a831892e59ac7..da0ef2b0c2ac136f999001487e6fb47d46b3bf35 100644 (file)
@@ -187,6 +187,8 @@ void WM_init(bContext *C, int argc, const char **argv)
        (void)argv; /* unused */
 #endif
 
+       ED_spacemacros_init();
+
        if (!G.background && !wm_start_with_console)
                GHOST_toggleConsole(3);
 
index a95f2af1a8f125a74d7c957f4ce05209aa2d413e..e7bdce92122ee29d99751d33ec9ea1f3ce01c206 100644 (file)
@@ -464,6 +464,14 @@ wmKeyMapItem *WM_keymap_add_menu(wmKeyMap *keymap, const char *idname, int type,
        return kmi;
 }
 
+wmKeyMapItem *WM_keymap_add_menu_pie(wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier, bool force_click)
+{
+       wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_call_menu_pie", type, val, modifier, keymodifier);
+       RNA_string_set(kmi->ptr, "name", idname);
+       RNA_boolean_set(kmi->ptr, "force_click", force_click);
+       return kmi;
+}
+
 bool WM_keymap_remove_item(wmKeyMap *keymap, wmKeyMapItem *kmi)
 {
        if (BLI_findindex(&keymap->items, kmi) != -1) {
index 0239175835ed9e3fa6d378583f37c4d18eb898c4..d71beed3602ffe8362a2b0ea7e7055129f0b691c 100644 (file)
@@ -2049,6 +2049,41 @@ static void WM_OT_call_menu(wmOperatorType *ot)
        RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu");
 }
 
+static int wm_call_pie_menu_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+       char idname[BKE_ST_MAXNAME];
+       RNA_string_get(op->ptr, "name", idname);
+
+       uiPieMenuInvoke(C, idname, event);
+
+       return OPERATOR_CANCELLED;
+}
+
+static int wm_call_pie_menu_exec(bContext *C, wmOperator *op)
+{
+       char idname[BKE_ST_MAXNAME];
+       RNA_string_get(op->ptr, "name", idname);
+
+       uiPieMenuInvoke(C, idname, CTX_wm_window(C)->eventstate);
+
+       return OPERATOR_CANCELLED;
+}
+
+static void WM_OT_call_menu_pie(wmOperatorType *ot)
+{
+       ot->name = "Call Pie Menu";
+       ot->idname = "WM_OT_call_menu_pie";
+       ot->description = "Call (draw) a pre-defined pie menu";
+
+       ot->invoke = wm_call_pie_menu_invoke;
+       ot->exec = wm_call_pie_menu_exec;
+       ot->poll = WM_operator_winactive;
+
+       ot->flag = OPTYPE_INTERNAL;
+
+       RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu");
+}
+
 /* ************ window / screen operator definitions ************** */
 
 /* this poll functions is needed in place of WM_operator_winactive
@@ -4427,6 +4462,7 @@ void wm_operatortype_init(void)
        WM_operatortype_append(WM_OT_splash);
        WM_operatortype_append(WM_OT_search_menu);
        WM_operatortype_append(WM_OT_call_menu);
+       WM_operatortype_append(WM_OT_call_menu_pie);
        WM_operatortype_append(WM_OT_radial_control);
 #if defined(WIN32)
        WM_operatortype_append(WM_OT_console_toggle);
index df084554c5478fdb70f57c44f7bd8b1a7444f471..d1a94194108670d4b721d208a2f6afd033093759 100644 (file)
@@ -81,8 +81,9 @@ enum {
 
 /* handler flag */
 enum {
-       WM_HANDLER_BLOCKING    = 1,  /* after this handler all others are ignored */
-       WM_HANDLER_DO_FREE     = 2   /* handler tagged to be freed in wm_handlers_do() */
+       WM_HANDLER_BLOCKING             = (1 << 0),  /* after this handler all others are ignored */
+       WM_HANDLER_DO_FREE              = (1 << 1),  /* handler tagged to be freed in wm_handlers_do() */
+       WM_HANDLER_ACCEPT_DBL_CLICK     = (1 << 2),  /* handler accepts double key press events */
 };
 
 /* wm_event_system.c */
index 57e947ed0a8ce201563bffc6e852ec2aa4f7d7e2..87a8bbf3e0298cc28997137b7e14621e0f94b2c0 100644 (file)
@@ -622,6 +622,13 @@ struct wmKeyMap *WM_modalkeymap_add(struct wmKeyConfig *keyconf, const char *idn
 struct uiPopupMenu *uiPupMenuBegin(struct bContext *C, const char *title, int icon) RET_NULL
 void uiPupMenuEnd(struct bContext *C, struct uiPopupMenu *head) RET_NONE
 struct uiLayout *uiPupMenuLayout(struct uiPopupMenu *head) RET_NULL
+struct uiLayout *uiPieMenuLayout(struct uiPieMenu *pie) RET_NULL
+void uiPieMenuInvoke(struct bContext *C, const char *idname, const struct wmEvent *event) RET_NONE
+struct uiPieMenu *uiPieMenuBegin(struct bContext *C, const char *title, int icon, const struct wmEvent *event) RET_NULL
+void uiPieMenuEnd(struct bContext *C, uiPieMenu *pie) RET_NONE
+struct uiLayout *uiLayoutRadial(struct uiLayout *layout) RET_NULL
+void uiPieOperatorEnumInvoke(struct bContext *C, const char *title, const char *opname,
+                             const char *propname, const struct wmEvent *event) RET_NONE
 
 /* RNA COLLADA dependency */
 int collada_export(struct Scene *sce,