diff options
Diffstat (limited to 'source/blender/windowmanager/gizmo')
11 files changed, 4825 insertions, 0 deletions
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h new file mode 100644 index 00000000000..ffd18af496e --- /dev/null +++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h @@ -0,0 +1,343 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmos/WM_gizmo_api.h + * \ingroup wm + * + * \name Manipulator API + * \brief API for external use of wmManipulator types. + * + * Only included in WM_api.h + */ + + +#ifndef __WM_GIZMO_API_H__ +#define __WM_GIZMO_API_H__ + +struct ARegion; +struct GHashIterator; +struct IDProperty; +struct Main; +struct PropertyRNA; +struct wmKeyConfig; +struct wmManipulator; +struct wmManipulatorProperty; +struct wmManipulatorPropertyType; +struct wmManipulatorType; +struct wmManipulatorGroup; +struct wmManipulatorGroupType; +struct wmManipulatorMap; +struct wmManipulatorMapType; +struct wmManipulatorMapType_Params; +struct wmMsgSubscribeKey; +struct wmMsgSubscribeValue; + +#include "wm_gizmo_fn.h" + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + +struct wmManipulator *WM_manipulator_new_ptr( + const struct wmManipulatorType *wt, struct wmManipulatorGroup *mgroup, + struct PointerRNA *properties); +struct wmManipulator *WM_manipulator_new( + const char *idname, struct wmManipulatorGroup *mgroup, + struct PointerRNA *properties); +void WM_manipulator_free(struct wmManipulator *mpr); +void WM_manipulator_unlink( + ListBase *manipulatorlist, struct wmManipulatorMap *mmap, struct wmManipulator *mpr, + struct bContext *C); + +void WM_manipulator_name_set(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr, const char *name); + +bool WM_manipulator_select_unlink(struct wmManipulatorMap *mmap, struct wmManipulator *mpr); +bool WM_manipulator_select_set(struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select); +void WM_manipulator_highlight_set(struct wmManipulatorMap *mmap, struct wmManipulator *mpr); + +void WM_manipulator_modal_set_from_setup( + struct wmManipulatorMap *mmap, struct bContext *C, + struct wmManipulator *mpr, int part_index, const struct wmEvent *event); + +struct wmManipulatorOpElem *WM_manipulator_operator_get( + struct wmManipulator *mpr, int part_index); +struct PointerRNA *WM_manipulator_operator_set( + struct wmManipulator *mpr, int part_index, + struct wmOperatorType *ot, struct IDProperty *properties); + +/* callbacks */ +void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn); + +void WM_manipulator_set_matrix_location( + struct wmManipulator *mpr, const float origin[3]); +void WM_manipulator_set_matrix_rotation_from_z_axis( + struct wmManipulator *mpr, const float z_axis[3]); +void WM_manipulator_set_matrix_rotation_from_yz_axis( + struct wmManipulator *mpr, const float y_axis[3], const float z_axis[3]); + +void WM_manipulator_set_matrix_offset_location( + struct wmManipulator *mpr, const float origin[3]); +void WM_manipulator_set_matrix_offset_rotation_from_z_axis( + struct wmManipulator *mpr, const float z_axis[3]); +void WM_manipulator_set_matrix_offset_rotation_from_yz_axis( + struct wmManipulator *mpr, const float y_axis[3], const float z_axis[3]); + +void WM_manipulator_set_flag(struct wmManipulator *mpr, const int flag, const bool enable); +void WM_manipulator_set_scale(struct wmManipulator *mpr, float scale); +void WM_manipulator_set_line_width(struct wmManipulator *mpr, const float line_width); + +void WM_manipulator_get_color(const struct wmManipulator *mpr, float color[4]); +void WM_manipulator_set_color(struct wmManipulator *mpr, const float color[4]); +void WM_manipulator_get_color_highlight(const struct wmManipulator *mpr, float color_hi[4]); +void WM_manipulator_set_color_highlight(struct wmManipulator *mpr, const float color[4]); + +/** + * Leaving values NULL use values from #wmManipulator. + */ +struct WM_ManipulatorMatrixParams { + const float(*matrix_space)[4]; + const float(*matrix_basis)[4]; + const float(*matrix_offset)[4]; + const float *scale_final; +}; + +void WM_manipulator_calc_matrix_final_params( + const struct wmManipulator *mpr, const struct WM_ManipulatorMatrixParams *params, + float r_mat[4][4]); +void WM_manipulator_calc_matrix_final_no_offset( + const struct wmManipulator *mpr, float r_mat[4][4]); + +void WM_manipulator_calc_matrix_final( + const struct wmManipulator *mpr, float r_mat[4][4]); + +/* properties */ +void WM_manipulator_properties_create_ptr(struct PointerRNA *ptr, struct wmManipulatorType *wt); +void WM_manipulator_properties_create(struct PointerRNA *ptr, const char *opstring); +void WM_manipulator_properties_alloc(struct PointerRNA **ptr, struct IDProperty **properties, const char *wtstring); +void WM_manipulator_properties_sanitize(struct PointerRNA *ptr, const bool no_context); +bool WM_manipulator_properties_default(struct PointerRNA *ptr, const bool do_update); +void WM_manipulator_properties_reset(struct wmManipulator *op); +void WM_manipulator_properties_clear(struct PointerRNA *ptr); +void WM_manipulator_properties_free(struct PointerRNA *ptr); + + +/* wm_manipulator_type.c */ +const struct wmManipulatorType *WM_manipulatortype_find(const char *idname, bool quiet); +void WM_manipulatortype_append(void (*wtfunc)(struct wmManipulatorType *)); +void WM_manipulatortype_append_ptr(void (*mnpfunc)(struct wmManipulatorType *, void *), void *userdata); +bool WM_manipulatortype_remove(struct bContext *C, struct Main *bmain, const char *idname); +void WM_manipulatortype_remove_ptr(struct bContext *C, struct Main *bmain, struct wmManipulatorType *wt); +void WM_manipulatortype_iter(struct GHashIterator *ghi); + +/* wm_manipulator_group_type.c */ +struct wmManipulatorGroupType *WM_manipulatorgrouptype_find(const char *idname, bool quiet); +struct wmManipulatorGroupType *WM_manipulatorgrouptype_append(void (*wtfunc)(struct wmManipulatorGroupType *)); +struct wmManipulatorGroupType *WM_manipulatorgrouptype_append_ptr(void (*mnpfunc)(struct wmManipulatorGroupType *, void *), void *userdata); +bool WM_manipulatorgrouptype_free(const char *idname); +void WM_manipulatorgrouptype_free_ptr(struct wmManipulatorGroupType *wt); +void WM_manipulatorgrouptype_iter(struct GHashIterator *ghi); + +struct wmManipulatorGroupTypeRef *WM_manipulatorgrouptype_append_and_link( + struct wmManipulatorMapType *mmap_type, + void (*wtfunc)(struct wmManipulatorGroupType *)); + +/* wm_manipulator_map.c */ + +/* Dynamic Updates (for RNA runtime registration) */ +void WM_manipulatorconfig_update_tag_init(struct wmManipulatorMapType *mmap_type, struct wmManipulatorGroupType *wgt); +void WM_manipulatorconfig_update_tag_remove(struct wmManipulatorMapType *mmap_type, struct wmManipulatorGroupType *wgt); +void WM_manipulatorconfig_update(struct Main *bmain); + + +/* wm_maniulator_target_props.c */ +struct wmManipulatorProperty *WM_manipulator_target_property_array(struct wmManipulator *mpr); +struct wmManipulatorProperty *WM_manipulator_target_property_at_index( + struct wmManipulator *mpr, int index); +struct wmManipulatorProperty *WM_manipulator_target_property_find( + struct wmManipulator *mpr, const char *idname); + +void WM_manipulator_target_property_def_rna_ptr( + struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type, + struct PointerRNA *ptr, struct PropertyRNA *prop, int index); +void WM_manipulator_target_property_def_rna( + struct wmManipulator *mpr, const char *idname, + struct PointerRNA *ptr, const char *propname, int index); + +void WM_manipulator_target_property_def_func_ptr( + struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type, + const struct wmManipulatorPropertyFnParams *params); +void WM_manipulator_target_property_def_func( + struct wmManipulator *mpr, const char *idname, + const struct wmManipulatorPropertyFnParams *params); + +void WM_manipulator_target_property_clear_rna_ptr( + struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type); +void WM_manipulator_target_property_clear_rna( + struct wmManipulator *mpr, const char *idname); + +bool WM_manipulator_target_property_is_valid_any(struct wmManipulator *mpr); +bool WM_manipulator_target_property_is_valid( + const struct wmManipulatorProperty *mpr_prop); +float WM_manipulator_target_property_value_get( + const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop); +void WM_manipulator_target_property_value_set( + struct bContext *C, const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop, + const float value); + +void WM_manipulator_target_property_value_get_array( + const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop, + float *value); +void WM_manipulator_target_property_value_set_array( + struct bContext *C, const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop, + const float *value); + +bool WM_manipulator_target_property_range_get( + const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop, + float range[2]); + +int WM_manipulator_target_property_array_length( + const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop); + +/* definitions */ +const struct wmManipulatorPropertyType *WM_manipulatortype_target_property_find( + const struct wmManipulatorType *wt, const char *idname); +void WM_manipulatortype_target_property_def( + struct wmManipulatorType *wt, const char *idname, int data_type, int array_length); + +/* utilities */ +void WM_manipulator_do_msg_notify_tag_refresh( + struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val); +void WM_manipulator_target_property_subscribe_all( + struct wmManipulator *mpr, struct wmMsgBus *mbus, struct ARegion *ar); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +/* Callbacks for 'wmManipulatorGroupType.setup_keymap' */ +struct wmKeyMap *WM_manipulatorgroup_keymap_common( + const struct wmManipulatorGroupType *wgt, struct wmKeyConfig *config); +struct wmKeyMap *WM_manipulatorgroup_keymap_common_select( + const struct wmManipulatorGroupType *wgt, struct wmKeyConfig *config); + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +struct wmManipulatorMap *WM_manipulatormap_new_from_type( + const struct wmManipulatorMapType_Params *mmap_params); +const struct ListBase *WM_manipulatormap_group_list(struct wmManipulatorMap *mmap); +struct wmManipulatorGroup *WM_manipulatormap_group_find( + struct wmManipulatorMap *mmap, + const char *idname); +struct wmManipulatorGroup *WM_manipulatormap_group_find_ptr( + struct wmManipulatorMap *mmap, + const struct wmManipulatorGroupType *wgt); +void WM_manipulatormap_tag_refresh(struct wmManipulatorMap *mmap); +void WM_manipulatormap_draw( + struct wmManipulatorMap *mmap, const struct bContext *C, const eWM_ManipulatorMapDrawStep drawstep); +void WM_manipulatormap_add_handlers(struct ARegion *ar, struct wmManipulatorMap *mmap); +bool WM_manipulatormap_select_all(struct bContext *C, struct wmManipulatorMap *mmap, const int action); +bool WM_manipulatormap_cursor_set(const struct wmManipulatorMap *mmap, struct wmWindow *win); +void WM_manipulatormap_message_subscribe( + struct bContext *C, struct wmManipulatorMap *mmap, struct ARegion *ar, struct wmMsgBus *mbus); +bool WM_manipulatormap_is_any_selected(const struct wmManipulatorMap *mmap); +bool WM_manipulatormap_minmax( + const struct wmManipulatorMap *mmap, bool use_hidden, bool use_select, + float r_min[3], float r_max[3]); + +struct ARegion *WM_manipulatormap_tooltip_init( + struct bContext *C, struct ARegion *ar, bool *r_exit_on_event); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMapType */ + +struct wmManipulatorMapType *WM_manipulatormaptype_find( + const struct wmManipulatorMapType_Params *mmap_params); +struct wmManipulatorMapType *WM_manipulatormaptype_ensure( + const struct wmManipulatorMapType_Params *mmap_params); + +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find( + struct wmManipulatorMapType *mmap_type, + const char *idname); +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr( + struct wmManipulatorMapType *mmap_type, + const struct wmManipulatorGroupType *wgt); +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link( + struct wmManipulatorMapType *mmap_type, + const char *idname); +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr( + struct wmManipulatorMapType *mmap_type, + struct wmManipulatorGroupType *wgt); + +void WM_manipulatormaptype_group_init_runtime_keymap( + const struct Main *bmain, + struct wmManipulatorGroupType *wgt); +void WM_manipulatormaptype_group_init_runtime( + const struct Main *bmain, struct wmManipulatorMapType *mmap_type, + struct wmManipulatorGroupType *wgt); +void WM_manipulatormaptype_group_unlink( + struct bContext *C, struct Main *bmain, struct wmManipulatorMapType *mmap_type, + const struct wmManipulatorGroupType *wgt); + +void WM_manipulatormaptype_group_free(struct wmManipulatorGroupTypeRef *wgt); + +/* -------------------------------------------------------------------- */ +/* ManipulatorGroup */ + +/* Add/Ensure/Remove (High level API) */ + +void WM_manipulator_group_type_add_ptr_ex( + struct wmManipulatorGroupType *wgt, + struct wmManipulatorMapType *mmap_type); +void WM_manipulator_group_type_add_ptr( + struct wmManipulatorGroupType *wgt); +void WM_manipulator_group_type_add(const char *idname); + +void WM_manipulator_group_type_ensure_ptr_ex( + struct wmManipulatorGroupType *wgt, + struct wmManipulatorMapType *mmap_type); +void WM_manipulator_group_type_ensure_ptr( + struct wmManipulatorGroupType *wgt); +void WM_manipulator_group_type_ensure(const char *idname); + +void WM_manipulator_group_type_remove_ptr_ex( + struct Main *bmain, struct wmManipulatorGroupType *wgt, + struct wmManipulatorMapType *mmap_type); +void WM_manipulator_group_type_remove_ptr( + struct Main *bmain, struct wmManipulatorGroupType *wgt); +void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname); + +void WM_manipulator_group_type_unlink_delayed_ptr_ex( + struct wmManipulatorGroupType *wgt, + struct wmManipulatorMapType *mmap_type); +void WM_manipulator_group_type_unlink_delayed_ptr( + struct wmManipulatorGroupType *wgt); +void WM_manipulator_group_type_unlink_delayed(const char *idname); + +/* Utilities */ +bool WM_manipulator_context_check_drawstep(const struct bContext *C, eWM_ManipulatorMapDrawStep step); + +bool WM_manipulator_group_type_poll(const struct bContext *C, const struct wmManipulatorGroupType *wgt); + +#endif /* __WM_GIZMO_API_H__ */ diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h new file mode 100644 index 00000000000..0f6af6db24c --- /dev/null +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -0,0 +1,422 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmo/WM_gizmo_types.h + * \ingroup wm + * + * \name Manipulator Types + * \brief Manipulator defines for external use. + * + * Only included in WM_types.h and lower level files. + */ + + +#ifndef __WM_GIZMO_TYPES_H__ +#define __WM_GIZMO_TYPES_H__ + +#include "BLI_compiler_attrs.h" + +struct wmManipulatorMapType; +struct wmManipulatorGroupType; +struct wmManipulatorGroup; +struct wmManipulator; +struct wmManipulatorProperty; +struct wmKeyConfig; + +#include "DNA_listBase.h" + + +/* -------------------------------------------------------------------- */ +/* Enum Typedef's */ + + +/** + * #wmManipulator.state + */ +typedef enum eWM_ManipulatorState { + WM_MANIPULATOR_STATE_HIGHLIGHT = (1 << 0), /* while hovered */ + WM_MANIPULATOR_STATE_MODAL = (1 << 1), /* while dragging */ + WM_MANIPULATOR_STATE_SELECT = (1 << 2), +} eWM_ManipulatorState; + + +/** + * #wmManipulator.flag + * Flags for individual manipulators. + */ +typedef enum eWM_ManipulatorFlag { + WM_MANIPULATOR_DRAW_HOVER = (1 << 0), /* draw *only* while hovering */ + WM_MANIPULATOR_DRAW_MODAL = (1 << 1), /* draw while dragging */ + WM_MANIPULATOR_DRAW_VALUE = (1 << 2), /* draw an indicator for the current value while dragging */ + WM_MANIPULATOR_HIDDEN = (1 << 3), + /** + * When set 'scale_final' value also scales the offset. + * Use when offset is to avoid screen-space overlap instead of absolute positioning. */ + WM_MANIPULATOR_DRAW_OFFSET_SCALE = (1 << 4), + /** + * User should still use 'scale_final' for any handles and UI elements. + * This simply skips scale when calculating the final matrix. + * Needed when the manipulator needs to align with the interface underneath it. */ + WM_MANIPULATOR_DRAW_NO_SCALE = (1 << 5), + /** + * Hide the cursor and lock it's position while interacting with this manipulator. + */ + WM_MANIPULATOR_GRAB_CURSOR = (1 << 6), + /** Don't write into the depth buffer when selecting. */ + WM_MANIPULATOR_SELECT_BACKGROUND = (1 << 7), +} eWM_ManipulatorFlag; + +/** + * #wmManipulatorGroupType.flag + * Flags that influence the behavior of all manipulators in the group. + */ +typedef enum eWM_ManipulatorGroupTypeFlag { + /* Mark manipulator-group as being 3D */ + WM_MANIPULATORGROUPTYPE_3D = (1 << 0), + /* Scale manipulators as 3D object that respects zoom (otherwise zoom independent draw size). + * note: currently only for 3D views, 2D support needs adding. */ + WM_MANIPULATORGROUPTYPE_SCALE = (1 << 1), + /* Manipulators can be depth culled with scene objects (covered by other geometry - TODO) */ + WM_MANIPULATORGROUPTYPE_DEPTH_3D = (1 << 2), + /* Manipulators can be selected */ + WM_MANIPULATORGROUPTYPE_SELECT = (1 << 3), + /* The manipulator group is to be kept (not removed on loading a new file for eg). */ + WM_MANIPULATORGROUPTYPE_PERSISTENT = (1 << 4), + /* Show all other manipulators when interacting. */ + WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL = (1 << 5), +} eWM_ManipulatorGroupTypeFlag; + + +/** + * #wmManipulatorGroup.init_flag + */ +typedef enum eWM_ManipulatorGroupInitFlag { + /* mgroup has been initialized */ + WM_MANIPULATORGROUP_INIT_SETUP = (1 << 0), + WM_MANIPULATORGROUP_INIT_REFRESH = (1 << 1), +} eWM_ManipulatorGroupInitFlag; + +/** + * #wmManipulatorMapType.type_update_flag + * Manipulator-map type update flag + */ +typedef enum eWM_ManipulatorMapTypeUpdateFlag { + /* A new type has been added, needs to be initialized for all views. */ + WM_MANIPULATORMAPTYPE_UPDATE_INIT = (1 << 0), + WM_MANIPULATORMAPTYPE_UPDATE_REMOVE = (1 << 1), + + /* Needed because keymap may be registered before and after window initialization. + * So we need to keep track of keymap initialization separately. */ + WM_MANIPULATORMAPTYPE_KEYMAP_INIT = (1 << 2), +} eWM_ManipulatorMapTypeUpdateFlag; + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + +/** + * \brief Manipulator tweak flag. + * Bitflag passed to manipulator while tweaking. + * + * \note Manipulators are responsible for handling this #wmManipulator.modal callback!. + */ +typedef enum { + /* Drag with extra precision (Shift). */ + WM_MANIPULATOR_TWEAK_PRECISE = (1 << 0), + /* Drag with snap enabled (Ctrl). */ + WM_MANIPULATOR_TWEAK_SNAP = (1 << 1), +} eWM_ManipulatorTweak; + +#include "wm_gizmo_fn.h" + +typedef struct wmManipulatorOpElem { + struct wmOperatorType *type; + /* operator properties if manipulator spawns and controls an operator, + * or owner pointer if manipulator spawns and controls a property */ + PointerRNA ptr; + + bool is_redo; +} wmManipulatorOpElem; + +/* manipulators are set per region by registering them on manipulator-maps */ +struct wmManipulator { + struct wmManipulator *next, *prev; + + /* While we don't have a real type, use this to put type-like vars. */ + const struct wmManipulatorType *type; + + /* Overrides 'type->modal' when set. + * Note that this is a workaround, remove if we can. */ + wmManipulatorFnModal custom_modal; + + /* pointer back to group this manipulator is in (just for quick access) */ + struct wmManipulatorGroup *parent_mgroup; + + void *py_instance; + + /* rna pointer to access properties */ + struct PointerRNA *ptr; + + /* flags that influence the behavior or how the manipulators are drawn */ + eWM_ManipulatorFlag flag; + /* state flags (active, highlighted, selected) */ + eWM_ManipulatorState state; + + /* Optional ID for highlighting different parts of this manipulator. + * -1 when unset, otherwise a valid index. (Used as index to 'op_data'). */ + int highlight_part; + /* For single click button manipulators, use a different part as a fallback, -1 when unused. */ + int drag_part; + + /* Transformation of the manipulator in 2d or 3d space. + * - Matrix axis are expected to be unit length (scale is applied after). + * - Behavior when axis aren't orthogonal depends on each manipulator. + * - Typically the +Z is the primary axis for manipulators to use. + * - 'matrix[3]' must be used for location, + * besides this it's up to the manipulators internal code how the + * rotation components are used for drawing and interaction. + */ + + /* The space this manipulator is being modified in. */ + float matrix_space[4][4]; + /* Transformation of this manipulator. */ + float matrix_basis[4][4]; + /* custom offset from origin */ + float matrix_offset[4][4]; + /* runtime property, set the scale while drawing on the viewport */ + float scale_final; + /* user defined scale, in addition to the original one */ + float scale_basis; + /* user defined width for line drawing */ + float line_width; + /* manipulator colors (uses default fallbacks if not defined) */ + float color[4], color_hi[4]; + + /* data used during interaction */ + void *interaction_data; + + /* Operator to spawn when activating the manipulator (overrides property editing), + * an array of items (aligned with #wmManipulator.highlight_part). */ + wmManipulatorOpElem *op_data; + int op_data_len; + + struct IDProperty *properties; + + /* over alloc target_properties after 'wmManipulatorType.struct_size' */ +}; + +/* Similar to PropertyElemRNA, but has an identifier. */ +typedef struct wmManipulatorProperty { + const struct wmManipulatorPropertyType *type; + + PointerRNA ptr; + PropertyRNA *prop; + int index; + + + /* Optional functions for converting to/from RNA */ + struct { + wmManipulatorPropertyFnGet value_get_fn; + wmManipulatorPropertyFnSet value_set_fn; + wmManipulatorPropertyFnRangeGet range_get_fn; + wmManipulatorPropertyFnFree free_fn; + void *user_data; + } custom_func; +} wmManipulatorProperty; + +typedef struct wmManipulatorPropertyType { + struct wmManipulatorPropertyType *next, *prev; + /* PropertyType, typically 'PROP_FLOAT' */ + int data_type; + int array_length; + + /* index within 'wmManipulatorType' */ + int index_in_type; + + /* over alloc */ + char idname[0]; +} wmManipulatorPropertyType; + + +/** + * Simple utility wrapper for storing a single manipulator as wmManipulatorGroup.customdata (which gets freed). + */ +typedef struct wmManipulatorWrapper { + struct wmManipulator *manipulator; +} wmManipulatorWrapper; + +struct wmManipulatorMapType_Params { + short spaceid; + short regionid; +}; + +typedef struct wmManipulatorType { + + const char *idname; /* MAX_NAME */ + + /* Set to 'sizeof(wmManipulator)' or larger for instances of this type, + * use so we can cant to other types without the hassle of a custom-data pointer. */ + uint struct_size; + + /* Initialize struct (calloc'd 'struct_size' region). */ + wmManipulatorFnSetup setup; + + /* draw manipulator */ + wmManipulatorFnDraw draw; + + /* determines 3d intersection by rendering the manipulator in a selection routine. */ + wmManipulatorFnDrawSelect draw_select; + + /* Determine if the mouse intersects with the manipulator. + * The calculation should be done in the callback itself, -1 for no seleciton. */ + wmManipulatorFnTestSelect test_select; + + /* handler used by the manipulator. Usually handles interaction tied to a manipulator type */ + wmManipulatorFnModal modal; + + /* manipulator-specific handler to update manipulator attributes based on the property value */ + wmManipulatorFnPropertyUpdate property_update; + + /* Returns the final transformation which may be different from the 'matrix', + * depending on the manipulator. + * Notes: + * - Scale isn't applied (wmManipulator.scale/user_scale). + * - Offset isn't applied (wmManipulator.matrix_offset). + */ + wmManipulatorFnMatrixBasisGet matrix_basis_get; + + /* activate a manipulator state when the user clicks on it */ + wmManipulatorFnInvoke invoke; + + /* called when manipulator tweaking is done - used to free data and reset property when cancelling */ + wmManipulatorFnExit exit; + + wmManipulatorFnCursorGet cursor_get; + + /* called when manipulator selection state changes */ + wmManipulatorFnSelectRefresh select_refresh; + + /* Free data (not the manipulator it's self), use when the manipulator allocates it's own members. */ + wmManipulatorFnFree free; + + /* RNA for properties */ + struct StructRNA *srna; + + /* RNA integration */ + ExtensionRNA ext; + + ListBase target_property_defs; + int target_property_defs_len; + +} wmManipulatorType; + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +/* factory class for a manipulator-group type, gets called every time a new area is spawned */ +typedef struct wmManipulatorGroupTypeRef { + struct wmManipulatorGroupTypeRef *next, *prev; + struct wmManipulatorGroupType *type; +} wmManipulatorGroupTypeRef; + +/* factory class for a manipulator-group type, gets called every time a new area is spawned */ +typedef struct wmManipulatorGroupType { + const char *idname; /* MAX_NAME */ + const char *name; /* manipulator-group name - displayed in UI (keymap editor) */ + char owner_id[64]; /* MAX_NAME */ + + /* poll if manipulator-map should be visible */ + wmManipulatorGroupFnPoll poll; + /* initially create manipulators and set permanent data - stuff you only need to do once */ + wmManipulatorGroupFnInit setup; + /* refresh data, only called if recreate flag is set (WM_manipulatormap_tag_refresh) */ + wmManipulatorGroupFnRefresh refresh; + /* refresh data for drawing, called before each redraw */ + wmManipulatorGroupFnDrawPrepare draw_prepare; + + /* Keymap init callback for this manipulator-group (optional), + * will fall back to default tweak keymap when left NULL. */ + wmManipulatorGroupFnSetupKeymap setup_keymap; + + /* Optionally subscribe to wmMsgBus events, + * these are calculated automatically from RNA properties, + * only needed if manipulators depend indirectly on properties. */ + wmManipulatorGroupFnMsgBusSubscribe message_subscribe; + + /* keymap created with callback from above */ + struct wmKeyMap *keymap; + /* Only for convenient removal. */ + struct wmKeyConfig *keyconf; + + /* Disable for now, maybe some day we want properties. */ +#if 0 + /* rna for properties */ + struct StructRNA *srna; +#endif + + /* RNA integration */ + ExtensionRNA ext; + + eWM_ManipulatorGroupTypeFlag flag; + + /* So we know which group type to update. */ + eWM_ManipulatorMapTypeUpdateFlag type_update_flag; + + /* same as manipulator-maps, so registering/unregistering goes to the correct region */ + struct wmManipulatorMapType_Params mmap_params; + +} wmManipulatorGroupType; + +typedef struct wmManipulatorGroup { + struct wmManipulatorGroup *next, *prev; + + struct wmManipulatorGroupType *type; + ListBase manipulators; + + struct wmManipulatorMap *parent_mmap; + + void *py_instance; /* python stores the class instance here */ + struct ReportList *reports; /* errors and warnings storage */ + + void *customdata; + void (*customdata_free)(void *); /* for freeing customdata from above */ + eWM_ManipulatorGroupInitFlag init_flag; +} wmManipulatorGroup; + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +/** + * Pass a value of this enum to #WM_manipulatormap_draw to tell it what to draw. + */ +typedef enum eWM_ManipulatorMapDrawStep { + /** Draw 2D manipulator-groups (#WM_MANIPULATORGROUPTYPE_3D not set). */ + WM_MANIPULATORMAP_DRAWSTEP_2D = 0, + /** Draw 3D manipulator-groups (#WM_MANIPULATORGROUPTYPE_3D set). */ + WM_MANIPULATORMAP_DRAWSTEP_3D, +} eWM_ManipulatorMapDrawStep; +#define WM_MANIPULATORMAP_DRAWSTEP_MAX 2 + +#endif /* __WM_GIZMO_TYPES_H__ */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c new file mode 100644 index 00000000000..bef193f6c56 --- /dev/null +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c @@ -0,0 +1,800 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmo/intern/wm_gizmo.c + * \ingroup wm + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "BKE_context.h" + +#include "GPU_batch.h" +#include "GPU_glew.h" +#include "GPU_immediate.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_idprop.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "UI_interface.h" + +#ifdef WITH_PYTHON +#include "BPY_extern.h" +#endif + +/* only for own init/exit calls (wm_manipulatortype_init/wm_manipulatortype_free) */ +#include "wm.h" + +/* own includes */ +#include "wm_gizmo_wmapi.h" +#include "wm_gizmo_intern.h" + +static void wm_manipulator_register( + wmManipulatorGroup *mgroup, wmManipulator *mpr); + +/** + * \note Follow #wm_operator_create convention. + */ +static wmManipulator *wm_manipulator_create( + const wmManipulatorType *wt, + PointerRNA *properties) +{ + BLI_assert(wt != NULL); + BLI_assert(wt->struct_size >= sizeof(wmManipulator)); + + wmManipulator *mpr = MEM_callocN( + wt->struct_size + (sizeof(wmManipulatorProperty) * wt->target_property_defs_len), __func__); + mpr->type = wt; + + /* initialize properties, either copy or create */ + mpr->ptr = MEM_callocN(sizeof(PointerRNA), "wmManipulatorPtrRNA"); + if (properties && properties->data) { + mpr->properties = IDP_CopyProperty(properties->data); + } + else { + IDPropertyTemplate val = {0}; + mpr->properties = IDP_New(IDP_GROUP, &val, "wmManipulatorProperties"); + } + RNA_pointer_create(G_MAIN->wm.first, wt->srna, mpr->properties, mpr->ptr); + + WM_manipulator_properties_sanitize(mpr->ptr, 0); + + unit_m4(mpr->matrix_space); + unit_m4(mpr->matrix_basis); + unit_m4(mpr->matrix_offset); + + mpr->drag_part = -1; + + return mpr; +} + +wmManipulator *WM_manipulator_new_ptr( + const wmManipulatorType *wt, wmManipulatorGroup *mgroup, + PointerRNA *properties) +{ + wmManipulator *mpr = wm_manipulator_create(wt, properties); + + wm_manipulator_register(mgroup, mpr); + + if (mpr->type->setup != NULL) { + mpr->type->setup(mpr); + } + + return mpr; +} + +/** + * \param wt: Must be valid, + * if you need to check it exists use #WM_manipulator_new_ptr + * because callers of this function don't NULL check the return value. + */ +wmManipulator *WM_manipulator_new( + const char *idname, wmManipulatorGroup *mgroup, + PointerRNA *properties) +{ + const wmManipulatorType *wt = WM_manipulatortype_find(idname, false); + return WM_manipulator_new_ptr(wt, mgroup, properties); +} + +/** + * Initialize default values and allocate needed memory for members. + */ +static void manipulator_init(wmManipulator *mpr) +{ + const float color_default[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + mpr->scale_basis = 1.0f; + mpr->line_width = 1.0f; + + /* defaults */ + copy_v4_v4(mpr->color, color_default); + copy_v4_v4(mpr->color_hi, color_default); +} + +/** + * Register \a manipulator. + * + * \param name: name used to create a unique idname for \a manipulator in \a mgroup + * + * \note Not to be confused with type registration from RNA. + */ +static void wm_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr) +{ + manipulator_init(mpr); + wm_manipulatorgroup_manipulator_register(mgroup, mpr); +} + +/** + * \warning this doesn't check #wmManipulatorMap (highlight, selection etc). + * Typical use is when freeing the windowing data, + * where caller can manage clearing selection, highlight... etc. + */ +void WM_manipulator_free(wmManipulator *mpr) +{ + if (mpr->type->free != NULL) { + mpr->type->free(mpr); + } + +#ifdef WITH_PYTHON + if (mpr->py_instance) { + /* do this first in case there are any __del__ functions or + * similar that use properties */ + BPY_DECREF_RNA_INVALIDATE(mpr->py_instance); + } +#endif + + if (mpr->op_data) { + for (int i = 0; i < mpr->op_data_len; i++) { + WM_operator_properties_free(&mpr->op_data[i].ptr); + } + MEM_freeN(mpr->op_data); + } + + if (mpr->ptr != NULL) { + WM_manipulator_properties_free(mpr->ptr); + MEM_freeN(mpr->ptr); + } + + if (mpr->type->target_property_defs_len != 0) { + wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr); + for (int i = 0; i < mpr->type->target_property_defs_len; i++) { + wmManipulatorProperty *mpr_prop = &mpr_prop_array[i]; + if (mpr_prop->custom_func.free_fn) { + mpr_prop->custom_func.free_fn(mpr, mpr_prop); + } + } + } + + MEM_freeN(mpr); +} + +/** + * Free \a manipulator and unlink from \a manipulatorlist. + * \a manipulatorlist is allowed to be NULL. + */ +void WM_manipulator_unlink(ListBase *manipulatorlist, wmManipulatorMap *mmap, wmManipulator *mpr, bContext *C) +{ + if (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) { + wm_manipulatormap_highlight_set(mmap, C, NULL, 0); + } + if (mpr->state & WM_MANIPULATOR_STATE_MODAL) { + wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false); + } + /* Unlink instead of setting so we don't run callbacks. */ + if (mpr->state & WM_MANIPULATOR_STATE_SELECT) { + WM_manipulator_select_unlink(mmap, mpr); + } + + if (manipulatorlist) { + BLI_remlink(manipulatorlist, mpr); + } + + BLI_assert(mmap->mmap_context.highlight != mpr); + BLI_assert(mmap->mmap_context.modal != mpr); + + WM_manipulator_free(mpr); +} + +/* -------------------------------------------------------------------- */ +/** \name Manipulator Creation API + * + * API for defining data on manipulator creation. + * + * \{ */ + +struct wmManipulatorOpElem *WM_manipulator_operator_get( + wmManipulator *mpr, int part_index) +{ + if (mpr->op_data && ((part_index >= 0) && (part_index < mpr->op_data_len))) { + return &mpr->op_data[part_index]; + } + return NULL; +} + +PointerRNA *WM_manipulator_operator_set( + wmManipulator *mpr, int part_index, + wmOperatorType *ot, IDProperty *properties) +{ + BLI_assert(part_index < 255); + /* We could pre-allocate these but using multiple is such a rare thing. */ + if (part_index >= mpr->op_data_len) { + mpr->op_data_len = part_index + 1; + mpr->op_data = MEM_recallocN(mpr->op_data, sizeof(*mpr->op_data) * mpr->op_data_len); + } + wmManipulatorOpElem *mpop = &mpr->op_data[part_index]; + mpop->type = ot; + + if (mpop->ptr.data) { + WM_operator_properties_free(&mpop->ptr); + } + WM_operator_properties_create_ptr(&mpop->ptr, ot); + + if (properties) { + mpop->ptr.data = properties; + } + + return &mpop->ptr; +} + +static void wm_manipulator_set_matrix_rotation_from_z_axis__internal( + float matrix[4][4], const float z_axis[3]) +{ + /* old code, seems we can use simpler method */ +#if 0 + const float z_global[3] = {0.0f, 0.0f, 1.0f}; + float rot[3][3]; + + rotation_between_vecs_to_mat3(rot, z_global, z_axis); + copy_v3_v3(matrix[0], rot[0]); + copy_v3_v3(matrix[1], rot[1]); + copy_v3_v3(matrix[2], rot[2]); +#else + normalize_v3_v3(matrix[2], z_axis); + ortho_basis_v3v3_v3(matrix[0], matrix[1], matrix[2]); +#endif + +} + +static void wm_manipulator_set_matrix_rotation_from_yz_axis__internal( + float matrix[4][4], const float y_axis[3], const float z_axis[3]) +{ + normalize_v3_v3(matrix[1], y_axis); + normalize_v3_v3(matrix[2], z_axis); + cross_v3_v3v3(matrix[0], matrix[1], matrix[2]); + normalize_v3(matrix[0]); +} + +/** + * wmManipulator.matrix utils. + */ +void WM_manipulator_set_matrix_rotation_from_z_axis( + wmManipulator *mpr, const float z_axis[3]) +{ + wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_basis, z_axis); +} +void WM_manipulator_set_matrix_rotation_from_yz_axis( + wmManipulator *mpr, const float y_axis[3], const float z_axis[3]) +{ + wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_basis, y_axis, z_axis); +} +void WM_manipulator_set_matrix_location(wmManipulator *mpr, const float origin[3]) +{ + copy_v3_v3(mpr->matrix_basis[3], origin); +} + +/** + * wmManipulator.matrix_offset utils. + */ +void WM_manipulator_set_matrix_offset_rotation_from_z_axis( + wmManipulator *mpr, const float z_axis[3]) +{ + wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_offset, z_axis); +} +void WM_manipulator_set_matrix_offset_rotation_from_yz_axis( + wmManipulator *mpr, const float y_axis[3], const float z_axis[3]) +{ + wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_offset, y_axis, z_axis); +} +void WM_manipulator_set_matrix_offset_location(wmManipulator *mpr, const float offset[3]) +{ + copy_v3_v3(mpr->matrix_offset[3], offset); +} + +void WM_manipulator_set_flag(wmManipulator *mpr, const int flag, const bool enable) +{ + if (enable) { + mpr->flag |= flag; + } + else { + mpr->flag &= ~flag; + } +} + +void WM_manipulator_set_scale(wmManipulator *mpr, const float scale) +{ + mpr->scale_basis = scale; +} + +void WM_manipulator_set_line_width(wmManipulator *mpr, const float line_width) +{ + mpr->line_width = line_width; +} + +/** + * Set manipulator rgba colors. + * + * \param col Normal state color. + * \param col_hi Highlighted state color. + */ +void WM_manipulator_get_color(const wmManipulator *mpr, float color[4]) +{ + copy_v4_v4(color, mpr->color); +} +void WM_manipulator_set_color(wmManipulator *mpr, const float color[4]) +{ + copy_v4_v4(mpr->color, color); +} + +void WM_manipulator_get_color_highlight(const wmManipulator *mpr, float color_hi[4]) +{ + copy_v4_v4(color_hi, mpr->color_hi); +} +void WM_manipulator_set_color_highlight(wmManipulator *mpr, const float color_hi[4]) +{ + copy_v4_v4(mpr->color_hi, color_hi); +} + + +/** \} */ // Manipulator Creation API + + +/* -------------------------------------------------------------------- */ +/** \name Manipulator Callback Assignment + * + * \{ */ + +void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn) +{ + mpr->custom_modal = fn; +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** + * Add/Remove \a manipulator to selection. + * Reallocates memory for selected manipulators so better not call for selecting multiple ones. + * + * \return if the selection has changed. + */ +bool wm_manipulator_select_set_ex( + wmManipulatorMap *mmap, wmManipulator *mpr, bool select, + bool use_array, bool use_callback) +{ + bool changed = false; + + if (select) { + if ((mpr->state & WM_MANIPULATOR_STATE_SELECT) == 0) { + if (use_array) { + wm_manipulatormap_select_array_push_back(mmap, mpr); + } + mpr->state |= WM_MANIPULATOR_STATE_SELECT; + changed = true; + } + } + else { + if (mpr->state & WM_MANIPULATOR_STATE_SELECT) { + if (use_array) { + wm_manipulatormap_select_array_remove(mmap, mpr); + } + mpr->state &= ~WM_MANIPULATOR_STATE_SELECT; + changed = true; + } + } + + /* In the case of unlinking we only want to remove from the array + * and not write to the external state */ + if (use_callback && changed) { + if (mpr->type->select_refresh) { + mpr->type->select_refresh(mpr); + } + } + + return changed; +} + +/* Remove from selection array without running callbacks. */ +bool WM_manipulator_select_unlink(wmManipulatorMap *mmap, wmManipulator *mpr) +{ + return wm_manipulator_select_set_ex(mmap, mpr, false, true, false); +} + +bool WM_manipulator_select_set(wmManipulatorMap *mmap, wmManipulator *mpr, bool select) +{ + return wm_manipulator_select_set_ex(mmap, mpr, select, true, true); +} + +void WM_manipulator_highlight_set(wmManipulatorMap *mmap, wmManipulator *mpr) +{ + wm_manipulatormap_highlight_set(mmap, NULL, mpr, mpr ? mpr->highlight_part : 0); +} + +bool wm_manipulator_select_and_highlight(bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr) +{ + if (WM_manipulator_select_set(mmap, mpr, true)) { + wm_manipulatormap_highlight_set(mmap, C, mpr, mpr->highlight_part); + return true; + } + else { + return false; + } +} + +/** + * Special function to run from setup so manipulators start out interactive. + * + * We could do this when linking them, but this complicates things since the window update code needs to run first. + */ +void WM_manipulator_modal_set_from_setup( + struct wmManipulatorMap *mmap, struct bContext *C, + struct wmManipulator *mpr, int part_index, const wmEvent *event) +{ + mpr->highlight_part = part_index; + WM_manipulator_highlight_set(mmap, mpr); + if (false) { + wm_manipulatormap_modal_set(mmap, C, mpr, event, true); + } + else { + /* WEAK: but it works. */ + WM_operator_name_call(C, "MANIPULATORGROUP_OT_manipulator_tweak", WM_OP_INVOKE_DEFAULT, NULL); + } +} + +void wm_manipulator_calculate_scale(wmManipulator *mpr, const bContext *C) +{ + const RegionView3D *rv3d = CTX_wm_region_view3d(C); + float scale = UI_DPI_FAC; + + if ((mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCALE) == 0) { + scale *= U.manipulator_size; + if (rv3d) { + /* 'ED_view3d_pixel_size' includes 'U.pixelsize', remove it. */ + float matrix_world[4][4]; + if (mpr->type->matrix_basis_get) { + float matrix_basis[4][4]; + mpr->type->matrix_basis_get(mpr, matrix_basis); + mul_m4_m4m4(matrix_world, mpr->matrix_space, matrix_basis); + } + else { + mul_m4_m4m4(matrix_world, mpr->matrix_space, mpr->matrix_basis); + } + + /* Exclude matrix_offset from scale. */ + scale *= ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_world[3]); + } + else { + scale *= 0.02f; + } + } + + mpr->scale_final = mpr->scale_basis * scale; +} + +static void manipulator_update_prop_data(wmManipulator *mpr) +{ + /* manipulator property might have been changed, so update manipulator */ + if (mpr->type->property_update) { + wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr); + for (int i = 0; i < mpr->type->target_property_defs_len; i++) { + wmManipulatorProperty *mpr_prop = &mpr_prop_array[i]; + if (WM_manipulator_target_property_is_valid(mpr_prop)) { + mpr->type->property_update(mpr, mpr_prop); + } + } + } +} + +void wm_manipulator_update(wmManipulator *mpr, const bContext *C, const bool refresh_map) +{ + if (refresh_map) { + manipulator_update_prop_data(mpr); + } + wm_manipulator_calculate_scale(mpr, C); +} + +int wm_manipulator_is_visible(wmManipulator *mpr) +{ + if (mpr->flag & WM_MANIPULATOR_HIDDEN) { + return 0; + } + if ((mpr->state & WM_MANIPULATOR_STATE_MODAL) && + !(mpr->flag & (WM_MANIPULATOR_DRAW_MODAL | WM_MANIPULATOR_DRAW_VALUE))) + { + /* don't draw while modal (dragging) */ + return 0; + } + if ((mpr->flag & WM_MANIPULATOR_DRAW_HOVER) && + !(mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) && + !(mpr->state & WM_MANIPULATOR_STATE_SELECT)) /* still draw selected manipulators */ + { + /* update but don't draw */ + return WM_MANIPULATOR_IS_VISIBLE_UPDATE; + } + + return WM_MANIPULATOR_IS_VISIBLE_UPDATE | WM_MANIPULATOR_IS_VISIBLE_DRAW; +} + +void WM_manipulator_calc_matrix_final_params( + const wmManipulator *mpr, + const struct WM_ManipulatorMatrixParams *params, + float r_mat[4][4]) +{ + const float (* const matrix_space)[4] = params->matrix_space ? params->matrix_space : mpr->matrix_space; + const float (* const matrix_basis)[4] = params->matrix_basis ? params->matrix_basis : mpr->matrix_basis; + const float (* const matrix_offset)[4] = params->matrix_offset ? params->matrix_offset : mpr->matrix_offset; + const float *scale_final = params->scale_final ? params->scale_final : &mpr->scale_final; + + float final_matrix[4][4]; + if (params->matrix_basis == NULL && mpr->type->matrix_basis_get) { + mpr->type->matrix_basis_get(mpr, final_matrix); + } + else { + copy_m4_m4(final_matrix, matrix_basis); + } + + if (mpr->flag & WM_MANIPULATOR_DRAW_NO_SCALE) { + mul_m4_m4m4(final_matrix, final_matrix, matrix_offset); + } + else { + if (mpr->flag & WM_MANIPULATOR_DRAW_OFFSET_SCALE) { + mul_mat3_m4_fl(final_matrix, *scale_final); + mul_m4_m4m4(final_matrix, final_matrix, matrix_offset); + } + else { + mul_m4_m4m4(final_matrix, final_matrix, matrix_offset); + mul_mat3_m4_fl(final_matrix, *scale_final); + } + } + + mul_m4_m4m4(r_mat, matrix_space, final_matrix); +} + +void WM_manipulator_calc_matrix_final_no_offset(const wmManipulator *mpr, float r_mat[4][4]) +{ + float mat_identity[4][4]; + unit_m4(mat_identity); + + WM_manipulator_calc_matrix_final_params( + mpr, + &((struct WM_ManipulatorMatrixParams) { + .matrix_space = NULL, + .matrix_basis = NULL, + .matrix_offset = mat_identity, + .scale_final = NULL, + }), r_mat + ); +} + +void WM_manipulator_calc_matrix_final(const wmManipulator *mpr, float r_mat[4][4]) +{ + WM_manipulator_calc_matrix_final_params( + mpr, + &((struct WM_ManipulatorMatrixParams) { + .matrix_space = NULL, + .matrix_basis = NULL, + .matrix_offset = NULL, + .scale_final = NULL, + }), r_mat + ); +} + +/** \name Manipulator Propery Access + * + * Matches `WM_operator_properties` conventions. + * + * \{ */ + + +void WM_manipulator_properties_create_ptr(PointerRNA *ptr, wmManipulatorType *wt) +{ + RNA_pointer_create(NULL, wt->srna, NULL, ptr); +} + +void WM_manipulator_properties_create(PointerRNA *ptr, const char *wtstring) +{ + const wmManipulatorType *wt = WM_manipulatortype_find(wtstring, false); + + if (wt) + WM_manipulator_properties_create_ptr(ptr, (wmManipulatorType *)wt); + else + RNA_pointer_create(NULL, &RNA_ManipulatorProperties, NULL, ptr); +} + +/* similar to the function above except its uses ID properties + * used for keymaps and macros */ +void WM_manipulator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *wtstring) +{ + if (*properties == NULL) { + IDPropertyTemplate val = {0}; + *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp"); + } + + if (*ptr == NULL) { + *ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr"); + WM_manipulator_properties_create(*ptr, wtstring); + } + + (*ptr)->data = *properties; + +} + +void WM_manipulator_properties_sanitize(PointerRNA *ptr, const bool no_context) +{ + RNA_STRUCT_BEGIN (ptr, prop) + { + switch (RNA_property_type(prop)) { + case PROP_ENUM: + if (no_context) + RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT); + else + RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT); + break; + case PROP_POINTER: + { + StructRNA *ptype = RNA_property_pointer_type(ptr, prop); + + /* recurse into manipulator properties */ + if (RNA_struct_is_a(ptype, &RNA_ManipulatorProperties)) { + PointerRNA opptr = RNA_property_pointer_get(ptr, prop); + WM_manipulator_properties_sanitize(&opptr, no_context); + } + break; + } + default: + break; + } + } + RNA_STRUCT_END; +} + + +/** set all props to their default, + * \param do_update Only update un-initialized props. + * + * \note, theres nothing specific to manipulators here. + * this could be made a general function. + */ +bool WM_manipulator_properties_default(PointerRNA *ptr, const bool do_update) +{ + bool changed = false; + RNA_STRUCT_BEGIN (ptr, prop) + { + switch (RNA_property_type(prop)) { + case PROP_POINTER: + { + StructRNA *ptype = RNA_property_pointer_type(ptr, prop); + if (ptype != &RNA_Struct) { + PointerRNA opptr = RNA_property_pointer_get(ptr, prop); + changed |= WM_manipulator_properties_default(&opptr, do_update); + } + break; + } + default: + if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) { + if (RNA_property_reset(ptr, prop, -1)) { + changed = true; + } + } + break; + } + } + RNA_STRUCT_END; + + return changed; +} + +/* remove all props without PROP_SKIP_SAVE */ +void WM_manipulator_properties_reset(wmManipulator *mpr) +{ + if (mpr->ptr->data) { + PropertyRNA *iterprop; + iterprop = RNA_struct_iterator_property(mpr->type->srna); + + RNA_PROP_BEGIN (mpr->ptr, itemptr, iterprop) + { + PropertyRNA *prop = itemptr.data; + + if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) { + const char *identifier = RNA_property_identifier(prop); + RNA_struct_idprops_unset(mpr->ptr, identifier); + } + } + RNA_PROP_END; + } +} + +void WM_manipulator_properties_clear(PointerRNA *ptr) +{ + IDProperty *properties = ptr->data; + + if (properties) { + IDP_ClearProperty(properties); + } +} + +void WM_manipulator_properties_free(PointerRNA *ptr) +{ + IDProperty *properties = ptr->data; + + if (properties) { + IDP_FreeProperty(properties); + MEM_freeN(properties); + ptr->data = NULL; /* just in case */ + } +} + +/** \} */ + +/** \name General Utilities + * + * \{ */ + +bool WM_manipulator_context_check_drawstep(const struct bContext *C, eWM_ManipulatorMapDrawStep step) +{ + switch (step) { + case WM_MANIPULATORMAP_DRAWSTEP_2D: + { + break; + } + case WM_MANIPULATORMAP_DRAWSTEP_3D: + { + wmWindowManager *wm = CTX_wm_manager(C); + if (ED_screen_animation_playing(wm)) { + return false; + } + break; + } + } + return true; +} + +/** \} */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c new file mode 100644 index 00000000000..83af1a71163 --- /dev/null +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -0,0 +1,949 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmo/intern/wm_gizmo_group.c + * \ingroup wm + * + * \name Manipulator-Group + * + * Manipulator-groups store and manage groups of manipulators. They can be + * attached to modal handlers and have own keymaps. + */ + +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_workspace.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm_event_system.h" + +#include "ED_screen.h" +#include "ED_undo.h" + +/* own includes */ +#include "wm_gizmo_wmapi.h" +#include "wm_gizmo_intern.h" + +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + +/* Allow manipulator part's to be single click only, + * dragging falls back to activating their 'drag_part' action. */ +#define USE_DRAG_DETECT + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorGroup + * + * \{ */ + +/** + * Create a new manipulator-group from \a wgt. + */ +wmManipulatorGroup *wm_manipulatorgroup_new_from_type( + wmManipulatorMap *mmap, wmManipulatorGroupType *wgt) +{ + wmManipulatorGroup *mgroup = MEM_callocN(sizeof(*mgroup), "manipulator-group"); + mgroup->type = wgt; + + /* keep back-link */ + mgroup->parent_mmap = mmap; + + BLI_addtail(&mmap->groups, mgroup); + + return mgroup; +} + +void wm_manipulatorgroup_free(bContext *C, wmManipulatorGroup *mgroup) +{ + wmManipulatorMap *mmap = mgroup->parent_mmap; + + /* Similar to WM_manipulator_unlink, but only to keep mmap state correct, + * we don't want to run callbacks. */ + if (mmap->mmap_context.highlight && mmap->mmap_context.highlight->parent_mgroup == mgroup) { + wm_manipulatormap_highlight_set(mmap, C, NULL, 0); + } + if (mmap->mmap_context.modal && mmap->mmap_context.modal->parent_mgroup == mgroup) { + wm_manipulatormap_modal_set(mmap, C, mmap->mmap_context.modal, NULL, false); + } + + for (wmManipulator *mpr = mgroup->manipulators.first, *mpr_next; mpr; mpr = mpr_next) { + mpr_next = mpr->next; + if (mmap->mmap_context.select.len) { + WM_manipulator_select_unlink(mmap, mpr); + } + WM_manipulator_free(mpr); + } + BLI_listbase_clear(&mgroup->manipulators); + +#ifdef WITH_PYTHON + if (mgroup->py_instance) { + /* do this first in case there are any __del__ functions or + * similar that use properties */ + BPY_DECREF_RNA_INVALIDATE(mgroup->py_instance); + } +#endif + + if (mgroup->reports && (mgroup->reports->flag & RPT_FREE)) { + BKE_reports_clear(mgroup->reports); + MEM_freeN(mgroup->reports); + } + + if (mgroup->customdata_free) { + mgroup->customdata_free(mgroup->customdata); + } + else { + MEM_SAFE_FREE(mgroup->customdata); + } + + BLI_remlink(&mmap->groups, mgroup); + + MEM_freeN(mgroup); +} + +/** + * Add \a manipulator to \a mgroup and make sure its name is unique within the group. + */ +void wm_manipulatorgroup_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr) +{ + BLI_assert(BLI_findindex(&mgroup->manipulators, mpr) == -1); + BLI_addtail(&mgroup->manipulators, mpr); + mpr->parent_mgroup = mgroup; +} + +wmManipulator *wm_manipulatorgroup_find_intersected_manipulator( + const wmManipulatorGroup *mgroup, bContext *C, const wmEvent *event, + int *r_part) +{ + for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + if (mpr->type->test_select && (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) { + if ((*r_part = mpr->type->test_select(C, mpr, event)) != -1) { + return mpr; + } + } + } + + return NULL; +} + +/** + * Adds all manipulators of \a mgroup that can be selected to the head of \a listbase. Added items need freeing! + */ +void wm_manipulatorgroup_intersectable_manipulators_to_list(const wmManipulatorGroup *mgroup, ListBase *listbase) +{ + for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + if ((mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) { + if (((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) && mpr->type->draw_select) || + ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0 && mpr->type->test_select)) + { + BLI_addhead(listbase, BLI_genericNodeN(mpr)); + } + } + } +} + +void wm_manipulatorgroup_ensure_initialized(wmManipulatorGroup *mgroup, const bContext *C) +{ + /* prepare for first draw */ + if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0)) { + mgroup->type->setup(C, mgroup); + + /* Not ideal, initialize keymap here, needed for RNA runtime generated manipulators. */ + wmManipulatorGroupType *wgt = mgroup->type; + if (wgt->keymap == NULL) { + wmWindowManager *wm = CTX_wm_manager(C); + wm_manipulatorgrouptype_setup_keymap(wgt, wm->defaultconf); + BLI_assert(wgt->keymap != NULL); + } + mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_SETUP; + } + + /* refresh may be called multiple times, this just ensures its called at least once before we draw. */ + if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_REFRESH) == 0)) { + if (mgroup->type->refresh) { + mgroup->type->refresh(C, mgroup); + } + mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_REFRESH; + } +} + +bool WM_manipulator_group_type_poll(const bContext *C, const struct wmManipulatorGroupType *wgt) +{ + /* If we're tagged, only use compatible. */ + if (wgt->owner_id[0] != '\0') { + const WorkSpace *workspace = CTX_wm_workspace(C); + if (BKE_workspace_owner_id_check(workspace, wgt->owner_id) == false) { + return false; + } + } + /* Check for poll function, if manipulator-group belongs to an operator, also check if the operator is running. */ + return (!wgt->poll || wgt->poll(C, (wmManipulatorGroupType *)wgt)); +} + +bool wm_manipulatorgroup_is_visible_in_drawstep( + const wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep) +{ + switch (drawstep) { + case WM_MANIPULATORMAP_DRAWSTEP_2D: + return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0; + case WM_MANIPULATORMAP_DRAWSTEP_3D: + return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D); + default: + BLI_assert(0); + return false; + } +} + +bool wm_manipulatorgroup_is_any_selected(const wmManipulatorGroup *mgroup) +{ + if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT) { + for (const wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + if (mpr->state & WM_MANIPULATOR_STATE_SELECT) { + return true; + } + } + } + return false; +} + +/** \} */ + +/** \name Manipulator operators + * + * Basic operators for manipulator interaction with user configurable keymaps. + * + * \{ */ + +static int manipulator_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + ARegion *ar = CTX_wm_region(C); + wmManipulatorMap *mmap = ar->manipulator_map; + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + wmManipulator *highlight = mmap->mmap_context.highlight; + + bool extend = RNA_boolean_get(op->ptr, "extend"); + bool deselect = RNA_boolean_get(op->ptr, "deselect"); + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + + /* deselect all first */ + if (extend == false && deselect == false && toggle == false) { + wm_manipulatormap_deselect_all(mmap); + BLI_assert(msel->items == NULL && msel->len == 0); + UNUSED_VARS_NDEBUG(msel); + } + + if (highlight) { + const bool is_selected = (highlight->state & WM_MANIPULATOR_STATE_SELECT); + bool redraw = false; + + if (toggle) { + /* toggle: deselect if already selected, else select */ + deselect = is_selected; + } + + if (deselect) { + if (is_selected && WM_manipulator_select_set(mmap, highlight, false)) { + redraw = true; + } + } + else if (wm_manipulator_select_and_highlight(C, mmap, highlight)) { + redraw = true; + } + + if (redraw) { + ED_region_tag_redraw(ar); + } + + return OPERATOR_FINISHED; + } + else { + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + return OPERATOR_PASS_THROUGH; +} + +void MANIPULATORGROUP_OT_manipulator_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Gizmo Select"; + ot->description = "Select the currently highlighted manipulator"; + ot->idname = "MANIPULATORGROUP_OT_manipulator_select"; + + /* api callbacks */ + ot->invoke = manipulator_select_invoke; + + ot->flag = OPTYPE_UNDO; + + WM_operator_properties_mouse_select(ot); +} + +typedef struct ManipulatorTweakData { + wmManipulatorMap *mmap; + wmManipulatorGroup *mgroup; + wmManipulator *mpr_modal; + + int init_event; /* initial event type */ + int flag; /* tweak flags */ + +#ifdef USE_DRAG_DETECT + /* True until the mouse is moved (only use when the operator has no modal). + * this allows some manipulators to be click-only. */ + enum { + /* Don't detect dragging. */ + DRAG_NOP = 0, + /* Detect dragging (wait until a drag or click is detected). */ + DRAG_DETECT, + /* Drag has started, idle until there is no active modal operator. + * This is needed because finishing the modal operator also exits + * the modal manipulator state (un-grabbs the cursor). + * Ideally this workaround could be removed later. */ + DRAG_IDLE, + } drag_state; +#endif + +} ManipulatorTweakData; + +static bool manipulator_tweak_start( + bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr, const wmEvent *event) +{ + /* activate highlighted manipulator */ + wm_manipulatormap_modal_set(mmap, C, mpr, event, true); + + return (mpr->state & WM_MANIPULATOR_STATE_MODAL); +} + +static bool manipulator_tweak_start_and_finish( + bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr, const wmEvent *event, bool *r_is_modal) +{ + wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part); + if (r_is_modal) { + *r_is_modal = false; + } + if (mpop && mpop->type) { + + /* Undo/Redo */ + if (mpop->is_redo) { + wmWindowManager *wm = CTX_wm_manager(C); + wmOperator *op = WM_operator_last_redo(C); + + /* We may want to enable this, for now the manipulator can manage it's own properties. */ +#if 0 + IDP_MergeGroup(mpop->ptr.data, op->properties, false); +#endif + + WM_operator_free_all_after(wm, op); + ED_undo_pop_op(C, op); + } + + /* XXX temporary workaround for modal manipulator operator + * conflicting with modal operator attached to manipulator */ + if (mpop->type->modal) { + /* activate highlighted manipulator */ + wm_manipulatormap_modal_set(mmap, C, mpr, event, true); + if (r_is_modal) { + *r_is_modal = true; + } + } + else { + /* Allow for 'button' manipulators, single click to run an action. */ + WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr); + } + return true; + } + else { + return false; + } +} + +static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel, bool clear_modal) +{ + ManipulatorTweakData *mtweak = op->customdata; + if (mtweak->mpr_modal->type->exit) { + mtweak->mpr_modal->type->exit(C, mtweak->mpr_modal, cancel); + } + if (clear_modal) { + /* The manipulator may have been removed. */ + if ((BLI_findindex(&mtweak->mmap->groups, mtweak->mgroup) != -1) && + (BLI_findindex(&mtweak->mgroup->manipulators, mtweak->mpr_modal) != -1)) + { + wm_manipulatormap_modal_set(mtweak->mmap, C, mtweak->mpr_modal, NULL, false); + } + } + MEM_freeN(mtweak); +} + +static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ManipulatorTweakData *mtweak = op->customdata; + wmManipulator *mpr = mtweak->mpr_modal; + int retval = OPERATOR_PASS_THROUGH; + bool clear_modal = true; + + if (mpr == NULL) { + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + +#ifdef USE_DRAG_DETECT + wmManipulatorMap *mmap = mtweak->mmap; + if (mtweak->drag_state == DRAG_DETECT) { + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + if (len_manhattan_v2v2_int(&event->x, mmap->mmap_context.event_xy) > 2) { + mtweak->drag_state = DRAG_IDLE; + mpr->highlight_part = mpr->drag_part; + } + } + else if (event->type == mtweak->init_event && event->val == KM_RELEASE) { + mtweak->drag_state = DRAG_NOP; + retval = OPERATOR_FINISHED; + } + + if (mtweak->drag_state != DRAG_DETECT) { + /* Follow logic in 'manipulator_tweak_invoke' */ + bool is_modal = false; + if (manipulator_tweak_start_and_finish(C, mmap, mpr, event, &is_modal)) { + if (is_modal) { + clear_modal = false; + } + } + else { + if (!manipulator_tweak_start(C, mmap, mpr, event)) { + retval = OPERATOR_FINISHED; + } + } + } + } + if (mtweak->drag_state == DRAG_IDLE) { + if (mmap->mmap_context.modal != NULL) { + return OPERATOR_PASS_THROUGH; + } + else { + manipulator_tweak_finish(C, op, false, false); + return OPERATOR_FINISHED; + } + } +#endif /* USE_DRAG_DETECT */ + + if (retval == OPERATOR_FINISHED) { + /* pass */ + } + else if (event->type == mtweak->init_event && event->val == KM_RELEASE) { + retval = OPERATOR_FINISHED; + } + else if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case TWEAK_MODAL_CANCEL: + retval = OPERATOR_CANCELLED; + break; + case TWEAK_MODAL_CONFIRM: + retval = OPERATOR_FINISHED; + break; + case TWEAK_MODAL_PRECISION_ON: + mtweak->flag |= WM_MANIPULATOR_TWEAK_PRECISE; + break; + case TWEAK_MODAL_PRECISION_OFF: + mtweak->flag &= ~WM_MANIPULATOR_TWEAK_PRECISE; + break; + + case TWEAK_MODAL_SNAP_ON: + mtweak->flag |= WM_MANIPULATOR_TWEAK_SNAP; + break; + case TWEAK_MODAL_SNAP_OFF: + mtweak->flag &= ~WM_MANIPULATOR_TWEAK_SNAP; + break; + } + } + + if (retval != OPERATOR_PASS_THROUGH) { + manipulator_tweak_finish(C, op, retval != OPERATOR_FINISHED, clear_modal); + return retval; + } + + /* handle manipulator */ + wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal; + if (modal_fn) { + int modal_retval = modal_fn(C, mpr, event, mtweak->flag); + + if ((modal_retval & OPERATOR_RUNNING_MODAL) == 0) { + manipulator_tweak_finish(C, op, (modal_retval & OPERATOR_CANCELLED) != 0, true); + return OPERATOR_FINISHED; + } + + /* Ugly hack to send manipulator events */ + ((wmEvent *)event)->type = EVT_MANIPULATOR_UPDATE; + } + + /* always return PASS_THROUGH so modal handlers + * with manipulators attached can update */ + BLI_assert(retval == OPERATOR_PASS_THROUGH); + return OPERATOR_PASS_THROUGH; +} + +static int manipulator_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + wmManipulatorMap *mmap = ar->manipulator_map; + wmManipulator *mpr = mmap->mmap_context.highlight; + + /* Needed for single click actions which don't enter modal state. */ + WM_tooltip_clear(C, CTX_wm_window(C)); + + if (!mpr) { + /* wm_handlers_do_intern shouldn't let this happen */ + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + bool use_drag_fallback = false; + +#ifdef USE_DRAG_DETECT + use_drag_fallback = !ELEM(mpr->drag_part, -1, mpr->highlight_part); +#endif + + if (use_drag_fallback == false) { + if (manipulator_tweak_start_and_finish(C, mmap, mpr, event, NULL)) { + return OPERATOR_FINISHED; + } + } + + bool use_drag_detect = false; +#ifdef USE_DRAG_DETECT + if (use_drag_fallback) { + wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part); + if (mpop && mpop->type) { + if (mpop->type->modal == NULL) { + use_drag_detect = true; + } + } + } +#endif + + if (use_drag_detect == false) { + if (!manipulator_tweak_start(C, mmap, mpr, event)) { + /* failed to start */ + return OPERATOR_PASS_THROUGH; + } + } + + ManipulatorTweakData *mtweak = MEM_mallocN(sizeof(ManipulatorTweakData), __func__); + + mtweak->init_event = WM_userdef_event_type_from_keymap_type(event->type); + mtweak->mpr_modal = mmap->mmap_context.highlight; + mtweak->mgroup = mtweak->mpr_modal->parent_mgroup; + mtweak->mmap = mmap; + mtweak->flag = 0; + +#ifdef USE_DRAG_DETECT + mtweak->drag_state = use_drag_detect ? DRAG_DETECT : DRAG_NOP; +#endif + + op->customdata = mtweak; + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void MANIPULATORGROUP_OT_manipulator_tweak(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Gizmo Tweak"; + ot->description = "Tweak the active gizmo"; + ot->idname = "MANIPULATORGROUP_OT_manipulator_tweak"; + + /* api callbacks */ + ot->invoke = manipulator_tweak_invoke; + ot->modal = manipulator_tweak_modal; + + /* TODO(campbell) This causes problems tweaking settings for operators, + * need to find a way to support this. */ +#if 0 + ot->flag = OPTYPE_UNDO; +#endif +} + +/** \} */ + + +static wmKeyMap *manipulatorgroup_tweak_modal_keymap(wmKeyConfig *keyconf, const char *mgroupname) +{ + wmKeyMap *keymap; + char name[KMAP_MAX_NAME]; + + static EnumPropertyItem modal_items[] = { + {TWEAK_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, + {TWEAK_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + {TWEAK_MODAL_PRECISION_ON, "PRECISION_ON", 0, "Enable Precision", ""}, + {TWEAK_MODAL_PRECISION_OFF, "PRECISION_OFF", 0, "Disable Precision", ""}, + {TWEAK_MODAL_SNAP_ON, "SNAP_ON", 0, "Enable Snap", ""}, + {TWEAK_MODAL_SNAP_OFF, "SNAP_OFF", 0, "Disable Snap", ""}, + {0, NULL, 0, NULL, NULL} + }; + + + BLI_snprintf(name, sizeof(name), "%s Tweak Modal Map", mgroupname); + keymap = WM_modalkeymap_get(keyconf, name); + + /* this function is called for each spacetype, only needs to add map once */ + if (keymap && keymap->modal_items) + return NULL; + + keymap = WM_modalkeymap_add(keyconf, name, modal_items); + + + /* items for modal map */ + WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL); + + WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM); + + WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON); + WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF); + WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON); + WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF); + + WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON); + WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF); + WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON); + WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF); + + WM_modalkeymap_assign(keymap, "MANIPULATORGROUP_OT_manipulator_tweak"); + + return keymap; +} + +/** + * Common default keymap for manipulator groups + */ +wmKeyMap *WM_manipulatorgroup_keymap_common( + const wmManipulatorGroupType *wgt, wmKeyConfig *config) +{ + /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */ + wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid); + + WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", LEFTMOUSE, KM_PRESS, KM_ANY, 0); + manipulatorgroup_tweak_modal_keymap(config, wgt->name); + + return km; +} + +/** + * Variation of #WM_manipulatorgroup_keymap_common but with keymap items for selection + */ +wmKeyMap *WM_manipulatorgroup_keymap_common_select( + const wmManipulatorGroupType *wgt, wmKeyConfig *config) +{ + /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */ + wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid); + + WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0); + WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", EVT_TWEAK_S, KM_ANY, 0, 0); + manipulatorgroup_tweak_modal_keymap(config, wgt->name); + + wmKeyMapItem *kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "extend", false); + RNA_boolean_set(kmi->ptr, "deselect", false); + RNA_boolean_set(kmi->ptr, "toggle", false); + kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "extend", false); + RNA_boolean_set(kmi->ptr, "deselect", false); + RNA_boolean_set(kmi->ptr, "toggle", true); + + return km; +} + +/** \} */ /* wmManipulatorGroup */ + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorGroupType + * + * \{ */ + +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr( + struct wmManipulatorMapType *mmap_type, + const wmManipulatorGroupType *wgt) +{ + /* could use hash lookups as operator types do, for now simple search. */ + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; + wgt_ref; + wgt_ref = wgt_ref->next) + { + if (wgt_ref->type == wgt) { + return wgt_ref; + } + } + return NULL; +} + +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find( + struct wmManipulatorMapType *mmap_type, + const char *idname) +{ + /* could use hash lookups as operator types do, for now simple search. */ + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; + wgt_ref; + wgt_ref = wgt_ref->next) + { + if (STREQ(idname, wgt_ref->type->idname)) { + return wgt_ref; + } + } + return NULL; +} + +/** + * Use this for registering manipulators on startup. For runtime, use #WM_manipulatormaptype_group_link_runtime. + */ +wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link( + wmManipulatorMapType *mmap_type, const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + BLI_assert(wgt != NULL); + return WM_manipulatormaptype_group_link_ptr(mmap_type, wgt); +} + +wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr( + wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt) +{ + wmManipulatorGroupTypeRef *wgt_ref = MEM_callocN(sizeof(wmManipulatorGroupTypeRef), "manipulator-group-ref"); + wgt_ref->type = wgt; + BLI_addtail(&mmap_type->grouptype_refs, wgt_ref); + return wgt_ref; +} + +void WM_manipulatormaptype_group_init_runtime_keymap( + const Main *bmain, + wmManipulatorGroupType *wgt) +{ + /* init keymap - on startup there's an extra call to init keymaps for 'permanent' manipulator-groups */ + wm_manipulatorgrouptype_setup_keymap(wgt, ((wmWindowManager *)bmain->wm.first)->defaultconf); +} + +void WM_manipulatormaptype_group_init_runtime( + const Main *bmain, wmManipulatorMapType *mmap_type, + wmManipulatorGroupType *wgt) +{ + /* now create a manipulator for all existing areas */ + for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) { + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + for (ARegion *ar = lb->first; ar; ar = ar->next) { + wmManipulatorMap *mmap = ar->manipulator_map; + if (mmap && mmap->type == mmap_type) { + wm_manipulatorgroup_new_from_type(mmap, wgt); + + /* just add here, drawing will occur on next update */ + wm_manipulatormap_highlight_set(mmap, NULL, NULL, 0); + ED_region_tag_redraw(ar); + } + } + } + } + } +} + + +/** + * Unlike #WM_manipulatormaptype_group_unlink this doesn't maintain correct state, simply free. + */ +void WM_manipulatormaptype_group_free(wmManipulatorGroupTypeRef *wgt_ref) +{ + MEM_freeN(wgt_ref); +} + +void WM_manipulatormaptype_group_unlink( + bContext *C, Main *bmain, wmManipulatorMapType *mmap_type, + const wmManipulatorGroupType *wgt) +{ + /* Free instances. */ + for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) { + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + for (ARegion *ar = lb->first; ar; ar = ar->next) { + wmManipulatorMap *mmap = ar->manipulator_map; + if (mmap && mmap->type == mmap_type) { + wmManipulatorGroup *mgroup, *mgroup_next; + for (mgroup = mmap->groups.first; mgroup; mgroup = mgroup_next) { + mgroup_next = mgroup->next; + if (mgroup->type == wgt) { + BLI_assert(mgroup->parent_mmap == mmap); + wm_manipulatorgroup_free(C, mgroup); + ED_region_tag_redraw(ar); + } + } + } + } + } + } + } + + /* Free types. */ + wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt); + if (wgt_ref) { + BLI_remlink(&mmap_type->grouptype_refs, wgt_ref); + WM_manipulatormaptype_group_free(wgt_ref); + } + + /* Note, we may want to keep this keymap for editing */ + WM_keymap_remove(wgt->keyconf, wgt->keymap); + + BLI_assert(WM_manipulatormaptype_group_find_ptr(mmap_type, wgt) == NULL); +} + +void wm_manipulatorgrouptype_setup_keymap( + wmManipulatorGroupType *wgt, wmKeyConfig *keyconf) +{ + /* Use flag since setup_keymap may return NULL, + * in that case we better not keep calling it. */ + if (wgt->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) { + wgt->keymap = wgt->setup_keymap(wgt, keyconf); + wgt->keyconf = keyconf; + wgt->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT; + } +} + +/** \} */ /* wmManipulatorGroupType */ + +/* -------------------------------------------------------------------- */ +/** \name High Level Add/Remove API + * + * For use directly from operators & RNA registration. + * + * \note In context of manipulator API these names are a bit misleading, + * but for general use terms its OK. + * `WM_manipulator_group_type_add` would be more correctly called: + * `WM_manipulatormaptype_grouptype_reference_link` + * but for general purpose API this is too detailed & annoying. + * + * \note We may want to return a value if there is nothing to remove. + * + * \{ */ + +void WM_manipulator_group_type_add_ptr_ex( + wmManipulatorGroupType *wgt, + wmManipulatorMapType *mmap_type) +{ + WM_manipulatormaptype_group_link_ptr(mmap_type, wgt); + + WM_manipulatorconfig_update_tag_init(mmap_type, wgt); +} +void WM_manipulator_group_type_add_ptr( + wmManipulatorGroupType *wgt) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type); +} +void WM_manipulator_group_type_add(const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + BLI_assert(wgt != NULL); + WM_manipulator_group_type_add_ptr(wgt); +} + +void WM_manipulator_group_type_ensure_ptr_ex( + wmManipulatorGroupType *wgt, + wmManipulatorMapType *mmap_type) +{ + wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt); + if (wgt_ref == NULL) { + WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type); + } +} +void WM_manipulator_group_type_ensure_ptr( + wmManipulatorGroupType *wgt) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulator_group_type_ensure_ptr_ex(wgt, mmap_type); +} +void WM_manipulator_group_type_ensure(const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + BLI_assert(wgt != NULL); + WM_manipulator_group_type_ensure_ptr(wgt); +} + +void WM_manipulator_group_type_remove_ptr_ex( + struct Main *bmain, wmManipulatorGroupType *wgt, + wmManipulatorMapType *mmap_type) +{ + WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt); + WM_manipulatorgrouptype_free_ptr(wgt); +} +void WM_manipulator_group_type_remove_ptr( + struct Main *bmain, wmManipulatorGroupType *wgt) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulator_group_type_remove_ptr_ex(bmain, wgt, mmap_type); +} +void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + BLI_assert(wgt != NULL); + WM_manipulator_group_type_remove_ptr(bmain, wgt); +} + +/* delayed versions */ + +void WM_manipulator_group_type_unlink_delayed_ptr_ex( + wmManipulatorGroupType *wgt, + wmManipulatorMapType *mmap_type) +{ + WM_manipulatorconfig_update_tag_remove(mmap_type, wgt); +} + +void WM_manipulator_group_type_unlink_delayed_ptr( + wmManipulatorGroupType *wgt) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulator_group_type_unlink_delayed_ptr_ex(wgt, mmap_type); +} + +void WM_manipulator_group_type_unlink_delayed(const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + BLI_assert(wgt != NULL); + WM_manipulator_group_type_unlink_delayed_ptr(wgt); +} + +/** \} */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c new file mode 100644 index 00000000000..f93beb0ac9f --- /dev/null +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c @@ -0,0 +1,197 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c + * \ingroup wm + */ + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "BKE_context.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* only for own init/exit calls (wm_manipulatorgrouptype_init/wm_manipulatorgrouptype_free) */ +#include "wm.h" + +/* own includes */ +#include "wm_gizmo_wmapi.h" +#include "wm_gizmo_intern.h" + + +/** \name ManipulatorGroup Type Append + * + * \note This follows conventions from #WM_operatortype_find #WM_operatortype_append & friends. + * \{ */ + +static GHash *global_manipulatorgrouptype_hash = NULL; + +wmManipulatorGroupType *WM_manipulatorgrouptype_find(const char *idname, bool quiet) +{ + if (idname[0]) { + wmManipulatorGroupType *wgt; + + wgt = BLI_ghash_lookup(global_manipulatorgrouptype_hash, idname); + if (wgt) { + return wgt; + } + + if (!quiet) { + printf("search for unknown manipulator group '%s'\n", idname); + } + } + else { + if (!quiet) { + printf("search for empty manipulator group\n"); + } + } + + return NULL; +} + +/* caller must free */ +void WM_manipulatorgrouptype_iter(GHashIterator *ghi) +{ + BLI_ghashIterator_init(ghi, global_manipulatorgrouptype_hash); +} + +static wmManipulatorGroupType *wm_manipulatorgrouptype_append__begin(void) +{ + wmManipulatorGroupType *wgt = MEM_callocN(sizeof(wmManipulatorGroupType), "manipulatorgrouptype"); + + return wgt; +} +static void wm_manipulatorgrouptype_append__end(wmManipulatorGroupType *wgt) +{ + BLI_assert(wgt->name != NULL); + BLI_assert(wgt->idname != NULL); + + wgt->type_update_flag |= WM_MANIPULATORMAPTYPE_KEYMAP_INIT; + + /* if not set, use default */ + if (wgt->setup_keymap == NULL) { + if (wgt->flag & WM_MANIPULATORGROUPTYPE_SELECT) { + wgt->setup_keymap = WM_manipulatorgroup_keymap_common_select; + } + else { + wgt->setup_keymap = WM_manipulatorgroup_keymap_common; + } + } + + BLI_ghash_insert(global_manipulatorgrouptype_hash, (void *)wgt->idname, wgt); +} + +wmManipulatorGroupType *WM_manipulatorgrouptype_append( + void (*wtfunc)(struct wmManipulatorGroupType *)) +{ + wmManipulatorGroupType *wgt = wm_manipulatorgrouptype_append__begin(); + wtfunc(wgt); + wm_manipulatorgrouptype_append__end(wgt); + return wgt; +} + +wmManipulatorGroupType *WM_manipulatorgrouptype_append_ptr( + void (*wtfunc)(struct wmManipulatorGroupType *, void *), void *userdata) +{ + wmManipulatorGroupType *wgt = wm_manipulatorgrouptype_append__begin(); + wtfunc(wgt, userdata); + wm_manipulatorgrouptype_append__end(wgt); + return wgt; +} + +/** + * Append and insert into a manipulator typemap. + * This is most common for C manipulators which are enabled by default. + */ +wmManipulatorGroupTypeRef *WM_manipulatorgrouptype_append_and_link( + wmManipulatorMapType *mmap_type, + void (*wtfunc)(struct wmManipulatorGroupType *)) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_append(wtfunc); + + wgt->mmap_params.spaceid = mmap_type->spaceid; + wgt->mmap_params.regionid = mmap_type->regionid; + + return WM_manipulatormaptype_group_link_ptr(mmap_type, wgt); +} + +/** + * Free but don't remove from ghash. + */ +static void manipulatorgrouptype_free(wmManipulatorGroupType *wgt) +{ + if (wgt->ext.srna) { /* python manipulator group, allocs own string */ + MEM_freeN((void *)wgt->idname); + } + + MEM_freeN(wgt); +} + +void WM_manipulatorgrouptype_free_ptr(wmManipulatorGroupType *wgt) +{ + BLI_assert(wgt == WM_manipulatorgrouptype_find(wgt->idname, false)); + + BLI_ghash_remove(global_manipulatorgrouptype_hash, wgt->idname, NULL, NULL); + + manipulatorgrouptype_free(wgt); + + /* XXX, TODO, update the world! */ +} + +bool WM_manipulatorgrouptype_free(const char *idname) +{ + wmManipulatorGroupType *wgt = BLI_ghash_lookup(global_manipulatorgrouptype_hash, idname); + + if (wgt == NULL) { + return false; + } + + WM_manipulatorgrouptype_free_ptr(wgt); + + return true; +} + +static void wm_manipulatorgrouptype_ghash_free_cb(wmManipulatorGroupType *wgt) +{ + manipulatorgrouptype_free(wgt); +} + +void wm_manipulatorgrouptype_free(void) +{ + BLI_ghash_free(global_manipulatorgrouptype_hash, NULL, (GHashValFreeFP)wm_manipulatorgrouptype_ghash_free_cb); + global_manipulatorgrouptype_hash = NULL; +} + +/* called on initialize WM_init() */ +void wm_manipulatorgrouptype_init(void) +{ + /* reserve size is set based on blender default setup */ + global_manipulatorgrouptype_hash = BLI_ghash_str_new_ex("wm_manipulatorgrouptype_init gh", 128); +} + +/** \} */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h b/source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h new file mode 100644 index 00000000000..7a007a8a909 --- /dev/null +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h @@ -0,0 +1,144 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmo/intern/wm_gizmo_intern.h + * \ingroup wm + */ + + +#ifndef __WM_GIZMO_INTERN_H__ +#define __WM_GIZMO_INTERN_H__ + +struct wmKeyConfig; +struct wmManipulatorMap; +struct ManipulatorGeomInfo; +struct GHashIterator; + +#include "wm_gizmo_fn.h" + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + + +bool wm_manipulator_select_set_ex( + struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select, + bool use_array, bool use_callback); +bool wm_manipulator_select_and_highlight(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulator *mpr); + +void wm_manipulator_calculate_scale(struct wmManipulator *mpr, const bContext *C); +void wm_manipulator_update(struct wmManipulator *mpr, const bContext *C, const bool refresh_map); + +int wm_manipulator_is_visible(struct wmManipulator *mpr); +enum { + WM_MANIPULATOR_IS_VISIBLE_UPDATE = (1 << 0), + WM_MANIPULATOR_IS_VISIBLE_DRAW = (1 << 1), +}; + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +enum { + TWEAK_MODAL_CANCEL = 1, + TWEAK_MODAL_CONFIRM, + TWEAK_MODAL_PRECISION_ON, + TWEAK_MODAL_PRECISION_OFF, + TWEAK_MODAL_SNAP_ON, + TWEAK_MODAL_SNAP_OFF, +}; + +struct wmManipulatorGroup *wm_manipulatorgroup_new_from_type( + struct wmManipulatorMap *mmap, struct wmManipulatorGroupType *wgt); +void wm_manipulatorgroup_free(bContext *C, struct wmManipulatorGroup *mgroup); +void wm_manipulatorgroup_manipulator_register(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr); +struct wmManipulator *wm_manipulatorgroup_find_intersected_manipulator( + const struct wmManipulatorGroup *mgroup, struct bContext *C, const struct wmEvent *event, + int *r_part); +void wm_manipulatorgroup_intersectable_manipulators_to_list( + const struct wmManipulatorGroup *mgroup, struct ListBase *listbase); +void wm_manipulatorgroup_ensure_initialized(struct wmManipulatorGroup *mgroup, const struct bContext *C); +bool wm_manipulatorgroup_is_visible_in_drawstep( + const struct wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep); + +void wm_manipulatorgrouptype_setup_keymap( + struct wmManipulatorGroupType *wgt, struct wmKeyConfig *keyconf); + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +typedef struct wmManipulatorMapSelectState { + struct wmManipulator **items; + int len, len_alloc; +} wmManipulatorMapSelectState; + +struct wmManipulatorMap { + + struct wmManipulatorMapType *type; + ListBase groups; /* wmManipulatorGroup */ + + /* private, update tagging (enum defined in C source). */ + char update_flag[WM_MANIPULATORMAP_DRAWSTEP_MAX]; + + /** + * \brief Manipulator map runtime context + * + * Contains information about this manipulator-map. Currently + * highlighted manipulator, currently selected manipulators, ... + */ + struct { + /* we redraw the manipulator-map when this changes */ + struct wmManipulator *highlight; + /* User has clicked this manipulator and it gets all input. */ + struct wmManipulator *modal; + /* array for all selected manipulators */ + struct wmManipulatorMapSelectState select; + /* cursor location at point of entering modal (see: WM_MANIPULATOR_GRAB_CURSOR) */ + int event_xy[2]; + short event_grabcursor; + /* until we have nice cursor push/pop API. */ + int last_cursor; + } mmap_context; +}; + +/** + * This is a container for all manipulator types that can be instantiated in a region. + * (similar to dropboxes). + * + * \note There is only ever one of these for every (area, region) combination. + */ +struct wmManipulatorMapType { + struct wmManipulatorMapType *next, *prev; + short spaceid, regionid; + /* types of manipulator-groups for this manipulator-map type */ + ListBase grouptype_refs; + + /* eManipulatorMapTypeUpdateFlags */ + eWM_ManipulatorMapTypeUpdateFlag type_update_flag; +}; + +void wm_manipulatormap_select_array_clear(struct wmManipulatorMap *mmap); +bool wm_manipulatormap_deselect_all(struct wmManipulatorMap *mmap); +void wm_manipulatormap_select_array_shrink(struct wmManipulatorMap *mmap, int len_subtract); +void wm_manipulatormap_select_array_push_back(struct wmManipulatorMap *mmap, wmManipulator *mpr); +void wm_manipulatormap_select_array_remove(struct wmManipulatorMap *mmap, wmManipulator *mpr); + +#endif diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c new file mode 100644 index 00000000000..94db6b45887 --- /dev/null +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -0,0 +1,1209 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmo/intern/wm_gizmo_map.c + * \ingroup wm + */ + +#include <string.h> + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_rect.h" +#include "BLI_string.h" +#include "BLI_ghash.h" + +#include "BKE_context.h" +#include "BKE_global.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "GPU_glew.h" +#include "GPU_matrix.h" +#include "GPU_select.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm_event_system.h" + +/* for tool-tips */ +#include "UI_interface.h" + +#include "DEG_depsgraph.h" + +/* own includes */ +#include "wm_gizmo_wmapi.h" +#include "wm_gizmo_intern.h" + +/** + * Store all manipulator-maps here. Anyone who wants to register a manipulator for a certain + * area type can query the manipulator-map to do so. + */ +static ListBase manipulatormaptypes = {NULL, NULL}; + +/** + * Update when manipulator-map types change. + */ +/* so operator removal can trigger update */ +typedef enum eWM_ManipulatorGroupTypeGlobalFlag { + WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0), + WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1), +} eWM_ManipulatorGroupTypeGlobalFlag; + +static eWM_ManipulatorGroupTypeGlobalFlag wm_mmap_type_update_flag = 0; + +/** + * Manipulator-map update tagging. + */ +enum { + /** #manipulatormap_prepare_drawing has run */ + MANIPULATORMAP_IS_PREPARE_DRAW = (1 << 0), + MANIPULATORMAP_IS_REFRESH_CALLBACK = (1 << 1), +}; + + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorMap Selection Array API + * + * Just handle ``wm_manipulatormap_select_array_*``, not flags or callbacks. + * + * \{ */ + +static void wm_manipulatormap_select_array_ensure_len_alloc(wmManipulatorMap *mmap, int len) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + if (len <= msel->len_alloc) { + return; + } + msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len); + msel->len_alloc = len; +} + +void wm_manipulatormap_select_array_clear(wmManipulatorMap *mmap) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + MEM_SAFE_FREE(msel->items); + msel->len = 0; + msel->len_alloc = 0; +} + +void wm_manipulatormap_select_array_shrink(wmManipulatorMap *mmap, int len_subtract) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + msel->len -= len_subtract; + if (msel->len <= 0) { + wm_manipulatormap_select_array_clear(mmap); + } + else { + if (msel->len < msel->len_alloc / 2) { + msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len); + msel->len_alloc = msel->len; + } + } +} + +void wm_manipulatormap_select_array_push_back(wmManipulatorMap *mmap, wmManipulator *mpr) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + BLI_assert(msel->len <= msel->len_alloc); + if (msel->len == msel->len_alloc) { + msel->len_alloc = (msel->len + 1) * 2; + msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len_alloc); + } + msel->items[msel->len++] = mpr; +} + +void wm_manipulatormap_select_array_remove(wmManipulatorMap *mmap, wmManipulator *mpr) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + /* remove manipulator from selected_manipulators array */ + for (int i = 0; i < msel->len; i++) { + if (msel->items[i] == mpr) { + for (int j = i; j < (msel->len - 1); j++) { + msel->items[j] = msel->items[j + 1]; + } + wm_manipulatormap_select_array_shrink(mmap, 1); + break; + } + } + +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorMap + * + * \{ */ + +/** + * Creates a manipulator-map with all registered manipulators for that type + */ +wmManipulatorMap *WM_manipulatormap_new_from_type( + const struct wmManipulatorMapType_Params *mmap_params) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(mmap_params); + wmManipulatorMap *mmap; + + mmap = MEM_callocN(sizeof(wmManipulatorMap), "ManipulatorMap"); + mmap->type = mmap_type; + WM_manipulatormap_tag_refresh(mmap); + + /* create all manipulator-groups for this manipulator-map. We may create an empty one + * too in anticipation of manipulators from operators etc */ + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) { + wm_manipulatorgroup_new_from_type(mmap, wgt_ref->type); + } + + return mmap; +} + +void wm_manipulatormap_remove(wmManipulatorMap *mmap) +{ + /* Clear first so further calls don't waste time trying to maintain correct array state. */ + wm_manipulatormap_select_array_clear(mmap); + + for (wmManipulatorGroup *mgroup = mmap->groups.first, *mgroup_next; mgroup; mgroup = mgroup_next) { + mgroup_next = mgroup->next; + BLI_assert(mgroup->parent_mmap == mmap); + wm_manipulatorgroup_free(NULL, mgroup); + } + BLI_assert(BLI_listbase_is_empty(&mmap->groups)); + + MEM_freeN(mmap); +} + + +wmManipulatorGroup *WM_manipulatormap_group_find( + struct wmManipulatorMap *mmap, + const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + if (wgt) { + return WM_manipulatormap_group_find_ptr(mmap, wgt); + } + return NULL; +} + +wmManipulatorGroup *WM_manipulatormap_group_find_ptr( + struct wmManipulatorMap *mmap, + const struct wmManipulatorGroupType *wgt) +{ + for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + if (mgroup->type == wgt) { + return mgroup; + } + } + return NULL; +} + +const ListBase *WM_manipulatormap_group_list(wmManipulatorMap *mmap) +{ + return &mmap->groups; +} + +bool WM_manipulatormap_is_any_selected(const wmManipulatorMap *mmap) +{ + return mmap->mmap_context.select.len != 0; +} + +/** + * \note We could use a callback to define bounds, for now just use matrix location. + */ +bool WM_manipulatormap_minmax( + const wmManipulatorMap *mmap, bool UNUSED(use_hidden), bool use_select, + float r_min[3], float r_max[3]) +{ + if (use_select) { + int i; + for (i = 0; i < mmap->mmap_context.select.len; i++) { + minmax_v3v3_v3(r_min, r_max, mmap->mmap_context.select.items[i]->matrix_basis[3]); + } + return i != 0; + } + else { + bool ok = false; + BLI_assert(!"TODO"); + return ok; + } +} + +/** + * Creates and returns idname hash table for (visible) manipulators in \a mmap + * + * \param poll Polling function for excluding manipulators. + * \param data Custom data passed to \a poll + * + * TODO(campbell): this uses unreliable order, + * best we use an iterator function instead of a hash. + */ +static GHash *WM_manipulatormap_manipulator_hash_new( + const bContext *C, wmManipulatorMap *mmap, + bool (*poll)(const wmManipulator *, void *), + void *data, const bool include_hidden) +{ + GHash *hash = BLI_ghash_ptr_new(__func__); + + /* collect manipulators */ + for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + if (WM_manipulator_group_type_poll(C, mgroup->type)) { + for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + if ((include_hidden || (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) && + (!poll || poll(mpr, data))) + { + BLI_ghash_insert(hash, mpr, mpr); + } + } + } + } + + return hash; +} + +void WM_manipulatormap_tag_refresh(wmManipulatorMap *mmap) +{ + if (mmap) { + /* We might want only to refresh some, for tag all steps. */ + for (int i = 0; i < WM_MANIPULATORMAP_DRAWSTEP_MAX; i++) { + mmap->update_flag[i] |= ( + MANIPULATORMAP_IS_PREPARE_DRAW | + MANIPULATORMAP_IS_REFRESH_CALLBACK); + } + } +} + +static bool manipulator_prepare_drawing( + wmManipulatorMap *mmap, wmManipulator *mpr, + const bContext *C, ListBase *draw_manipulators, + const eWM_ManipulatorMapDrawStep drawstep) +{ + int do_draw = wm_manipulator_is_visible(mpr); + if (do_draw == 0) { + /* skip */ + } + else { + /* Ensure we get RNA updates */ + if (do_draw & WM_MANIPULATOR_IS_VISIBLE_UPDATE) { + /* hover manipulators need updating, even if we don't draw them */ + wm_manipulator_update(mpr, C, (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_PREPARE_DRAW) != 0); + } + if (do_draw & WM_MANIPULATOR_IS_VISIBLE_DRAW) { + BLI_addhead(draw_manipulators, BLI_genericNodeN(mpr)); + } + return true; + } + + return false; +} + +/** + * Update manipulators of \a mmap to prepare for drawing. Adds all manipulators that + * should be drawn to list \a draw_manipulators, note that added items need freeing. + */ +static void manipulatormap_prepare_drawing( + wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators, + const eWM_ManipulatorMapDrawStep drawstep) +{ + if (!mmap || BLI_listbase_is_empty(&mmap->groups)) + return; + wmManipulator *mpr_modal = mmap->mmap_context.modal; + + /* only active manipulator needs updating */ + if (mpr_modal) { + if ((mpr_modal->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL) == 0) { + if (wm_manipulatorgroup_is_visible_in_drawstep(mpr_modal->parent_mgroup, drawstep)) { + if (manipulator_prepare_drawing(mmap, mpr_modal, C, draw_manipulators, drawstep)) { + mmap->update_flag[drawstep] &= ~MANIPULATORMAP_IS_PREPARE_DRAW; + } + } + /* don't draw any other manipulators */ + return; + } + } + + for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */ + if (!wm_manipulatorgroup_is_visible_in_drawstep(mgroup, drawstep) || + !WM_manipulator_group_type_poll(C, mgroup->type)) + { + continue; + } + + /* needs to be initialized on first draw */ + /* XXX weak: Manipulator-group may skip refreshing if it's invisible (map gets untagged nevertheless) */ + if (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_REFRESH_CALLBACK) { + /* force refresh again. */ + mgroup->init_flag &= ~WM_MANIPULATORGROUP_INIT_REFRESH; + } + /* Calls `setup`, `setup_keymap` and `refresh` if they're defined. */ + wm_manipulatorgroup_ensure_initialized(mgroup, C); + + /* prepare drawing */ + if (mgroup->type->draw_prepare) { + mgroup->type->draw_prepare(C, mgroup); + } + + for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + manipulator_prepare_drawing(mmap, mpr, C, draw_manipulators, drawstep); + } + } + + mmap->update_flag[drawstep] &= + ~(MANIPULATORMAP_IS_REFRESH_CALLBACK | + MANIPULATORMAP_IS_PREPARE_DRAW); +} + +/** + * Draw all visible manipulators in \a mmap. + * Uses global draw_manipulators listbase. + */ +static void manipulators_draw_list(const wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators) +{ + /* Can be empty if we're dynamically added and removed. */ + if ((mmap == NULL) || BLI_listbase_is_empty(&mmap->groups)) { + return; + } + + /* TODO this will need it own shader probably? don't think it can be handled from that point though. */ +/* const bool use_lighting = (U.manipulator_flag & V3D_MANIPULATOR_SHADED) != 0; */ + + bool is_depth_prev = false; + + /* draw_manipulators contains all visible manipulators - draw them */ + for (LinkData *link = draw_manipulators->first, *link_next; link; link = link_next) { + wmManipulator *mpr = link->data; + link_next = link->next; + + bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0; + + /* Weak! since we don't 100% support depth yet (select ignores depth) always show highlighted */ + if (is_depth && (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT)) { + is_depth = false; + } + + if (is_depth == is_depth_prev) { + /* pass */ + } + else { + if (is_depth) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + is_depth_prev = is_depth; + } + + /* XXX force AntiAlias Manipulators. */ + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POLYGON_SMOOTH); + + mpr->type->draw(C, mpr); + + glDisable(GL_LINE_SMOOTH); + glDisable(GL_POLYGON_SMOOTH); + + /* free/remove manipulator link after drawing */ + BLI_freelinkN(draw_manipulators, link); + } + + if (is_depth_prev) { + glDisable(GL_DEPTH_TEST); + } +} + +void WM_manipulatormap_draw( + wmManipulatorMap *mmap, const bContext *C, + const eWM_ManipulatorMapDrawStep drawstep) +{ + if (!WM_manipulator_context_check_drawstep(C, drawstep)) { + return; + } + + ListBase draw_manipulators = {NULL}; + + manipulatormap_prepare_drawing(mmap, C, &draw_manipulators, drawstep); + manipulators_draw_list(mmap, C, &draw_manipulators); + BLI_assert(BLI_listbase_is_empty(&draw_manipulators)); +} + +static void manipulator_draw_select_3D_loop(const bContext *C, ListBase *visible_manipulators) +{ + int select_id = 0; + wmManipulator *mpr; + + /* TODO(campbell): this depends on depth buffer being written to, currently broken for the 3D view. */ + bool is_depth_prev = false; + bool is_depth_skip_prev = false; + + for (LinkData *link = visible_manipulators->first; link; link = link->next) { + mpr = link->data; + + bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0; + if (is_depth == is_depth_prev) { + /* pass */ + } + else { + if (is_depth) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + is_depth_prev = is_depth; + } + bool is_depth_skip = (mpr->flag & WM_MANIPULATOR_SELECT_BACKGROUND) != 0; + if (is_depth_skip == is_depth_skip_prev) { + /* pass */ + } + else { + glDepthMask(!is_depth_skip); + is_depth_skip_prev = is_depth_skip; + } + + /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected manipulator part id */ + + mpr->type->draw_select(C, mpr, select_id << 8); + + + select_id++; + } + + if (is_depth_prev) { + glDisable(GL_DEPTH_TEST); + } + if (is_depth_skip_prev) { + glDepthMask(true); + } +} + +static int manipulator_find_intersected_3d_intern( + ListBase *visible_manipulators, const bContext *C, const int co[2], + const int hotspot) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + View3D *v3d = sa->spacedata.first; + rcti rect; + /* Almost certainly overkill, but allow for many custom manipulators. */ + GLuint buffer[MAXPICKBUF]; + short hits; + const bool do_passes = GPU_select_query_check_active(); + + BLI_rcti_init_pt_radius(&rect, co, hotspot); + + ED_view3d_draw_setup_view(CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, &rect); + + if (do_passes) + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0); + else + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_ALL, 0); + /* do the drawing */ + manipulator_draw_select_3D_loop(C, visible_manipulators); + + hits = GPU_select_end(); + + if (do_passes && (hits > 0)) { + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits); + manipulator_draw_select_3D_loop(C, visible_manipulators); + GPU_select_end(); + } + + ED_view3d_draw_setup_view(CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, NULL); + + const GLuint *hit_near = GPU_select_buffer_near(buffer, hits); + + return hit_near ? hit_near[3] : -1; +} + +/** + * Try to find a 3D manipulator at screen-space coordinate \a co. Uses OpenGL picking. + */ +static wmManipulator *manipulator_find_intersected_3d( + bContext *C, const int co[2], ListBase *visible_manipulators, + int *r_part) +{ + wmManipulator *result = NULL; + int hit = -1; + + int hotspot_radii[] = { + 3 * U.pixelsize, + /* This runs on mouse move, careful doing too many tests! */ + 10 * U.pixelsize, + }; + + *r_part = 0; + + /* set up view matrices */ + view3d_operator_needs_opengl(C); + + hit = -1; + + for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) { + hit = manipulator_find_intersected_3d_intern(visible_manipulators, C, co, hotspot_radii[i]); + if (hit != -1) { + break; + } + } + + if (hit != -1) { + LinkData *link = BLI_findlink(visible_manipulators, hit >> 8); + if (link != NULL) { + *r_part = hit & 255; + result = link->data; + } + else { + /* All manipulators should use selection ID they're given as part of the callback, + * if they don't it will attempt tp lookup non-existing index. */ + BLI_assert(0); + } + } + + return result; +} + +/** + * Try to find a manipulator under the mouse position. 2D intersections have priority over + * 3D ones (could check for smallest screen-space distance but not needed right now). + */ +wmManipulator *wm_manipulatormap_highlight_find( + wmManipulatorMap *mmap, bContext *C, const wmEvent *event, + int *r_part) +{ + wmManipulator *mpr = NULL; + ListBase visible_3d_manipulators = {NULL}; + bool do_step[WM_MANIPULATORMAP_DRAWSTEP_MAX]; + + for (int i = 0; i < ARRAY_SIZE(do_step); i++) { + do_step[i] = WM_manipulator_context_check_drawstep(C, i); + } + + for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + + /* If it were important we could initialize here, + * but this only happens when events are handled before drawing, + * just skip to keep code-path for initializing manipulators simple. */ + if ((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0) { + continue; + } + + if (WM_manipulator_group_type_poll(C, mgroup->type)) { + eWM_ManipulatorMapDrawStep step; + if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) { + step = WM_MANIPULATORMAP_DRAWSTEP_3D; + } + else { + step = WM_MANIPULATORMAP_DRAWSTEP_2D; + } + + if (do_step[step]) { + if ((mmap->update_flag[step] & MANIPULATORMAP_IS_REFRESH_CALLBACK) && + (mgroup->type->refresh != NULL)) + { + mgroup->type->refresh(C, mgroup); + /* cleared below */ + } + if (step == WM_MANIPULATORMAP_DRAWSTEP_3D) { + wm_manipulatorgroup_intersectable_manipulators_to_list(mgroup, &visible_3d_manipulators); + } + else if (step == WM_MANIPULATORMAP_DRAWSTEP_2D) { + if ((mpr = wm_manipulatorgroup_find_intersected_manipulator(mgroup, C, event, r_part))) { + break; + } + } + } + } + } + + if (!BLI_listbase_is_empty(&visible_3d_manipulators)) { + /* 2D manipulators get priority. */ + if (mpr == NULL) { + mpr = manipulator_find_intersected_3d(C, event->mval, &visible_3d_manipulators, r_part); + } + BLI_freelistN(&visible_3d_manipulators); + } + + mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_3D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK; + mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_2D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK; + + return mpr; +} + +void WM_manipulatormap_add_handlers(ARegion *ar, wmManipulatorMap *mmap) +{ + wmEventHandler *handler; + + for (handler = ar->handlers.first; handler; handler = handler->next) { + if (handler->manipulator_map == mmap) { + return; + } + } + + handler = MEM_callocN(sizeof(wmEventHandler), "manipulator handler"); + + BLI_assert(mmap == ar->manipulator_map); + handler->manipulator_map = mmap; + BLI_addtail(&ar->handlers, handler); +} + +void wm_manipulatormaps_handled_modal_update( + bContext *C, wmEvent *event, wmEventHandler *handler) +{ + const bool modal_running = (handler->op != NULL); + + /* happens on render or when joining areas */ + if (!handler->op_region || !handler->op_region->manipulator_map) { + return; + } + + wmManipulatorMap *mmap = handler->op_region->manipulator_map; + wmManipulator *mpr = wm_manipulatormap_modal_get(mmap); + ScrArea *area = CTX_wm_area(C); + ARegion *region = CTX_wm_region(C); + + wm_manipulatormap_handler_context(C, handler); + + /* regular update for running operator */ + if (modal_running) { + wmManipulatorOpElem *mpop = mpr ? WM_manipulator_operator_get(mpr, mpr->highlight_part) : NULL; + if (mpr && mpop && (mpop->type != NULL) && (mpop->type == handler->op->type)) { + wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal; + if (modal_fn != NULL) { + int retval = modal_fn(C, mpr, event, 0); + /* The manipulator is tried to the operator, we can't choose when to exit. */ + BLI_assert(retval & OPERATOR_RUNNING_MODAL); + UNUSED_VARS_NDEBUG(retval); + } + } + } + /* operator not running anymore */ + else { + wm_manipulatormap_highlight_set(mmap, C, NULL, 0); + if (mpr) { + /* This isn't defined if it ends because of success of cancel, we may want to change. */ + bool cancel = true; + if (mpr->type->exit) { + mpr->type->exit(C, mpr, cancel); + } + wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false); + } + } + + /* restore the area */ + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); +} + +/** + * Deselect all selected manipulators in \a mmap. + * \return if selection has changed. + */ +bool wm_manipulatormap_deselect_all(wmManipulatorMap *mmap) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + + if (msel->items == NULL || msel->len == 0) { + return false; + } + + for (int i = 0; i < msel->len; i++) { + wm_manipulator_select_set_ex(mmap, msel->items[i], false, false, true); + } + + wm_manipulatormap_select_array_clear(mmap); + + /* always return true, we already checked + * if there's anything to deselect */ + return true; +} + +BLI_INLINE bool manipulator_selectable_poll(const wmManipulator *mpr, void *UNUSED(data)) +{ + return (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT); +} + +/** + * Select all selectable manipulators in \a mmap. + * \return if selection has changed. + */ +static bool wm_manipulatormap_select_all_intern( + bContext *C, wmManipulatorMap *mmap) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + /* GHash is used here to avoid having to loop over all manipulators twice (once to + * get tot_sel for allocating, once for actually selecting). Instead we collect + * selectable manipulators in hash table and use this to get tot_sel and do selection */ + + GHash *hash = WM_manipulatormap_manipulator_hash_new(C, mmap, manipulator_selectable_poll, NULL, true); + GHashIterator gh_iter; + int i; + bool changed = false; + + wm_manipulatormap_select_array_ensure_len_alloc(mmap, BLI_ghash_len(hash)); + + GHASH_ITER_INDEX (gh_iter, hash, i) { + wmManipulator *mpr_iter = BLI_ghashIterator_getValue(&gh_iter); + WM_manipulator_select_set(mmap, mpr_iter, true); + } + /* highlight first manipulator */ + wm_manipulatormap_highlight_set(mmap, C, msel->items[0], msel->items[0]->highlight_part); + + BLI_assert(BLI_ghash_len(hash) == msel->len); + + BLI_ghash_free(hash, NULL, NULL); + return changed; +} + +/** + * Select/Deselect all selectable manipulators in \a mmap. + * \return if selection has changed. + * + * TODO select all by type + */ +bool WM_manipulatormap_select_all(bContext *C, wmManipulatorMap *mmap, const int action) +{ + bool changed = false; + + switch (action) { + case SEL_SELECT: + changed = wm_manipulatormap_select_all_intern(C, mmap); + break; + case SEL_DESELECT: + changed = wm_manipulatormap_deselect_all(mmap); + break; + default: + BLI_assert(0); + break; + } + + if (changed) + WM_event_add_mousemove(C); + + return changed; +} + +/** + * Prepare context for manipulator handling (but only if area/region is + * part of screen). Version of #wm_handler_op_context for manipulators. + */ +void wm_manipulatormap_handler_context(bContext *C, wmEventHandler *handler) +{ + bScreen *screen = CTX_wm_screen(C); + + if (screen) { + if (handler->op_area == NULL) { + /* do nothing in this context */ + } + else { + ScrArea *sa; + + for (sa = screen->areabase.first; sa; sa = sa->next) + if (sa == handler->op_area) + break; + if (sa == NULL) { + /* when changing screen layouts with running modal handlers (like render display), this + * is not an error to print */ + if (handler->manipulator_map == NULL) + printf("internal error: modal manipulator-map handler has invalid area\n"); + } + else { + ARegion *ar; + CTX_wm_area_set(C, sa); + for (ar = sa->regionbase.first; ar; ar = ar->next) + if (ar == handler->op_region) + break; + /* XXX no warning print here, after full-area and back regions are remade */ + if (ar) + CTX_wm_region_set(C, ar); + } + } + } +} + +bool WM_manipulatormap_cursor_set(const wmManipulatorMap *mmap, wmWindow *win) +{ + wmManipulator *mpr = mmap->mmap_context.highlight; + if (mpr && mpr->type->cursor_get) { + WM_cursor_set(win, mpr->type->cursor_get(mpr)); + return true; + } + + return false; +} + +bool wm_manipulatormap_highlight_set( + wmManipulatorMap *mmap, const bContext *C, wmManipulator *mpr, int part) +{ + if ((mpr != mmap->mmap_context.highlight) || + (mpr && part != mpr->highlight_part)) + { + if (mmap->mmap_context.highlight) { + mmap->mmap_context.highlight->state &= ~WM_MANIPULATOR_STATE_HIGHLIGHT; + mmap->mmap_context.highlight->highlight_part = -1; + } + + mmap->mmap_context.highlight = mpr; + + if (mpr) { + mpr->state |= WM_MANIPULATOR_STATE_HIGHLIGHT; + mpr->highlight_part = part; + mmap->mmap_context.last_cursor = -1; + + if (C && mpr->type->cursor_get) { + wmWindow *win = CTX_wm_window(C); + mmap->mmap_context.last_cursor = win->cursor; + WM_cursor_set(win, mpr->type->cursor_get(mpr)); + } + } + else { + if (C && mmap->mmap_context.last_cursor != -1) { + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, mmap->mmap_context.last_cursor); + } + } + + /* tag the region for redraw */ + if (C) { + ARegion *ar = CTX_wm_region(C); + ED_region_tag_redraw(ar); + } + + return true; + } + + return false; +} + +wmManipulator *wm_manipulatormap_highlight_get(wmManipulatorMap *mmap) +{ + return mmap->mmap_context.highlight; +} + +/** + * Caller should call exit when (enable == False). + */ +void wm_manipulatormap_modal_set( + wmManipulatorMap *mmap, bContext *C, wmManipulator *mpr, const wmEvent *event, bool enable) +{ + if (enable) { + BLI_assert(mmap->mmap_context.modal == NULL); + wmWindow *win = CTX_wm_window(C); + + WM_tooltip_clear(C, win); + + if (mpr->type->invoke && + (mpr->type->modal || mpr->custom_modal)) + { + const int retval = mpr->type->invoke(C, mpr, event); + if ((retval & OPERATOR_RUNNING_MODAL) == 0) { + return; + } + } + + mpr->state |= WM_MANIPULATOR_STATE_MODAL; + mmap->mmap_context.modal = mpr; + + if ((mpr->flag & WM_MANIPULATOR_GRAB_CURSOR) && + (event->is_motion_absolute == false)) + { + WM_cursor_grab_enable(win, true, true, NULL); + copy_v2_v2_int(mmap->mmap_context.event_xy, &event->x); + mmap->mmap_context.event_grabcursor = win->grabcursor; + } + else { + mmap->mmap_context.event_xy[0] = INT_MAX; + } + + struct wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part); + if (mpop && mpop->type) { + const int retval = WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr); + if ((retval & OPERATOR_RUNNING_MODAL) == 0) { + wm_manipulatormap_modal_set(mmap, C, mpr, event, false); + } + + /* we failed to hook the manipulator to the operator handler or operator was cancelled, return */ + if (!mmap->mmap_context.modal) { + mpr->state &= ~WM_MANIPULATOR_STATE_MODAL; + MEM_SAFE_FREE(mpr->interaction_data); + } + return; + } + } + else { + BLI_assert(ELEM(mmap->mmap_context.modal, NULL, mpr)); + + /* deactivate, manipulator but first take care of some stuff */ + if (mpr) { + mpr->state &= ~WM_MANIPULATOR_STATE_MODAL; + MEM_SAFE_FREE(mpr->interaction_data); + } + mmap->mmap_context.modal = NULL; + + if (C) { + wmWindow *win = CTX_wm_window(C); + if (mmap->mmap_context.event_xy[0] != INT_MAX) { + /* Check if some other part of Blender (typically operators) + * have adjusted the grab mode since it was set. + * If so: warp, so we have a predictable outcome. */ + if (mmap->mmap_context.event_grabcursor == win->grabcursor) { + WM_cursor_grab_disable(win, mmap->mmap_context.event_xy); + } + else { + WM_cursor_warp(win, UNPACK2(mmap->mmap_context.event_xy)); + } + } + ED_region_tag_redraw(CTX_wm_region(C)); + WM_event_add_mousemove(C); + } + + mmap->mmap_context.event_xy[0] = INT_MAX; + } +} + +wmManipulator *wm_manipulatormap_modal_get(wmManipulatorMap *mmap) +{ + return mmap->mmap_context.modal; +} + +wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len) +{ + *r_selected_len = mmap->mmap_context.select.len; + return mmap->mmap_context.select.items; +} + +ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap) +{ + return &mmap->groups; +} + +void WM_manipulatormap_message_subscribe( + bContext *C, wmManipulatorMap *mmap, ARegion *ar, struct wmMsgBus *mbus) +{ + for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + if (!WM_manipulator_group_type_poll(C, mgroup->type)) { + continue; + } + for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + if (mpr->flag & WM_MANIPULATOR_HIDDEN) { + continue; + } + WM_manipulator_target_property_subscribe_all(mpr, mbus, ar); + } + if (mgroup->type->message_subscribe != NULL) { + mgroup->type->message_subscribe(C, mgroup, mbus); + } + } +} + +/** \} */ /* wmManipulatorMap */ + + +/* -------------------------------------------------------------------- */ +/** \name Tooltip Handling + * + * \{ */ + +struct ARegion *WM_manipulatormap_tooltip_init( + struct bContext *C, struct ARegion *ar, bool *r_exit_on_event) +{ + wmManipulatorMap *mmap = ar->manipulator_map; + *r_exit_on_event = true; + if (mmap) { + wmManipulator *mpr = mmap->mmap_context.highlight; + if (mpr) { + return UI_tooltip_create_from_manipulator(C, mpr); + } + } + return NULL; +} + +/** \} */ /* wmManipulatorMapType */ + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorMapType + * + * \{ */ + +wmManipulatorMapType *WM_manipulatormaptype_find( + const struct wmManipulatorMapType_Params *mmap_params) +{ + for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) { + if (mmap_type->spaceid == mmap_params->spaceid && + mmap_type->regionid == mmap_params->regionid) + { + return mmap_type; + } + } + + return NULL; +} + +wmManipulatorMapType *WM_manipulatormaptype_ensure( + const struct wmManipulatorMapType_Params *mmap_params) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_find(mmap_params); + + if (mmap_type) { + return mmap_type; + } + + mmap_type = MEM_callocN(sizeof(wmManipulatorMapType), "manipulatortype list"); + mmap_type->spaceid = mmap_params->spaceid; + mmap_type->regionid = mmap_params->regionid; + BLI_addhead(&manipulatormaptypes, mmap_type); + + return mmap_type; +} + +void wm_manipulatormaptypes_free(void) +{ + for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first, *mmap_type_next; + mmap_type; + mmap_type = mmap_type_next) + { + mmap_type_next = mmap_type->next; + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_next; + wgt_ref; + wgt_ref = wgt_next) + { + wgt_next = wgt_ref->next; + WM_manipulatormaptype_group_free(wgt_ref); + } + MEM_freeN(mmap_type); + } +} + +/** + * Initialize keymaps for all existing manipulator-groups + */ +void wm_manipulators_keymap(wmKeyConfig *keyconf) +{ + /* we add this item-less keymap once and use it to group manipulator-group keymaps into it */ + WM_keymap_find(keyconf, "Manipulators", 0, 0); + + for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) { + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) { + wm_manipulatorgrouptype_setup_keymap(wgt_ref->type, keyconf); + } + } +} + +/** \} */ /* wmManipulatorMapType */ + +/* -------------------------------------------------------------------- */ +/** \name Updates for Dynamic Type Registraion + * + * \{ */ + + +void WM_manipulatorconfig_update_tag_init( + wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt) +{ + /* tag for update on next use */ + mmap_type->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT); + wgt->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT); + + wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT; +} + +void WM_manipulatorconfig_update_tag_remove( + wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt) +{ + /* tag for update on next use */ + mmap_type->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE; + wgt->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE; + + wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE; +} + +/** + * Run incase new types have been added (runs often, early exit where possible). + * Follows #WM_keyconfig_update concentions. + */ +void WM_manipulatorconfig_update(struct Main *bmain) +{ + if (G.background) + return; + + if (wm_mmap_type_update_flag == 0) + return; + + if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) { + for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; + mmap_type; + mmap_type = mmap_type->next) + { + if (mmap_type->type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) { + mmap_type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_REMOVE; + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_ref_next; + wgt_ref; + wgt_ref = wgt_ref_next) + { + wgt_ref_next = wgt_ref->next; + if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_REMOVE) { + wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_REMOVE; + WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt_ref->type); + } + } + } + } + + wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE; + } + + if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT) { + for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; + mmap_type; + mmap_type = mmap_type->next) + { + const uchar type_update_all = WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT; + if (mmap_type->type_update_flag & type_update_all) { + mmap_type->type_update_flag &= ~type_update_all; + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; + wgt_ref; + wgt_ref = wgt_ref->next) + { + if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) { + WM_manipulatormaptype_group_init_runtime_keymap(bmain, wgt_ref->type); + wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT; + } + + if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_INIT) { + WM_manipulatormaptype_group_init_runtime(bmain, mmap_type, wgt_ref->type); + wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_INIT; + } + } + } + } + + wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT; + } +} + +/** \} */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c new file mode 100644 index 00000000000..78d12e4e4ba --- /dev/null +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c @@ -0,0 +1,364 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c + * \ingroup wm + */ + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "BKE_context.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "WM_message.h" + +#include "wm.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +/* own includes */ +#include "wm_gizmo_wmapi.h" +#include "wm_gizmo_intern.h" + +/* -------------------------------------------------------------------- */ + +/** \name Property Definition + * \{ */ + +BLI_INLINE wmManipulatorProperty *wm_manipulator_target_property_array(wmManipulator *mpr) +{ + return (wmManipulatorProperty *)(POINTER_OFFSET(mpr, mpr->type->struct_size)); +} + +wmManipulatorProperty *WM_manipulator_target_property_array(wmManipulator *mpr) +{ + return wm_manipulator_target_property_array(mpr); +} + +wmManipulatorProperty *WM_manipulator_target_property_at_index(wmManipulator *mpr, int index) +{ + BLI_assert(index < mpr->type->target_property_defs_len); + BLI_assert(index != -1); + wmManipulatorProperty *mpr_prop_array = wm_manipulator_target_property_array(mpr); + return &mpr_prop_array[index]; +} + +wmManipulatorProperty *WM_manipulator_target_property_find(wmManipulator *mpr, const char *idname) +{ + int index = BLI_findstringindex( + &mpr->type->target_property_defs, idname, offsetof(wmManipulatorPropertyType, idname)); + if (index != -1) { + return WM_manipulator_target_property_at_index(mpr, index); + } + else { + return NULL; + } +} + +void WM_manipulator_target_property_def_rna_ptr( + wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type, + PointerRNA *ptr, PropertyRNA *prop, int index) +{ + wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type); + + /* if manipulator evokes an operator we cannot use it for property manipulation */ + BLI_assert(mpr->op_data == NULL); + + mpr_prop->type = mpr_prop_type; + + mpr_prop->ptr = *ptr; + mpr_prop->prop = prop; + mpr_prop->index = index; + + if (mpr->type->property_update) { + mpr->type->property_update(mpr, mpr_prop); + } +} + +void WM_manipulator_target_property_def_rna( + wmManipulator *mpr, const char *idname, + PointerRNA *ptr, const char *propname, int index) +{ + const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname); + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); + WM_manipulator_target_property_def_rna_ptr(mpr, mpr_prop_type, ptr, prop, index); +} + +void WM_manipulator_target_property_def_func_ptr( + wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type, + const wmManipulatorPropertyFnParams *params) +{ + wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type); + + /* if manipulator evokes an operator we cannot use it for property manipulation */ + BLI_assert(mpr->op_data == NULL); + + mpr_prop->type = mpr_prop_type; + + mpr_prop->custom_func.value_get_fn = params->value_get_fn; + mpr_prop->custom_func.value_set_fn = params->value_set_fn; + mpr_prop->custom_func.range_get_fn = params->range_get_fn; + mpr_prop->custom_func.free_fn = params->free_fn; + mpr_prop->custom_func.user_data = params->user_data; + + if (mpr->type->property_update) { + mpr->type->property_update(mpr, mpr_prop); + } +} + +void WM_manipulator_target_property_def_func( + wmManipulator *mpr, const char *idname, + const wmManipulatorPropertyFnParams *params) +{ + const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname); + WM_manipulator_target_property_def_func_ptr(mpr, mpr_prop_type, params); +} + +void WM_manipulator_target_property_clear_rna_ptr( + wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type) +{ + wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type); + + /* if manipulator evokes an operator we cannot use it for property manipulation */ + BLI_assert(mpr->op_data == NULL); + + mpr_prop->type = NULL; + + mpr_prop->ptr = PointerRNA_NULL; + mpr_prop->prop = NULL; + mpr_prop->index = -1; +} + +void WM_manipulator_target_property_clear_rna( + wmManipulator *mpr, const char *idname) +{ + const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname); + WM_manipulator_target_property_clear_rna_ptr(mpr, mpr_prop_type); +} + + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Property Access + * \{ */ + +bool WM_manipulator_target_property_is_valid_any(wmManipulator *mpr) +{ + wmManipulatorProperty *mpr_prop_array = wm_manipulator_target_property_array(mpr); + for (int i = 0; i < mpr->type->target_property_defs_len; i++) { + wmManipulatorProperty *mpr_prop = &mpr_prop_array[i]; + if (WM_manipulator_target_property_is_valid(mpr_prop)) { + return true; + } + } + return false; +} + +bool WM_manipulator_target_property_is_valid(const wmManipulatorProperty *mpr_prop) +{ + return ((mpr_prop->prop != NULL) || + (mpr_prop->custom_func.value_get_fn && mpr_prop->custom_func.value_set_fn)); +} + +float WM_manipulator_target_property_value_get( + const wmManipulator *mpr, wmManipulatorProperty *mpr_prop) +{ + if (mpr_prop->custom_func.value_get_fn) { + float value = 0.0f; + BLI_assert(mpr_prop->type->array_length == 1); + mpr_prop->custom_func.value_get_fn(mpr, mpr_prop, &value); + return value; + } + + if (mpr_prop->index == -1) { + return RNA_property_float_get(&mpr_prop->ptr, mpr_prop->prop); + } + else { + return RNA_property_float_get_index(&mpr_prop->ptr, mpr_prop->prop, mpr_prop->index); + } +} + +void WM_manipulator_target_property_value_set( + bContext *C, const wmManipulator *mpr, + wmManipulatorProperty *mpr_prop, const float value) +{ + if (mpr_prop->custom_func.value_set_fn) { + BLI_assert(mpr_prop->type->array_length == 1); + mpr_prop->custom_func.value_set_fn(mpr, mpr_prop, &value); + return; + } + + /* reset property */ + if (mpr_prop->index == -1) { + RNA_property_float_set(&mpr_prop->ptr, mpr_prop->prop, value); + } + else { + RNA_property_float_set_index(&mpr_prop->ptr, mpr_prop->prop, mpr_prop->index, value); + } + RNA_property_update(C, &mpr_prop->ptr, mpr_prop->prop); +} + +void WM_manipulator_target_property_value_get_array( + const wmManipulator *mpr, wmManipulatorProperty *mpr_prop, + float *value) +{ + if (mpr_prop->custom_func.value_get_fn) { + mpr_prop->custom_func.value_get_fn(mpr, mpr_prop, value); + return; + } + RNA_property_float_get_array(&mpr_prop->ptr, mpr_prop->prop, value); +} + +void WM_manipulator_target_property_value_set_array( + bContext *C, const wmManipulator *mpr, wmManipulatorProperty *mpr_prop, + const float *value) +{ + if (mpr_prop->custom_func.value_set_fn) { + mpr_prop->custom_func.value_set_fn(mpr, mpr_prop, value); + return; + } + RNA_property_float_set_array(&mpr_prop->ptr, mpr_prop->prop, value); + + RNA_property_update(C, &mpr_prop->ptr, mpr_prop->prop); +} + +bool WM_manipulator_target_property_range_get( + const wmManipulator *mpr, wmManipulatorProperty *mpr_prop, + float range[2]) +{ + if (mpr_prop->custom_func.value_get_fn) { + if (mpr_prop->custom_func.range_get_fn) { + mpr_prop->custom_func.range_get_fn(mpr, mpr_prop, range); + return true; + } + else { + return false; + + } + } + + float step, precision; + RNA_property_float_ui_range(&mpr_prop->ptr, mpr_prop->prop, &range[0], &range[1], &step, &precision); + return true; +} + +int WM_manipulator_target_property_array_length( + const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop) +{ + if (mpr_prop->custom_func.value_get_fn) { + return mpr_prop->type->array_length; + } + return RNA_property_array_length(&mpr_prop->ptr, mpr_prop->prop); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Property Define + * \{ */ + +const wmManipulatorPropertyType *WM_manipulatortype_target_property_find( + const wmManipulatorType *wt, const char *idname) +{ + return BLI_findstring(&wt->target_property_defs, idname, offsetof(wmManipulatorPropertyType, idname)); +} + +void WM_manipulatortype_target_property_def( + wmManipulatorType *wt, const char *idname, int data_type, int array_length) +{ + wmManipulatorPropertyType *mpt; + + BLI_assert(WM_manipulatortype_target_property_find(wt, idname) == NULL); + + const uint idname_size = strlen(idname) + 1; + mpt = MEM_callocN(sizeof(wmManipulatorPropertyType) + idname_size, __func__); + memcpy(mpt->idname, idname, idname_size); + mpt->data_type = data_type; + mpt->array_length = array_length; + mpt->index_in_type = wt->target_property_defs_len; + wt->target_property_defs_len += 1; + BLI_addtail(&wt->target_property_defs, mpt); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ + +/** \name Property Utilities + * \{ */ + +void WM_manipulator_do_msg_notify_tag_refresh( + bContext *UNUSED(C), wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val) +{ + ARegion *ar = msg_val->owner; + wmManipulatorMap *mmap = msg_val->user_data; + + ED_region_tag_redraw(ar); + WM_manipulatormap_tag_refresh(mmap); +} + +/** + * Runs on the "prepare draw" pass, + * drawing the region clears. + */ +void WM_manipulator_target_property_subscribe_all( + wmManipulator *mpr, struct wmMsgBus *mbus, ARegion *ar) +{ + if (mpr->type->target_property_defs_len) { + wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr); + for (int i = 0; i < mpr->type->target_property_defs_len; i++) { + wmManipulatorProperty *mpr_prop = &mpr_prop_array[i]; + if (WM_manipulator_target_property_is_valid(mpr_prop)) { + if (mpr_prop->prop) { + WM_msg_subscribe_rna( + mbus, &mpr_prop->ptr, mpr_prop->prop, + &(const wmMsgSubscribeValue){ + .owner = ar, + .user_data = ar, + .notify = ED_region_do_msg_notify_tag_redraw, + }, __func__); + WM_msg_subscribe_rna( + mbus, &mpr_prop->ptr, mpr_prop->prop, + &(const wmMsgSubscribeValue){ + .owner = ar, + .user_data = mpr->parent_mgroup->parent_mmap, + .notify = WM_manipulator_do_msg_notify_tag_refresh, + }, __func__); + } + } + } + } +} + +/** \} */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c new file mode 100644 index 00000000000..4f31afb5b56 --- /dev/null +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c @@ -0,0 +1,212 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmo/intern/wm_gizmo_type.c + * \ingroup wm + */ + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "BKE_context.h" +#include "BKE_main.h" + +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" + +/* only for own init/exit calls (wm_manipulatortype_init/wm_manipulatortype_free) */ +#include "wm.h" + +/* own includes */ +#include "wm_gizmo_wmapi.h" +#include "wm_gizmo_intern.h" + + +/** \name Manipulator Type Append + * + * \note This follows conventions from #WM_operatortype_find #WM_operatortype_append & friends. + * \{ */ + +static GHash *global_manipulatortype_hash = NULL; + +const wmManipulatorType *WM_manipulatortype_find(const char *idname, bool quiet) +{ + if (idname[0]) { + wmManipulatorType *wt; + + wt = BLI_ghash_lookup(global_manipulatortype_hash, idname); + if (wt) { + return wt; + } + + if (!quiet) { + printf("search for unknown manipulator '%s'\n", idname); + } + } + else { + if (!quiet) { + printf("search for empty manipulator\n"); + } + } + + return NULL; +} + +/* caller must free */ +void WM_manipulatortype_iter(GHashIterator *ghi) +{ + BLI_ghashIterator_init(ghi, global_manipulatortype_hash); +} + +static wmManipulatorType *wm_manipulatortype_append__begin(void) +{ + wmManipulatorType *wt = MEM_callocN(sizeof(wmManipulatorType), "manipulatortype"); + wt->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_ManipulatorProperties); +#if 0 + /* Set the default i18n context now, so that opfunc can redefine it if needed! */ + RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); + ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; +#endif + return wt; +} +static void wm_manipulatortype_append__end(wmManipulatorType *wt) +{ + BLI_assert(wt->struct_size >= sizeof(wmManipulator)); + + RNA_def_struct_identifier(&BLENDER_RNA, wt->srna, wt->idname); + + BLI_ghash_insert(global_manipulatortype_hash, (void *)wt->idname, wt); +} + +void WM_manipulatortype_append(void (*wtfunc)(struct wmManipulatorType *)) +{ + wmManipulatorType *wt = wm_manipulatortype_append__begin(); + wtfunc(wt); + wm_manipulatortype_append__end(wt); +} + +void WM_manipulatortype_append_ptr(void (*wtfunc)(struct wmManipulatorType *, void *), void *userdata) +{ + wmManipulatorType *mt = wm_manipulatortype_append__begin(); + wtfunc(mt, userdata); + wm_manipulatortype_append__end(mt); +} + +/** + * Free but don't remove from ghash. + */ +static void manipulatortype_free(wmManipulatorType *wt) +{ + if (wt->ext.srna) { /* python manipulator, allocs own string */ + MEM_freeN((void *)wt->idname); + } + + BLI_freelistN(&wt->target_property_defs); + MEM_freeN(wt); +} + +/** + * \param C: May be NULL. + */ +static void manipulatortype_unlink( + bContext *C, Main *bmain, wmManipulatorType *wt) +{ + /* Free instances. */ + for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) { + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + for (ARegion *ar = lb->first; ar; ar = ar->next) { + wmManipulatorMap *mmap = ar->manipulator_map; + if (mmap) { + wmManipulatorGroup *mgroup; + for (mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + for (wmManipulator *mpr = mgroup->manipulators.first, *mpr_next; mpr; mpr = mpr_next) { + mpr_next = mpr->next; + BLI_assert(mgroup->parent_mmap == mmap); + if (mpr->type == wt) { + WM_manipulator_unlink(&mgroup->manipulators, mgroup->parent_mmap, mpr, C); + ED_region_tag_redraw(ar); + } + } + } + } + } + } + } + } +} + +void WM_manipulatortype_remove_ptr(bContext *C, Main *bmain, wmManipulatorType *wt) +{ + BLI_assert(wt == WM_manipulatortype_find(wt->idname, false)); + + BLI_ghash_remove(global_manipulatortype_hash, wt->idname, NULL, NULL); + + manipulatortype_unlink(C, bmain, wt); + + manipulatortype_free(wt); +} + +bool WM_manipulatortype_remove(bContext *C, Main *bmain, const char *idname) +{ + wmManipulatorType *wt = BLI_ghash_lookup(global_manipulatortype_hash, idname); + + if (wt == NULL) { + return false; + } + + WM_manipulatortype_remove_ptr(C, bmain, wt); + + return true; +} + +static void wm_manipulatortype_ghash_free_cb(wmManipulatorType *mt) +{ + manipulatortype_free(mt); +} + +void wm_manipulatortype_free(void) +{ + BLI_ghash_free(global_manipulatortype_hash, NULL, (GHashValFreeFP)wm_manipulatortype_ghash_free_cb); + global_manipulatortype_hash = NULL; +} + +/* called on initialize WM_init() */ +void wm_manipulatortype_init(void) +{ + /* reserve size is set based on blender default setup */ + global_manipulatortype_hash = BLI_ghash_str_new_ex("wm_manipulatortype_init gh", 128); +} + +/** \} */ diff --git a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h new file mode 100644 index 00000000000..8d28febbd5d --- /dev/null +++ b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h @@ -0,0 +1,88 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmo/wm_gizmo_fn.h + * \ingroup wm + * + * Callback function definitions, needed for both Types & API headers. + */ + +#ifndef __WM_GIZMO_FN_H__ +#define __WM_GIZMO_FN_H__ + +#include "BLI_compiler_attrs.h" + +/* wmManipulatorGroup */ +typedef bool (*wmManipulatorGroupFnPoll)( + const struct bContext *, struct wmManipulatorGroupType *) + ATTR_WARN_UNUSED_RESULT; +typedef void (*wmManipulatorGroupFnInit)( + const struct bContext *, struct wmManipulatorGroup *); +typedef void (*wmManipulatorGroupFnRefresh)( + const struct bContext *, struct wmManipulatorGroup *); +typedef void (*wmManipulatorGroupFnDrawPrepare)( + const struct bContext *, struct wmManipulatorGroup *); +typedef struct wmKeyMap *(*wmManipulatorGroupFnSetupKeymap)( + const struct wmManipulatorGroupType *, struct wmKeyConfig *) + ATTR_WARN_UNUSED_RESULT; +typedef void (*wmManipulatorGroupFnMsgBusSubscribe)( + const struct bContext *, struct wmManipulatorGroup *, struct wmMsgBus *); + +/* wmManipulator */ +/* See: wmManipulatorType for docs on each type. */ + +typedef void (*wmManipulatorFnSetup)(struct wmManipulator *); +typedef void (*wmManipulatorFnDraw)(const struct bContext *, struct wmManipulator *); +typedef void (*wmManipulatorFnDrawSelect)(const struct bContext *, struct wmManipulator *, int); +typedef int (*wmManipulatorFnTestSelect)(struct bContext *, struct wmManipulator *, const struct wmEvent *); +typedef int (*wmManipulatorFnModal)(struct bContext *, struct wmManipulator *, const struct wmEvent *, eWM_ManipulatorTweak); +typedef void (*wmManipulatorFnPropertyUpdate)(struct wmManipulator *, struct wmManipulatorProperty *); +typedef void (*wmManipulatorFnMatrixBasisGet)(const struct wmManipulator *, float[4][4]); +typedef int (*wmManipulatorFnInvoke)(struct bContext *, struct wmManipulator *, const struct wmEvent *); +typedef void (*wmManipulatorFnExit)(struct bContext *, struct wmManipulator *, const bool); +typedef int (*wmManipulatorFnCursorGet)(struct wmManipulator *); +typedef void (*wmManipulatorFnSelectRefresh)(struct wmManipulator *); +typedef void (*wmManipulatorFnFree)(struct wmManipulator *); + +/* wmManipulatorProperty ('value' type defined by 'wmManipulatorProperty.data_type') */ +typedef void (*wmManipulatorPropertyFnGet)( + const struct wmManipulator *, struct wmManipulatorProperty *, + /* typically 'float *' */ + void *value); +typedef void (*wmManipulatorPropertyFnSet)( + const struct wmManipulator *, struct wmManipulatorProperty *, + /* typically 'const float *' */ + const void *value); +typedef void (*wmManipulatorPropertyFnRangeGet)( + const struct wmManipulator *, struct wmManipulatorProperty *, + /* typically 'float[2]' */ + void *range); +typedef void (*wmManipulatorPropertyFnFree)( + const struct wmManipulator *, struct wmManipulatorProperty *); + +typedef struct wmManipulatorPropertyFnParams { + wmManipulatorPropertyFnGet value_get_fn; + wmManipulatorPropertyFnSet value_set_fn; + wmManipulatorPropertyFnRangeGet range_get_fn; + wmManipulatorPropertyFnFree free_fn; + void *user_data; +} wmManipulatorPropertyFnParams; + +#endif /* __WM_GIZMO_FN_H__ */ diff --git a/source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h b/source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h new file mode 100644 index 00000000000..80418cbfeb3 --- /dev/null +++ b/source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h @@ -0,0 +1,97 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/gizmo/wm_gizmo_wmapi.h + * \ingroup wm + * + * \name Manipulators Window Manager API + * API for usage in window manager code only. It should contain all functionality + * needed to hook up the manipulator system with Blender's window manager. It's + * mostly the event system that needs to communicate with manipulator code. + * + * Only included in wm.h and lower level files. + */ + + +#ifndef __WM_GIZMO_WMAPI_H__ +#define __WM_GIZMO_WMAPI_H__ + +struct wmEventHandler; +struct wmManipulatorMap; +struct wmOperatorType; +struct wmOperator; + + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + +/* wm_manipulator_type.c, for init/exit */ +void wm_manipulatortype_free(void); +void wm_manipulatortype_init(void); + +/* wm_manipulatorgroup_type.c, for init/exit */ +void wm_manipulatorgrouptype_free(void); +void wm_manipulatorgrouptype_init(void); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +void MANIPULATORGROUP_OT_manipulator_select(struct wmOperatorType *ot); +void MANIPULATORGROUP_OT_manipulator_tweak(struct wmOperatorType *ot); + +bool wm_manipulatorgroup_is_any_selected(const struct wmManipulatorGroup *mgroup); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +void wm_manipulatormap_remove(struct wmManipulatorMap *mmap); + +void wm_manipulators_keymap(struct wmKeyConfig *keyconf); + +void wm_manipulatormaps_handled_modal_update( + bContext *C, struct wmEvent *event, struct wmEventHandler *handler); +void wm_manipulatormap_handler_context(bContext *C, struct wmEventHandler *handler); + +struct wmManipulator *wm_manipulatormap_highlight_find( + struct wmManipulatorMap *mmap, bContext *C, const struct wmEvent *event, + int *r_part); +bool wm_manipulatormap_highlight_set( + struct wmManipulatorMap *mmap, const bContext *C, + struct wmManipulator *mpr, int part); +struct wmManipulator *wm_manipulatormap_highlight_get(struct wmManipulatorMap *mmap); +void wm_manipulatormap_modal_set( + struct wmManipulatorMap *mmap, bContext *C, struct wmManipulator *mpr, + const struct wmEvent *event, bool enable); + +struct wmManipulator *wm_manipulatormap_modal_get(struct wmManipulatorMap *mmap); +struct wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len); +struct ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMapType */ + +void wm_manipulatormaptypes_free(void); + +#endif /* __WM_GIZMO_WMAPI_H__ */ |