From 6e358a1d069f9b1bd4582edae130e14106fe0698 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 7 Oct 2016 16:34:55 +0200 Subject: Custom Manipulators Core Backend This commit lands the core backend of the Custom Manipulators project onto the blender2.8 branch. It is a generic backend for managinig interactive on-screen controls that can be integrated into any 2D or 3D edito. It's also already integrated into the window-manager and editor code where needed. NOTE: The changes here should not be visible for users at all. It's really just a back-end patch. Neither does this include any RNA or Python integration. Of course, there's still lots of work ahead for custom manipulators, but this is a big milestone. WIP code that actually uses this backend can be found in the 'custom-manipulators' branch (previously called 'wiggly-widgets'). The work here isn't completely my own, all the initial work was done by @Antony Riakiotakis (psy-fi) and - although it has changed a lot since them - it's still the same in essence. He definitely deserves a big credit! Some changes in this patch were also done by @Campbell Barton (campbellbarton). Thank you guys! Merge accepted by @brecht and @merwin. Patch: https://developer.blender.org/D2232 Code documentation: https://wiki.blender.org/index.php/Dev:2.8/Source/Custom_Manipulator Main task: https://developer.blender.org/T47343 More info: https://code.blender.org/2015/09/the-custom-manipulator-project-widget-project/ --- source/blender/blenkernel/BKE_screen.h | 6 + source/blender/blenkernel/intern/screen.c | 14 + source/blender/blenlib/BLI_math_vector.h | 1 + source/blender/blenlib/intern/math_vector.c | 14 + source/blender/blenloader/intern/readfile.c | 1 + source/blender/editors/interface/resources.c | 2 + source/blender/editors/screen/screen_edit.c | 3 + source/blender/editors/space_api/spacetypes.c | 11 +- source/blender/makesdna/DNA_screen_types.h | 5 +- source/blender/makesdna/DNA_userdef_types.h | 3 +- source/blender/makesdna/DNA_view3d_types.h | 4 +- source/blender/windowmanager/CMakeLists.txt | 11 + source/blender/windowmanager/WM_api.h | 6 + source/blender/windowmanager/WM_types.h | 4 + .../blender/windowmanager/intern/wm_event_system.c | 75 +- source/blender/windowmanager/intern/wm_init_exit.c | 4 + source/blender/windowmanager/intern/wm_operators.c | 8 + .../manipulators/WM_manipulator_api.h | 110 +++ .../manipulators/WM_manipulator_types.h | 167 +++++ .../manipulator_library_intern.h | 100 +++ .../manipulator_library_utils.c | 171 +++++ .../manipulators/intern/wm_manipulator.c | 404 +++++++++++ .../manipulators/intern/wm_manipulator_intern.h | 230 +++++++ .../manipulators/intern/wm_manipulatorgroup.c | 586 ++++++++++++++++ .../manipulators/intern/wm_manipulatormap.c | 760 +++++++++++++++++++++ .../manipulators/wm_manipulator_wmapi.h | 90 +++ source/blender/windowmanager/wm.h | 2 + source/blender/windowmanager/wm_event_system.h | 3 +- source/blender/windowmanager/wm_event_types.h | 2 + 29 files changed, 2786 insertions(+), 11 deletions(-) create mode 100644 source/blender/windowmanager/manipulators/WM_manipulator_api.h create mode 100644 source/blender/windowmanager/manipulators/WM_manipulator_types.h create mode 100644 source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_intern.h create mode 100644 source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_utils.c create mode 100644 source/blender/windowmanager/manipulators/intern/wm_manipulator.c create mode 100644 source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h create mode 100644 source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c create mode 100644 source/blender/windowmanager/manipulators/intern/wm_manipulatormap.c create mode 100644 source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h (limited to 'source') diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 14e978b23f2..010810ad0cc 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -49,6 +49,7 @@ struct bScreen; struct uiLayout; struct uiList; struct wmKeyConfig; +struct wmManipulatorMap; struct wmNotifier; struct wmWindow; struct wmWindowManager; @@ -96,6 +97,9 @@ typedef struct SpaceType { /* on startup, define dropboxes for spacetype+regions */ void (*dropboxes)(void); + /* initialize manipulator-map-types and manipulator-group-types with the region */ + void (*manipulators)(void); + /* return context data */ int (*context)(const struct bContext *, const char *, struct bContextDataResult *); @@ -284,6 +288,8 @@ void BKE_spacedata_id_unref(struct ScrArea *sa, struct SpaceLink *sl, struct ID struct ARegion *BKE_area_region_copy(struct SpaceType *st, struct ARegion *ar); void BKE_area_region_free(struct SpaceType *st, struct ARegion *ar); void BKE_screen_area_free(struct ScrArea *sa); +/* Manipulator-maps of a region need to be freed with the region. Uses callback to avoid low-level call. */ +void BKE_region_callback_free_manipulatormap_set(void (*callback)(struct wmManipulatorMap *)); struct ARegion *BKE_area_find_region_type(struct ScrArea *sa, int type); struct ARegion *BKE_area_find_region_active_win(struct ScrArea *sa); diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 857bd5447c8..14820565478 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -180,6 +180,7 @@ ARegion *BKE_area_region_copy(SpaceType *st, ARegion *ar) BLI_listbase_clear(&newar->panels_category_active); BLI_listbase_clear(&newar->ui_lists); newar->swinid = 0; + newar->manipulator_map = NULL; newar->regiontimer = NULL; /* use optional regiondata callback */ @@ -288,6 +289,17 @@ void BKE_spacedata_id_unref(struct ScrArea *sa, struct SpaceLink *sl, struct ID } } + +/** + * Avoid bad-level calls to #WM_manipulatormap_delete. + */ +static void (*region_free_manipulatormap_callback)(struct wmManipulatorMap *) = NULL; + +void BKE_region_callback_free_manipulatormap_set(void (*callback)(struct wmManipulatorMap *)) +{ + region_free_manipulatormap_callback = callback; +} + /* not region itself */ void BKE_area_region_free(SpaceType *st, ARegion *ar) { @@ -337,6 +349,8 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar) MEM_freeN(uilst->properties); } } + + region_free_manipulatormap_callback(ar->manipulator_map); BLI_freelistN(&ar->ui_lists); BLI_freelistN(&ar->ui_previews); BLI_freelistN(&ar->panels_category); diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index e797a630948..fbecffc1270 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -304,6 +304,7 @@ void ortho_basis_v3v3_v3(float r_n1[3], float r_n2[3], const float n[3]); void ortho_v3_v3(float out[3], const float v[3]); void ortho_v2_v2(float out[2], const float v[2]); void bisect_v3_v3v3v3(float r[3], const float a[3], const float b[3], const float c[3]); +void rotate_v2_v2fl(float r[2], const float p[2], const float angle); void rotate_v3_v3v3fl(float v[3], const float p[3], const float axis[3], const float angle); void rotate_normalized_v3_v3v3fl(float out[3], const float p[3], const float axis[3], const float angle); diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index 95d5c9fde87..90e39da5d46 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -771,6 +771,20 @@ void ortho_v2_v2(float out[2], const float v[2]) out[1] = v[0]; } +/** + * Rotate a point \a p by \a angle around origin (0, 0) + */ +void rotate_v2_v2fl(float r[2], const float p[2], const float angle) +{ + const float co = cosf(angle); + const float si = sinf(angle); + + BLI_assert(r != p); + + r[0] = co * p[0] - si * p[1]; + r[1] = si * p[0] + co * p[1]; +} + /** * Rotate a point \a p by \a angle around an arbitrary unit length \a axis. * http://local.wasp.uwa.edu.au/~pbourke/geometry/ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 4e1eb41c7cf..03fcf336700 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6516,6 +6516,7 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype) ar->type = NULL; ar->swap = 0; ar->do_draw = 0; + ar->manipulator_map = NULL; ar->regiontimer = NULL; memset(&ar->drawrct, 0, sizeof(ar->drawrct)); } diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 79fa7a7571a..2f7eb5d047c 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -1660,6 +1660,8 @@ void init_userdef_do_versions(void) U.tw_size = 25; /* percentage of window size */ U.tw_handlesize = 16; /* percentage of widget radius */ } + if (U.manipulator_scale == 0) + U.manipulator_scale = 75; if (U.pad_rot_angle == 0.0f) U.pad_rot_angle = 15.0f; diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 677a6472c72..3d30f2be445 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1069,6 +1069,9 @@ static void region_cursor_set(wmWindow *win, int swinid, int swin_changed) for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->swinid == swinid) { if (swin_changed || (ar->type && ar->type->event_cursor)) { + if (WM_manipulatormap_cursor_set(ar->manipulator_map, win)) { + return; + } ED_region_cursor_set(win, sa, ar); } return; diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index ac6e3123e4e..5ff1d758563 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -121,12 +121,17 @@ void ED_spacetypes_init(void) ED_operatortypes_view2d(); ED_operatortypes_ui(); - - /* register operators */ + + /* register types for operators and manipulators */ spacetypes = BKE_spacetypes_list(); for (type = spacetypes->first; type; type = type->next) { - if (type->operatortypes) + /* init manipulator types first, operator-types need them */ + if (type->manipulators) { + type->manipulators(); + } + if (type->operatortypes) { type->operatortypes(); + } } /* register internal render callbacks */ diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index e208ef39719..2efb9d1f1ac 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -265,9 +265,10 @@ typedef struct ARegion { ListBase ui_previews; /* uiPreview */ ListBase handlers; /* wmEventHandler */ ListBase panels_category; /* Panel categories runtime */ - + + struct wmManipulatorMap *manipulator_map; /* manipulator-map of this region */ struct wmTimer *regiontimer; /* blend in/out */ - + char *headerstr; /* use this string to draw info */ void *regiondata; /* XXX 2.50, need spacedata equivalent? */ } ARegion; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 759ebbfb5cb..30d81df07ef 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -490,6 +490,7 @@ typedef struct UserDef { short tb_leftmouse, tb_rightmouse; struct SolidLight light[3]; short tw_hotspot, tw_flag, tw_handlesize, tw_size; + short manipulator_scale, pad3[3]; short textimeout, texcollectrate; short wmdrawmethod; /* removed wmpad */ short dragthreshold; @@ -782,8 +783,6 @@ typedef enum eText_Draw_Options { USER_TEXT_DISABLE_AA = (1 << 0), } eText_Draw_Options; -/* tw_flag (transform widget) */ - /* gp_settings (Grease Pencil Settings) */ typedef enum eGP_UserdefSettings { GP_PAINT_DOSMOOTH = (1 << 0), diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index a2b470e062b..e2d156ee3ca 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -111,7 +111,7 @@ typedef struct RegionView3D { struct wmTimer *smooth_timer; - /* transform widget matrix */ + /* transform manipulator matrix */ float twmat[4][4]; float viewquat[4]; /* view rotation, must be kept normalized */ @@ -202,7 +202,7 @@ typedef struct View3D { short gridsubdiv; /* Number of subdivisions in the grid between each highlighted grid line */ char gridflag; - /* transform widget info */ + /* transform manipulator info */ char twtype, twmode, twflag; short flag3; diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index b6245a8c0d1..b5c19534b15 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -25,6 +25,8 @@ set(INC . + manipulators + manipulators/intern ../blenfont ../blenkernel ../blenlib @@ -68,6 +70,10 @@ set(SRC intern/wm_subwindow.c intern/wm_window.c intern/wm_stereo.c + manipulators/intern/wm_manipulator.c + manipulators/intern/wm_manipulatorgroup.c + manipulators/intern/wm_manipulatormap.c + manipulators/intern/manipulator_library/manipulator_library_utils.c WM_api.h WM_keymap.h @@ -80,6 +86,11 @@ set(SRC wm_files.h wm_subwindow.h wm_window.h + manipulators/WM_manipulator_api.h + manipulators/WM_manipulator_types.h + manipulators/wm_manipulator_wmapi.h + manipulators/intern/wm_manipulator_intern.h + manipulators/intern/manipulator_library/manipulator_library_intern.h ) if(WITH_AUDASPACE) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 2b82f1becb3..4d159c69053 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -42,6 +42,9 @@ #include "WM_keymap.h" #include "BLI_compiler_attrs.h" +/* Include external manipulator API's */ +#include "manipulators/WM_manipulator_api.h" + #ifdef __cplusplus extern "C" { #endif @@ -70,6 +73,9 @@ struct wmNDOFMotionData; #endif typedef struct wmJob wmJob; +typedef struct wmManipulator wmManipulator; +typedef struct wmManipulatorMap wmManipulatorMap; +typedef struct wmManipulatorMapType wmManipulatorMapType; /* general API */ void WM_init_state_size_set (int stax, int stay, int sizx, int sizy); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index ce4a69a1841..547968e2906 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -119,6 +119,7 @@ struct ImBuf; /* exported types for WM */ #include "wm_cursors.h" #include "wm_event_types.h" +#include "manipulators/WM_manipulator_types.h" /* ************** wmOperatorType ************************ */ @@ -566,6 +567,9 @@ typedef struct wmOperatorType { /* pointer to modal keymap, do not free! */ struct wmKeyMap *modalkeymap; + /* manipulator-group that is accessible while operator runs */ + wmManipulatorGroupType *mgrouptype; + /* python needs the operator type as well */ int (*pyop_poll)(struct bContext *, struct wmOperatorType *ot) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 7625f55be6e..a75b3b1a0e6 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1672,7 +1672,12 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand wm_handler_op_context(C, handler, event); wm_region_mouse_co(C, event); wm_event_modalkeymap(C, op, event, &dbl_click_disabled); - + + /* attach manipulator-map to handler if not there yet */ + if (ot->mgrouptype && !handler->manipulator_map) { + wm_manipulatorgroup_attach_to_modal_handler(C, handler, ot->mgrouptype, op); + } + if (ot->flag & OPTYPE_UNDO) wm->op_undo_depth++; @@ -1721,6 +1726,9 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand CTX_wm_region_set(C, NULL); } + /* update manipulators during modal handlers */ + wm_manipulatormaps_handled_modal_update(C, event, handler, ot); + /* remove modal handler, operator itself should have been canceled and freed */ if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) { WM_cursor_grab_disable(CTX_wm_window(C), NULL); @@ -2090,6 +2098,71 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers } } } + else if (handler->manipulator_map) { + ScrArea *area = CTX_wm_area(C); + ARegion *region = CTX_wm_region(C); + wmManipulatorMap *mmap = handler->manipulator_map; + wmManipulator *manipulator = wm_manipulatormap_get_highlighted_manipulator(mmap); + unsigned char part; + + wm_manipulatormap_handler_context(C, handler); + wm_region_mouse_co(C, event); + + /* handle manipulator highlighting */ + if (event->type == MOUSEMOVE && !wm_manipulatormap_get_active_manipulator(mmap)) { + manipulator = wm_manipulatormap_find_highlighted_manipulator(mmap, C, event, &part); + wm_manipulatormap_set_highlighted_manipulator(mmap, C, manipulator, part); + } + /* handle user configurable manipulator-map keymap */ + else if (manipulator) { + /* get user customized keymap from default one */ + const wmManipulatorGroup *highlightgroup = wm_manipulator_get_parent_group(manipulator); + const wmKeyMap *keymap = WM_keymap_active(wm, highlightgroup->type->keymap); + wmKeyMapItem *kmi; + + PRINT("%s: checking '%s' ...", __func__, keymap->idname); + + if (!keymap->poll || keymap->poll(C)) { + PRINT("pass\n"); + for (kmi = keymap->items.first; kmi; kmi = kmi->next) { + if (wm_eventmatch(event, kmi)) { + wmOperator *op = handler->op; + + PRINT("%s: item matched '%s'\n", __func__, kmi->idname); + + /* weak, but allows interactive callback to not use rawkey */ + event->keymap_idname = kmi->idname; + + /* handler->op is called later, we want keymap op to be triggered here */ + handler->op = NULL; + action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr); + handler->op = op; + + if (action & WM_HANDLER_BREAK) { + if (action & WM_HANDLER_HANDLED) { + if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) + printf("%s: handled - and pass on! '%s'\n", __func__, kmi->idname); + } + else { + PRINT("%s: un-handled '%s'\n", __func__, kmi->idname); + } + } + } + } + } + else { + PRINT("fail\n"); + } + } + + /* restore the area */ + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + if (handler->op) { + action |= wm_handler_operator_call(C, handlers, handler, event, NULL); + } + } else { /* modal, swallows all */ action |= wm_handler_operator_call(C, handlers, handler, event, NULL); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index c11c398c616..fbbde2a6d28 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -169,6 +169,7 @@ void WM_init(bContext *C, int argc, const char **argv) BKE_library_callback_free_window_manager_set(wm_close_and_free); /* library.c */ BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference); /* library.c */ + BKE_region_callback_free_manipulatormap_set(wm_manipulatormap_delete); /* screen.c */ BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference); /* library.c */ BKE_blender_callback_test_break_set(wm_window_testbreak); /* blender.c */ BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); /* screen.c */ @@ -529,6 +530,9 @@ void WM_exit_ext(bContext *C, const bool do_python) ED_gpencil_strokes_copybuf_free(); BKE_node_clipboard_clear(); + /* free manipulator-maps after freeing blender, so no deleted data get accessed during cleaning up of areas */ + wm_manipulatormaptypes_free(); + BLF_exit(); #ifdef WITH_INTERNATIONAL diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 87ef0596e52..33f12927f2b 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -491,6 +491,9 @@ void WM_operatortype_remove_ptr(wmOperatorType *ot) BLI_ghash_remove(global_ops_hash, ot->idname, NULL, NULL); WM_keyconfig_update_operatortype(); + if (ot->mgrouptype) { + WM_manipulatorgrouptype_unregister(NULL, G.main, ot->mgrouptype); + } MEM_freeN(ot); } @@ -4170,6 +4173,10 @@ void wm_operatortype_init(void) WM_operatortype_append(WM_OT_previews_ensure); WM_operatortype_append(WM_OT_previews_clear); WM_operatortype_append(WM_OT_doc_view_manual_ui_context); + + /* manipulators */ + WM_operatortype_append(MANIPULATORGROUP_OT_manipulator_select); + WM_operatortype_append(MANIPULATORGROUP_OT_manipulator_tweak); } /* circleselect-like modal operators */ @@ -4475,6 +4482,7 @@ void wm_window_keymap(wmKeyConfig *keyconf) RNA_float_set(kmi->ptr, "value", 1.0f / 1.5f); #endif /* WITH_INPUT_NDOF */ + wm_manipulators_keymap(keyconf); gesture_circle_modal_keymap(keyconf); gesture_border_modal_keymap(keyconf); gesture_zoom_border_modal_keymap(keyconf); diff --git a/source/blender/windowmanager/manipulators/WM_manipulator_api.h b/source/blender/windowmanager/manipulators/WM_manipulator_api.h new file mode 100644 index 00000000000..099ad7fd851 --- /dev/null +++ b/source/blender/windowmanager/manipulators/WM_manipulator_api.h @@ -0,0 +1,110 @@ +/* + * ***** 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/manipulators/WM_manipulator_api.h + * \ingroup wm + * + * \name Manipulator API + * \brief API for external use of wmManipulator types. + * + * Only included in WM_api.h + */ + + +#ifndef __WM_MANIPULATOR_API_H__ +#define __WM_MANIPULATOR_API_H__ + +struct ARegion; +struct Main; +struct wmKeyConfig; +struct wmManipulatorGroupType; +struct wmManipulatorMap; +struct wmManipulatorMapType; +struct wmManipulatorMapType_Params; + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + +struct wmManipulator *WM_manipulator_new( + void (*draw)(const struct bContext *, struct wmManipulator *), + void (*render_3d_intersection)(const struct bContext *, struct wmManipulator *, int), + int (*intersect)(struct bContext *, const struct wmEvent *, struct wmManipulator *), + int (*handler)(struct bContext *, const struct wmEvent *, struct wmManipulator *, const int)); +void WM_manipulator_delete( + ListBase *manipulatorlist, struct wmManipulatorMap *mmap, struct wmManipulator *manipulator, + struct bContext *C); + +void WM_manipulator_set_property(struct wmManipulator *, int slot, struct PointerRNA *ptr, const char *propname); +struct PointerRNA *WM_manipulator_set_operator(struct wmManipulator *, const char *opname); +void WM_manipulator_set_func_select( + struct wmManipulator *manipulator, + void (*select)(struct bContext *, struct wmManipulator *, const int action)); /* wmManipulatorSelectFunc */ +void WM_manipulator_set_origin(struct wmManipulator *manipulator, const float origin[3]); +void WM_manipulator_set_offset(struct wmManipulator *manipulator, const float offset[3]); +void WM_manipulator_set_flag(struct wmManipulator *manipulator, const int flag, const bool enable); +void WM_manipulator_set_scale(struct wmManipulator *manipulator, float scale); +void WM_manipulator_set_line_width(struct wmManipulator *manipulator, const float line_width); +void WM_manipulator_set_colors(struct wmManipulator *manipulator, const float col[4], const float col_hi[4]); + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +struct wmManipulatorGroupType *WM_manipulatorgrouptype_append( + struct wmManipulatorMapType *mmaptype, + void (*mgrouptype_func)(struct wmManipulatorGroupType *)); +struct wmManipulatorGroupType *WM_manipulatorgrouptype_append_runtime( + const struct Main *main, struct wmManipulatorMapType *mmaptype, + void (*mgrouptype_func)(struct wmManipulatorGroupType *)); +void WM_manipulatorgrouptype_init_runtime( + const struct Main *bmain, struct wmManipulatorMapType *mmaptype, + struct wmManipulatorGroupType *mgrouptype); +void WM_manipulatorgrouptype_unregister( + struct bContext *C, struct Main *bmain, struct wmManipulatorGroupType *mgroup); + +struct wmKeyMap *WM_manipulatorgroup_keymap_common( + const struct wmManipulatorGroupType *mgrouptype, struct wmKeyConfig *config); +struct wmKeyMap *WM_manipulatorgroup_keymap_common_sel( + const struct wmManipulatorGroupType *mgrouptype, struct wmKeyConfig *config); + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +struct wmManipulatorMapType *WM_manipulatormaptype_find( + const struct wmManipulatorMapType_Params *mmap_params); +struct wmManipulatorMapType *WM_manipulatormaptype_ensure( + const struct wmManipulatorMapType_Params *mmap_params); + +struct wmManipulatorMap *WM_manipulatormap_new_from_type( + const struct wmManipulatorMapType_Params *mmap_params); +void WM_manipulatormap_tag_refresh(struct wmManipulatorMap *mmap); +void WM_manipulatormap_draw(struct wmManipulatorMap *mmap, const struct bContext *C, const int 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); + +#endif /* __WM_MANIPULATOR_API_H__ */ + diff --git a/source/blender/windowmanager/manipulators/WM_manipulator_types.h b/source/blender/windowmanager/manipulators/WM_manipulator_types.h new file mode 100644 index 00000000000..284a3e9bd22 --- /dev/null +++ b/source/blender/windowmanager/manipulators/WM_manipulator_types.h @@ -0,0 +1,167 @@ +/* + * ***** 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/manipulators/WM_manipulator_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_MANIPULATOR_TYPES_H__ +#define __WM_MANIPULATOR_TYPES_H__ + +#include "BLI_compiler_attrs.h" + +struct wmManipulatorGroupType; +struct wmManipulatorGroup; +struct wmKeyConfig; + +typedef bool (*wmManipulatorGroupPollFunc)(const struct bContext *, struct wmManipulatorGroupType *) ATTR_WARN_UNUSED_RESULT; +typedef void (*wmManipulatorGroupInitFunc)(const struct bContext *, struct wmManipulatorGroup *); +typedef void (*wmManipulatorGroupRefreshFunc)(const struct bContext *, struct wmManipulatorGroup *); +typedef void (*wmManipulatorGroupDrawPrepareFunc)(const struct bContext *, struct wmManipulatorGroup *); + + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + +/** + * Simple utility wrapper for storing a single manipulator as wmManipulatorGroup.customdata (which gets freed). + */ +typedef struct wmManipulatorWrapper { + struct wmManipulator *manipulator; +} wmManipulatorWrapper; + +/* wmManipulator.flag + * Flags for individual manipulators. */ +enum { + WM_MANIPULATOR_DRAW_HOVER = (1 << 0), /* draw *only* while hovering */ + WM_MANIPULATOR_DRAW_ACTIVE = (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), +}; + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +typedef struct wmManipulatorGroup { + struct wmManipulatorGroup *next, *prev; + + struct wmManipulatorGroupType *type; + ListBase manipulators; + + 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 */ + int flag; /* private */ + int pad; +} wmManipulatorGroup; + +/* factory class for a manipulator-group type, gets called every time a new area is spawned */ +typedef struct wmManipulatorGroupType { + struct wmManipulatorGroupType *next, *prev; + + char idname[64]; /* MAX_NAME */ + const char *name; /* manipulator-group name - displayed in UI (keymap editor) */ + + /* poll if manipulator-map should be visible */ + wmManipulatorGroupPollFunc poll; + /* initially create manipulators and set permanent data - stuff you only need to do once */ + wmManipulatorGroupInitFunc init; + /* refresh data, only called if recreate flag is set (WM_manipulatormap_tag_refresh) */ + wmManipulatorGroupRefreshFunc refresh; + /* refresh data for drawing, called before each redraw */ + wmManipulatorGroupDrawPrepareFunc draw_prepare; + + /* keymap init callback for this manipulator-group */ + struct wmKeyMap *(*keymap_init)(const struct wmManipulatorGroupType *, struct wmKeyConfig *); + /* keymap created with callback from above */ + struct wmKeyMap *keymap; + + /* rna for properties */ + struct StructRNA *srna; + + /* RNA integration */ + ExtensionRNA ext; + + int flag; + + /* if type is spawned from operator this is set here */ + void *op; + + /* same as manipulator-maps, so registering/unregistering goes to the correct region */ + short spaceid, regionid; + char mapidname[64]; +} wmManipulatorGroupType; + +/** + * wmManipulatorGroupType.flag + * Flags that influence the behavior of all manipulators in the group. + */ +enum { + /* Mark manipulator-group as being 3D */ + WM_MANIPULATORGROUPTYPE_IS_3D = (1 << 0), + /* Scale manipulators as 3D object that respects zoom (otherwise zoom independent draw size) */ + WM_MANIPULATORGROUPTYPE_SCALE_3D = (1 << 1), + /* Manipulators can be depth culled with scene objects (covered by other geometry - TODO) */ + WM_MANIPULATORGROUPTYPE_SCENE_DEPTH = (1 << 2), + /* Manipulators can be selected */ + WM_MANIPULATORGROUPTYPE_SELECTABLE = (1 << 3), + /* manipulator group is attached to operator, and is only accessible as long as this runs */ + WM_MANIPULATORGROUPTYPE_OP = (1 << 4), +}; + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +struct wmManipulatorMapType_Params { + const char *idname; + const int spaceid; + const int regionid; +}; + +/** + * Pass a value of this enum to #WM_manipulatormap_draw to tell it what to draw. + */ +enum { + /* Draw 2D manipulator-groups (ManipulatorGroupType.is_3d == false) */ + WM_MANIPULATORMAP_DRAWSTEP_2D = 0, + /* Draw 3D manipulator-groups (ManipulatorGroupType.is_3d == true) */ + WM_MANIPULATORMAP_DRAWSTEP_3D, + /* Draw only depth culled manipulators (WM_MANIPULATOR_SCENE_DEPTH flag). + * Note that these are expected to be 3D manipulators too. */ + WM_MANIPULATORMAP_DRAWSTEP_IN_SCENE, +}; + +#endif /* __WM_MANIPULATOR_TYPES_H__ */ + diff --git a/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_intern.h b/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_intern.h new file mode 100644 index 00000000000..66598fa29d7 --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_intern.h @@ -0,0 +1,100 @@ +/* + * ***** 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/manipulators/intern/manipulator_library/manipulator_library_intern.h + * \ingroup wm + */ + + +#ifndef __MANIPULATOR_LIBRARY_INTERN_H__ +#define __MANIPULATOR_LIBRARY_INTERN_H__ + +/* distance around which manipulators respond to input (and get highlighted) */ +#define MANIPULATOR_HOTSPOT 14.0f + +/** + * Data for common interactions. Used in manipulator_library_utils.c functions. + */ +typedef struct ManipulatorCommonData { + int flag; + + float range_fac; /* factor for arrow min/max distance */ + float offset; + + /* property range for constrained manipulators */ + float range; + /* min/max value for constrained manipulators */ + float min, max; +} ManipulatorCommonData; + +typedef struct ManipulatorInteraction { + float init_value; /* initial property value */ + float init_origin[3]; + float init_mval[2]; + float init_offset; + float init_scale; + + /* offset of last handling step */ + float prev_offset; + /* Total offset added by precision tweaking. + * Needed to allow toggling precision on/off without causing jumps */ + float precision_offset; +} ManipulatorInteraction; + +/* ManipulatorCommonData->flag */ +enum { + MANIPULATOR_CUSTOM_RANGE_SET = (1 << 0), +}; + + +float manipulator_offset_from_value( + ManipulatorCommonData *data, const float value, + const bool constrained, const bool inverted); +float manipulator_value_from_offset( + ManipulatorCommonData *data, ManipulatorInteraction *inter, const float offset, + const bool constrained, const bool inverted, const bool use_precision); + +void manipulator_property_data_update( + wmManipulator *manipulator, ManipulatorCommonData *data, const int slot, + const bool constrained, const bool inverted); + +void manipulator_property_value_set( + bContext *C, const wmManipulator *manipulator, + const int slot, const float value); +float manipulator_property_value_get( + const wmManipulator *manipulator, const int slot); +void manipulator_property_value_reset( + bContext *C, const wmManipulator *manipulator, ManipulatorInteraction *inter, + const int slot); + + +/* -------------------------------------------------------------------- */ + +void manipulator_color_get( + const wmManipulator *manipulator, const bool highlight, + float r_col[]); + +#endif /* __MANIPULATOR_LIBRARY_INTERN_H__ */ + diff --git a/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_utils.c b/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_utils.c new file mode 100644 index 00000000000..0617e9e873b --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_utils.c @@ -0,0 +1,171 @@ +/* + * ***** 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) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_utils.c + * \ingroup wm + * + * \name Manipulator Library Utilities + * + * \brief This file contains functions for common behaviors of manipulators. + */ + +#include "BKE_context.h" + +#include "BLI_math.h" + +#include "RNA_access.h" + +#include "WM_api.h" + +/* own includes */ +#include "WM_manipulator_types.h" +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_intern.h" +#include "manipulator_library_intern.h" + +/* factor for precision tweaking */ +#define MANIPULATOR_PRECISION_FAC 0.05f + + +BLI_INLINE float manipulator_offset_from_value_constr( + const float range_fac, const float min, const float range, const float value, + const bool inverted) +{ + return inverted ? (range_fac * (min + range - value) / range) : (range_fac * (value / range)); +} + +BLI_INLINE float manipulator_value_from_offset_constr( + const float range_fac, const float min, const float range, const float value, + const bool inverted) +{ + return inverted ? (min + range - (value * range / range_fac)) : (value * range / range_fac); +} + +float manipulator_offset_from_value( + ManipulatorCommonData *data, const float value, const bool constrained, const bool inverted) +{ + if (constrained) + return manipulator_offset_from_value_constr(data->range_fac, data->min, data->range, value, inverted); + + return value; +} + +float manipulator_value_from_offset( + ManipulatorCommonData *data, ManipulatorInteraction *inter, const float offset, + const bool constrained, const bool inverted, const bool use_precision) +{ + const float max = data->min + data->range; + + if (use_precision) { + /* add delta offset of this step to total precision_offset */ + inter->precision_offset += offset - inter->prev_offset; + } + inter->prev_offset = offset; + + float ofs_new = inter->init_offset + offset - inter->precision_offset * (1.0f - MANIPULATOR_PRECISION_FAC); + float value; + + if (constrained) { + value = manipulator_value_from_offset_constr(data->range_fac, data->min, data->range, ofs_new, inverted); + } + else { + value = ofs_new; + } + + /* clamp to custom range */ + if (data->flag & MANIPULATOR_CUSTOM_RANGE_SET) { + CLAMP(value, data->min, max); + } + + return value; +} + +void manipulator_property_data_update( + wmManipulator *manipulator, ManipulatorCommonData *data, const int slot, + const bool constrained, const bool inverted) +{ + if (!manipulator->props[slot]) { + data->offset = 0.0f; + return; + } + + PointerRNA ptr = manipulator->ptr[slot]; + PropertyRNA *prop = manipulator->props[slot]; + float value = manipulator_property_value_get(manipulator, slot); + + if (constrained) { + if ((data->flag & MANIPULATOR_CUSTOM_RANGE_SET) == 0) { + float step, precision; + float min, max; + RNA_property_float_ui_range(&ptr, prop, &min, &max, &step, &precision); + data->range = max - min; + data->min = min; + } + data->offset = manipulator_offset_from_value_constr(data->range_fac, data->min, data->range, value, inverted); + } + else { + data->offset = value; + } +} + +void manipulator_property_value_set( + bContext *C, const wmManipulator *manipulator, + const int slot, const float value) +{ + PointerRNA ptr = manipulator->ptr[slot]; + PropertyRNA *prop = manipulator->props[slot]; + + /* reset property */ + RNA_property_float_set(&ptr, prop, value); + RNA_property_update(C, &ptr, prop); +} + +float manipulator_property_value_get(const wmManipulator *manipulator, const int slot) +{ + BLI_assert(RNA_property_type(manipulator->props[slot]) == PROP_FLOAT); + return RNA_property_float_get(&manipulator->ptr[slot], manipulator->props[slot]); +} + +void manipulator_property_value_reset( + bContext *C, const wmManipulator *manipulator, ManipulatorInteraction *inter, + const int slot) +{ + manipulator_property_value_set(C, manipulator, slot, inter->init_value); +} + + +/* -------------------------------------------------------------------- */ + +void manipulator_color_get( + const wmManipulator *manipulator, const bool highlight, + float r_col[4]) +{ + if (highlight && !(manipulator->flag & WM_MANIPULATOR_DRAW_HOVER)) { + copy_v4_v4(r_col, manipulator->col_hi); + } + else { + copy_v4_v4(r_col, manipulator->col); + } +} diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator.c new file mode 100644 index 00000000000..824141d1c40 --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator.c @@ -0,0 +1,404 @@ +/* + * ***** 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/manipulators/intern/wm_manipulator.c + * \ingroup wm + */ + +#include "BKE_context.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "GL/glew.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* own includes */ +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_intern.h" + + +/* Still unused */ +wmManipulator *WM_manipulator_new( + void (*draw)(const bContext *C, wmManipulator *customdata), + void (*render_3d_intersection)(const bContext *C, wmManipulator *customdata, int selectionbase), + int (*intersect)(bContext *C, const wmEvent *event, wmManipulator *manipulator), + int (*handler)(bContext *C, const wmEvent *event, wmManipulator *manipulator, const int flag)) +{ + wmManipulator *manipulator = MEM_callocN(sizeof(wmManipulator), "manipulator"); + + manipulator->draw = draw; + manipulator->handler = handler; + manipulator->intersect = intersect; + manipulator->render_3d_intersection = render_3d_intersection; + + /* XXX */ +// fix_linking_manipulator_arrow(); +// fix_linking_manipulator_arrow2d(); +// fix_linking_manipulator_cage(); +// fix_linking_manipulator_dial(); +// fix_linking_manipulator_facemap(); +// fix_linking_manipulator_primitive(); + + return manipulator; +} + +/** + * Assign an idname that is unique in \a mgroup to \a manipulator. + * + * \param rawname: Name used as basis to define final unique idname. + */ +static void manipulator_unique_idname_set(wmManipulatorGroup *mgroup, wmManipulator *manipulator, const char *rawname) +{ + if (mgroup->type->idname[0]) { + BLI_snprintf(manipulator->idname, sizeof(manipulator->idname), "%s_%s", mgroup->type->idname, rawname); + } + else { + BLI_strncpy(manipulator->idname, rawname, sizeof(manipulator->idname)); + } + + /* ensure name is unique, append '.001', '.002', etc if not */ + BLI_uniquename(&mgroup->manipulators, manipulator, "Manipulator", '.', + offsetof(wmManipulator, idname), sizeof(manipulator->idname)); +} + +/** + * Initialize default values and allocate needed memory for members. + */ +static void manipulator_init(wmManipulator *manipulator) +{ + const float col_default[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + manipulator->user_scale = 1.0f; + manipulator->line_width = 1.0f; + + /* defaults */ + copy_v4_v4(manipulator->col, col_default); + copy_v4_v4(manipulator->col_hi, col_default); + + /* create at least one property for interaction */ + if (manipulator->max_prop == 0) { + manipulator->max_prop = 1; + } + + manipulator->props = MEM_callocN(sizeof(PropertyRNA *) * manipulator->max_prop, "manipulator->props"); + manipulator->ptr = MEM_callocN(sizeof(PointerRNA) * manipulator->max_prop, "manipulator->ptr"); +} + +/** + * Register \a manipulator. + * + * \param name: name used to create a unique idname for \a manipulator in \a mgroup + */ +void wm_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *manipulator, const char *name) +{ + manipulator_init(manipulator); + manipulator_unique_idname_set(mgroup, manipulator, name); + wm_manipulatorgroup_manipulator_register(mgroup, manipulator); +} + +/** + * Free \a manipulator and unlink from \a manipulatorlist. + * \a manipulatorlist is allowed to be NULL. + */ +void WM_manipulator_delete(ListBase *manipulatorlist, wmManipulatorMap *mmap, wmManipulator *manipulator, bContext *C) +{ + if (manipulator->state & WM_MANIPULATOR_HIGHLIGHT) { + wm_manipulatormap_set_highlighted_manipulator(mmap, C, NULL, 0); + } + if (manipulator->state & WM_MANIPULATOR_ACTIVE) { + wm_manipulatormap_set_active_manipulator(mmap, C, NULL, NULL); + } + if (manipulator->state & WM_MANIPULATOR_SELECTED) { + wm_manipulator_deselect(mmap, manipulator); + } + + if (manipulator->opptr.data) { + WM_operator_properties_free(&manipulator->opptr); + } + MEM_freeN(manipulator->props); + MEM_freeN(manipulator->ptr); + + if (manipulatorlist) + BLI_remlink(manipulatorlist, manipulator); + MEM_freeN(manipulator); +} + +wmManipulatorGroup *wm_manipulator_get_parent_group(const wmManipulator *manipulator) +{ + return manipulator->mgroup; +} + + +/* -------------------------------------------------------------------- */ +/** \name Manipulator Creation API + * + * API for defining data on manipulator creation. + * + * \{ */ + +void WM_manipulator_set_property(wmManipulator *manipulator, const int slot, PointerRNA *ptr, const char *propname) +{ + if (slot < 0 || slot >= manipulator->max_prop) { + fprintf(stderr, "invalid index %d when binding property for manipulator type %s\n", slot, manipulator->idname); + return; + } + + /* if manipulator evokes an operator we cannot use it for property manipulation */ + manipulator->opname = NULL; + manipulator->ptr[slot] = *ptr; + manipulator->props[slot] = RNA_struct_find_property(ptr, propname); + + if (manipulator->prop_data_update) + manipulator->prop_data_update(manipulator, slot); +} + +PointerRNA *WM_manipulator_set_operator(wmManipulator *manipulator, const char *opname) +{ + wmOperatorType *ot = WM_operatortype_find(opname, 0); + + if (ot) { + manipulator->opname = opname; + + if (manipulator->opptr.data) { + WM_operator_properties_free(&manipulator->opptr); + } + WM_operator_properties_create_ptr(&manipulator->opptr, ot); + + return &manipulator->opptr; + } + else { + fprintf(stderr, "Error binding operator to manipulator: operator %s not found!\n", opname); + } + + return NULL; +} + +/** + * \brief Set manipulator select callback. + * + * Callback is called when manipulator gets selected/deselected. + */ +void WM_manipulator_set_func_select(wmManipulator *manipulator, wmManipulatorSelectFunc select) +{ + BLI_assert(manipulator->mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECTABLE); + manipulator->select = select; +} + +void WM_manipulator_set_origin(wmManipulator *manipulator, const float origin[3]) +{ + copy_v3_v3(manipulator->origin, origin); +} + +void WM_manipulator_set_offset(wmManipulator *manipulator, const float offset[3]) +{ + copy_v3_v3(manipulator->offset, offset); +} + +void WM_manipulator_set_flag(wmManipulator *manipulator, const int flag, const bool enable) +{ + if (enable) { + manipulator->flag |= flag; + } + else { + manipulator->flag &= ~flag; + } +} + +void WM_manipulator_set_scale(wmManipulator *manipulator, const float scale) +{ + manipulator->user_scale = scale; +} + +void WM_manipulator_set_line_width(wmManipulator *manipulator, const float line_width) +{ + manipulator->line_width = line_width; +} + +/** + * Set manipulator rgba colors. + * + * \param col Normal state color. + * \param col_hi Highlighted state color. + */ +void WM_manipulator_set_colors(wmManipulator *manipulator, const float col[4], const float col_hi[4]) +{ + copy_v4_v4(manipulator->col, col); + copy_v4_v4(manipulator->col_hi, col_hi); +} + +/** \} */ // Manipulator Creation API + + +/* -------------------------------------------------------------------- */ + +/** + * Remove \a manipulator from selection. + * Reallocates memory for selected manipulators so better not call for selecting multiple ones. + * + * \return if the selection has changed. + */ +bool wm_manipulator_deselect(wmManipulatorMap *mmap, wmManipulator *manipulator) +{ + if (!mmap->mmap_context.selected_manipulator) + return false; + + wmManipulator ***sel = &mmap->mmap_context.selected_manipulator; + int *tot_selected = &mmap->mmap_context.tot_selected; + bool changed = false; + + /* caller should check! */ + BLI_assert(manipulator->state & WM_MANIPULATOR_SELECTED); + + /* remove manipulator from selected_manipulators array */ + for (int i = 0; i < (*tot_selected); i++) { + if ((*sel)[i] == manipulator) { + for (int j = i; j < ((*tot_selected) - 1); j++) { + (*sel)[j] = (*sel)[j + 1]; + } + changed = true; + break; + } + } + + /* update array data */ + if ((*tot_selected) <= 1) { + wm_manipulatormap_selected_delete(mmap); + } + else { + *sel = MEM_reallocN(*sel, sizeof(**sel) * (*tot_selected)); + (*tot_selected)--; + } + + manipulator->state &= ~WM_MANIPULATOR_SELECTED; + return changed; +} + +/** + * Add \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(bContext *C, wmManipulatorMap *mmap, wmManipulator *manipulator) +{ + wmManipulator ***sel = &mmap->mmap_context.selected_manipulator; + int *tot_selected = &mmap->mmap_context.tot_selected; + + if (!manipulator || (manipulator->state & WM_MANIPULATOR_SELECTED)) + return false; + + (*tot_selected)++; + + *sel = MEM_reallocN(*sel, sizeof(wmManipulator *) * (*tot_selected)); + (*sel)[(*tot_selected) - 1] = manipulator; + + manipulator->state |= WM_MANIPULATOR_SELECTED; + if (manipulator->select) { + manipulator->select(C, manipulator, SEL_SELECT); + } + wm_manipulatormap_set_highlighted_manipulator(mmap, C, manipulator, manipulator->highlighted_part); + + return true; +} + +void wm_manipulator_calculate_scale(wmManipulator *manipulator, const bContext *C) +{ + const RegionView3D *rv3d = CTX_wm_region_view3d(C); + float scale = 1.0f; + + if (manipulator->mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCALE_3D) { + if (rv3d /*&& (U.manipulator_flag & V3D_3D_MANIPULATORS) == 0*/) { /* UserPref flag might be useful for later */ + if (manipulator->get_final_position) { + float position[3]; + + manipulator->get_final_position(manipulator, position); + scale = ED_view3d_pixel_size(rv3d, position) * (float)U.manipulator_scale; + } + else { + scale = ED_view3d_pixel_size(rv3d, manipulator->origin) * (float)U.manipulator_scale; + } + } + else { + scale = U.manipulator_scale * 0.02f; + } + } + + manipulator->scale = scale * manipulator->user_scale; +} + +static void manipulator_update_prop_data(wmManipulator *manipulator) +{ + /* manipulator property might have been changed, so update manipulator */ + if (manipulator->props && manipulator->prop_data_update) { + for (int i = 0; i < manipulator->max_prop; i++) { + if (manipulator->props[i]) { + manipulator->prop_data_update(manipulator, i); + } + } + } +} + +void wm_manipulator_update(wmManipulator *manipulator, const bContext *C, const bool refresh_map) +{ + if (refresh_map) { + manipulator_update_prop_data(manipulator); + } + wm_manipulator_calculate_scale(manipulator, C); +} + +bool wm_manipulator_is_visible(wmManipulator *manipulator) +{ + if (manipulator->flag & WM_MANIPULATOR_HIDDEN) { + return false; + } + if ((manipulator->state & WM_MANIPULATOR_ACTIVE) && + !(manipulator->flag & (WM_MANIPULATOR_DRAW_ACTIVE | WM_MANIPULATOR_DRAW_VALUE))) + { + /* don't draw while active (while dragging) */ + return false; + } + if ((manipulator->flag & WM_MANIPULATOR_DRAW_HOVER) && + !(manipulator->state & WM_MANIPULATOR_HIGHLIGHT) && + !(manipulator->state & WM_MANIPULATOR_SELECTED)) /* still draw selected manipulators */ + { + /* only draw on mouse hover */ + return false; + } + + return true; +} + diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h b/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h new file mode 100644 index 00000000000..1a9693a472b --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h @@ -0,0 +1,230 @@ +/* + * ***** 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/manipulators/intern/wm_manipulator_intern.h + * \ingroup wm + */ + + +#ifndef __WM_MANIPULATOR_INTERN_H__ +#define __WM_MANIPULATOR_INTERN_H__ + +struct wmKeyConfig; +struct wmManipulatorMap; + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + +/* manipulators are set per region by registering them on manipulator-maps */ +typedef struct wmManipulator { + struct wmManipulator *next, *prev; + + char idname[MAX_NAME + 4]; /* + 4 for unique '.001', '.002', etc suffix */ + /* pointer back to group this manipulator is in (just for quick access) */ + struct wmManipulatorGroup *mgroup; + + /* could become wmManipulatorType */ + /* draw manipulator */ + void (*draw)(const struct bContext *, struct wmManipulator *); + + /* determine if the mouse intersects with the manipulator. The calculation should be done in the callback itself */ + int (*intersect)(struct bContext *, const struct wmEvent *, struct wmManipulator *); + + /* determines 3d intersection by rendering the manipulator in a selection routine. */ + void (*render_3d_intersection)(const struct bContext *, struct wmManipulator *, int); + + /* handler used by the manipulator. Usually handles interaction tied to a manipulator type */ + int (*handler)(struct bContext *, const struct wmEvent *, struct wmManipulator *, const int); + + /* manipulator-specific handler to update manipulator attributes based on the property value */ + void (*prop_data_update)(struct wmManipulator *, int); + + /* returns the final position which may be different from the origin, depending on the manipulator. + * used in calculations of scale */ + void (*get_final_position)(struct wmManipulator *, float[]); + + /* activate a manipulator state when the user clicks on it */ + int (*invoke)(struct bContext *, const struct wmEvent *, struct wmManipulator *); + + /* called when manipulator tweaking is done - used to free data and reset property when cancelling */ + void (*exit)(struct bContext *, struct wmManipulator *, const bool ); + + int (*get_cursor)(struct wmManipulator *); + + /* called when manipulator selection state changes */ + wmManipulatorSelectFunc select; + + int flag; /* flags that influence the behavior or how the manipulators are drawn */ + short state; /* state flags (active, highlighted, selected) */ + + unsigned char highlighted_part; + + /* center of manipulator in space, 2d or 3d */ + float origin[3]; + /* custom offset from origin */ + float offset[3]; + /* runtime property, set the scale while drawing on the viewport */ + float scale; + /* user defined scale, in addition to the original one */ + float user_scale; + /* user defined width for line drawing */ + float line_width; + /* manipulator colors (uses default fallbacks if not defined) */ + float col[4], col_hi[4]; + + /* data used during interaction */ + void *interaction_data; + + /* name of operator to spawn when activating the manipulator */ + const char *opname; + /* operator properties if manipulator spawns and controls an operator, + * or owner pointer if manipulator spawns and controls a property */ + PointerRNA opptr; + + /* maximum number of properties attached to the manipulator */ + int max_prop; + /* arrays of properties attached to various manipulator parameters. As + * the manipulator is interacted with, those properties get updated */ + PointerRNA *ptr; + PropertyRNA **props; +} wmManipulator; + +/* wmManipulator.state */ +enum { + WM_MANIPULATOR_HIGHLIGHT = (1 << 0), /* while hovered */ + WM_MANIPULATOR_ACTIVE = (1 << 1), /* while dragging */ + WM_MANIPULATOR_SELECTED = (1 << 2), +}; + +/** + * \brief Manipulator tweak flag. + * Bitflag passed to manipulator while tweaking. + */ +enum { + /* drag with extra precision (shift) + * NOTE: Manipulators are responsible for handling this (manipulator->handler callback)! */ + WM_MANIPULATOR_TWEAK_PRECISE = (1 << 0), +}; + +void wm_manipulator_register(struct wmManipulatorGroup *mgroup, struct wmManipulator *manipulator, const char *name); + +bool wm_manipulator_deselect(struct wmManipulatorMap *mmap, struct wmManipulator *manipulator); +bool wm_manipulator_select(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulator *manipulator); + +void wm_manipulator_calculate_scale(struct wmManipulator *manipulator, const bContext *C); +void wm_manipulator_update(struct wmManipulator *manipulator, const bContext *C, const bool refresh_map); +bool wm_manipulator_is_visible(struct wmManipulator *manipulator); + +void fix_linking_manipulator_arrow(void); +void fix_linking_manipulator_arrow2d(void); +void fix_linking_manipulator_cage(void); +void fix_linking_manipulator_dial(void); +void fix_linking_manipulator_facemap(void); +void fix_linking_manipulator_primitive(void); + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +enum { + TWEAK_MODAL_CANCEL = 1, + TWEAK_MODAL_CONFIRM, + TWEAK_MODAL_PRECISION_ON, + TWEAK_MODAL_PRECISION_OFF, +}; + +struct wmManipulatorGroup *wm_manipulatorgroup_new_from_type(struct wmManipulatorGroupType *mgrouptype); +void wm_manipulatorgroup_free(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulatorGroup *mgroup); +void wm_manipulatorgroup_manipulator_register(struct wmManipulatorGroup *mgroup, wmManipulator *manipulator); +wmManipulator *wm_manipulatorgroup_find_intersected_mainpulator( + const struct wmManipulatorGroup *mgroup, struct bContext *C, const struct wmEvent *event, + unsigned char *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(const struct wmManipulatorGroup *mgroup, const struct bContext *C); +bool wm_manipulatorgroup_is_visible_in_drawstep(const struct wmManipulatorGroup *mgroup, const int drawstep); + +void wm_manipulatorgrouptype_keymap_init(struct wmManipulatorGroupType *mgrouptype, struct wmKeyConfig *keyconf); + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +typedef struct wmManipulatorMap { + struct wmManipulatorMap *next, *prev; + + struct wmManipulatorMapType *type; + ListBase manipulator_groups; + + char update_flag; /* private, update tagging */ + + /** + * \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 *highlighted_manipulator; + /* user has clicked this manipulator and it gets all input */ + struct wmManipulator *active_manipulator; + /* array for all selected manipulators + * TODO check on using BLI_array */ + struct wmManipulator **selected_manipulator; + int tot_selected; + } mmap_context; +} wmManipulatorMap; + +/** + * 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. + */ +typedef struct wmManipulatorMapType { + struct wmManipulatorMapType *next, *prev; + char idname[64]; + short spaceid, regionid; + /* types of manipulator-groups for this manipulator-map type */ + ListBase manipulator_grouptypes; +} wmManipulatorMapType; + +void wm_manipulatormap_selected_delete(wmManipulatorMap *mmap); +bool wm_manipulatormap_deselect_all(struct wmManipulatorMap *mmap, wmManipulator ***sel); + + +/* -------------------------------------------------------------------- */ +/* Manipulator drawing */ + +typedef struct ManipulatorGeometryInfo { + int nverts; + int ntris; + float (*verts)[3]; + float (*normals)[3]; + unsigned short *indices; + bool init; +} ManipulatorGeometryInfo; + +#endif /* __WM_MANIPULATOR_INTERN_H__ */ + diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c b/source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c new file mode 100644 index 00000000000..37a3ca8b55c --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c @@ -0,0 +1,586 @@ +/* + * ***** 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/manipulators/intern/wm_manipulatorgroup.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 "BKE_context.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "BPY_extern.h" + +#include "ED_screen.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm_event_system.h" + +/* own includes */ +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_intern.h" + + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorGroup + * + * \{ */ + +/* wmManipulatorGroup.flag */ +enum { + WM_MANIPULATORGROUP_INITIALIZED = (1 << 2), /* mgroup has been initialized */ +}; + +/** + * Create a new manipulator-group from \a mgrouptype. + */ +wmManipulatorGroup *wm_manipulatorgroup_new_from_type(wmManipulatorGroupType *mgrouptype) +{ + wmManipulatorGroup *mgroup = MEM_callocN(sizeof(*mgroup), "manipulator-group"); + mgroup->type = mgrouptype; + + return mgroup; +} + +void wm_manipulatorgroup_free(bContext *C, wmManipulatorMap *mmap, wmManipulatorGroup *mgroup) +{ + for (wmManipulator *manipulator = mgroup->manipulators.first; manipulator;) { + wmManipulator *manipulator_next = manipulator->next; + WM_manipulator_delete(&mgroup->manipulators, mmap, manipulator, C); + manipulator = manipulator_next; + } + BLI_assert(BLI_listbase_is_empty(&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->manipulator_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 *manipulator) +{ + BLI_assert(!BLI_findstring(&mgroup->manipulators, manipulator->idname, offsetof(wmManipulator, idname))); + BLI_addtail(&mgroup->manipulators, manipulator); + manipulator->mgroup = mgroup; +} + +void wm_manipulatorgroup_attach_to_modal_handler( + bContext *C, wmEventHandler *handler, + wmManipulatorGroupType *mgrouptype, wmOperator *op) +{ + /* maybe overly careful, but manipulator-grouptype could come from a failed creation */ + if (!mgrouptype) { + return; + } + + /* now instantiate the manipulator-map */ + mgrouptype->op = op; + + /* try to find map in handler region that contains mgrouptype */ + if (handler->op_region && handler->op_region->manipulator_map) { + handler->manipulator_map = handler->op_region->manipulator_map; + ED_region_tag_redraw(handler->op_region); + } + + WM_event_add_mousemove(C); +} + +wmManipulator *wm_manipulatorgroup_find_intersected_mainpulator( + const wmManipulatorGroup *mgroup, bContext *C, const wmEvent *event, + unsigned char *part) +{ + for (wmManipulator *manipulator = mgroup->manipulators.first; manipulator; manipulator = manipulator->next) { + if (manipulator->intersect && (manipulator->flag & WM_MANIPULATOR_HIDDEN) == 0) { + if ((*part = manipulator->intersect(C, event, manipulator))) { + return manipulator; + } + } + } + + 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 *manipulator = mgroup->manipulators.first; manipulator; manipulator = manipulator->next) { + if ((manipulator->flag & WM_MANIPULATOR_HIDDEN) == 0) { + if (((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) && manipulator->render_3d_intersection) || + ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) == 0 && manipulator->intersect)) + { + BLI_addhead(listbase, BLI_genericNodeN(manipulator)); + } + } + } +} + +void wm_manipulatorgroup_ensure_initialized(wmManipulatorGroup *mgroup, const bContext *C) +{ + /* prepare for first draw */ + if (UNLIKELY((mgroup->flag & WM_MANIPULATORGROUP_INITIALIZED) == 0)) { + mgroup->type->init(C, mgroup); + mgroup->flag |= WM_MANIPULATORGROUP_INITIALIZED; + } +} + +bool wm_manipulatorgroup_is_visible(const wmManipulatorGroup *mgroup, const bContext *C) +{ + /* Check for poll function, if manipulator-group belongs to an operator, also check if the operator is running. */ + return ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_OP) == 0 || mgroup->type->op) && + (!mgroup->type->poll || mgroup->type->poll(C, mgroup->type)); +} + +bool wm_manipulatorgroup_is_visible_in_drawstep(const wmManipulatorGroup *mgroup, const int drawstep) +{ + switch (drawstep) { + case WM_MANIPULATORMAP_DRAWSTEP_2D: + return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) == 0; + case WM_MANIPULATORMAP_DRAWSTEP_3D: + return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D); + case WM_MANIPULATORMAP_DRAWSTEP_IN_SCENE: + return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCENE_DEPTH); + default: + BLI_assert(0); + 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; + wmManipulator ***sel = &mmap->mmap_context.selected_manipulator; + wmManipulator *highlighted = mmap->mmap_context.highlighted_manipulator; + + 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, sel); + BLI_assert(*sel == NULL && mmap->mmap_context.tot_selected == 0); + } + + if (highlighted) { + const bool is_selected = (highlighted->state & WM_MANIPULATOR_SELECTED); + bool redraw = false; + + if (toggle) { + /* toggle: deselect if already selected, else select */ + deselect = is_selected; + } + + if (deselect) { + if (is_selected && wm_manipulator_deselect(mmap, highlighted)) { + redraw = true; + } + } + else if (wm_manipulator_select(C, mmap, highlighted)) { + 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 = "Manipulator 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; + wmManipulator *active; + + int init_event; /* initial event type */ + int flag; /* tweak flags */ +} ManipulatorTweakData; + +static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel) +{ + ManipulatorTweakData *mtweak = op->customdata; + if (mtweak->active->exit) { + mtweak->active->exit(C, mtweak->active, cancel); + } + wm_manipulatormap_set_active_manipulator(mtweak->mmap, C, NULL, NULL); + MEM_freeN(mtweak); +} + +static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ManipulatorTweakData *mtweak = op->customdata; + wmManipulator *manipulator = mtweak->active; + + if (!manipulator) { + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + if (event->type == mtweak->init_event && event->val == KM_RELEASE) { + manipulator_tweak_finish(C, op, false); + return OPERATOR_FINISHED; + } + + + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case TWEAK_MODAL_CANCEL: + manipulator_tweak_finish(C, op, true); + return OPERATOR_CANCELLED; + case TWEAK_MODAL_CONFIRM: + manipulator_tweak_finish(C, op, false); + return OPERATOR_FINISHED; + case TWEAK_MODAL_PRECISION_ON: + mtweak->flag |= WM_MANIPULATOR_TWEAK_PRECISE; + break; + case TWEAK_MODAL_PRECISION_OFF: + mtweak->flag &= ~WM_MANIPULATOR_TWEAK_PRECISE; + break; + } + } + + /* handle manipulator */ + if (manipulator->handler) { + manipulator->handler(C, event, manipulator, mtweak->flag); + } + + /* Ugly hack to send manipulator events */ + ((wmEvent *)event)->type = EVT_MANIPULATOR_UPDATE; + + /* always return PASS_THROUGH so modal handlers + * with manipulators attached can update */ + 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 *manipulator = mmap->mmap_context.highlighted_manipulator; + + if (!manipulator) { + /* wm_handlers_do_intern shouldn't let this happen */ + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + + /* activate highlighted manipulator */ + wm_manipulatormap_set_active_manipulator(mmap, C, event, manipulator); + + /* XXX temporary workaround for modal manipulator operator + * conflicting with modal operator attached to manipulator */ + if (manipulator->opname) { + wmOperatorType *ot = WM_operatortype_find(manipulator->opname, true); + if (ot->modal) { + return OPERATOR_FINISHED; + } + } + + + ManipulatorTweakData *mtweak = MEM_mallocN(sizeof(ManipulatorTweakData), __func__); + + mtweak->init_event = event->type; + mtweak->active = mmap->mmap_context.highlighted_manipulator; + mtweak->mmap = mmap; + mtweak->flag = 0; + + op->customdata = mtweak; + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void MANIPULATORGROUP_OT_manipulator_tweak(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Manipulator Tweak"; + ot->description = "Tweak the active manipulator"; + ot->idname = "MANIPULATORGROUP_OT_manipulator_tweak"; + + /* api callbacks */ + ot->invoke = manipulator_tweak_invoke; + ot->modal = manipulator_tweak_modal; + + ot->flag = OPTYPE_UNDO; +} + +/** \} */ // Manipulator operators + + +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", ""}, + {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_assign(keymap, "MANIPULATORGROUP_OT_manipulator_tweak"); + + return keymap; +} + +/** + * Common default keymap for manipulator groups + */ +wmKeyMap *WM_manipulatorgroup_keymap_common(const struct wmManipulatorGroupType *mgrouptype, 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, mgrouptype->name, mgrouptype->spaceid, mgrouptype->regionid); + + WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0); + manipulatorgroup_tweak_modal_keymap(config, mgrouptype->name); + + return km; +} + +/** + * Variation of #WM_manipulatorgroup_keymap_common but with keymap items for selection + */ +wmKeyMap *WM_manipulatorgroup_keymap_common_sel(const struct wmManipulatorGroupType *mgrouptype, 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, mgrouptype->name, mgrouptype->spaceid, mgrouptype->regionid); + + WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0); + manipulatorgroup_tweak_modal_keymap(config, mgrouptype->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 + * + * \{ */ + +/** + * Use this for registering manipulators on startup. For runtime, use #WM_manipulatorgrouptype_append_runtime. + */ +wmManipulatorGroupType *WM_manipulatorgrouptype_append( + wmManipulatorMapType *mmaptype, void (*mgrouptype_func)(wmManipulatorGroupType *)) +{ + wmManipulatorGroupType *mgrouptype = MEM_callocN(sizeof(wmManipulatorGroupType), "manipulator-group"); + + mgrouptype_func(mgrouptype); + mgrouptype->spaceid = mmaptype->spaceid; + mgrouptype->regionid = mmaptype->regionid; + BLI_strncpy(mgrouptype->mapidname, mmaptype->idname, MAX_NAME); + /* if not set, use default */ + if (!mgrouptype->keymap_init) { + mgrouptype->keymap_init = WM_manipulatorgroup_keymap_common; + } + + /* add the type for future created areas of the same type */ + BLI_addtail(&mmaptype->manipulator_grouptypes, mgrouptype); + return mgrouptype; +} + +/** + * Use this for registering manipulators on runtime. + */ +wmManipulatorGroupType *WM_manipulatorgrouptype_append_runtime( + const Main *main, wmManipulatorMapType *mmaptype, + void (*mgrouptype_func)(wmManipulatorGroupType *)) +{ + wmManipulatorGroupType *mgrouptype = WM_manipulatorgrouptype_append(mmaptype, mgrouptype_func); + + /* Main is missing on startup when we create new areas. + * So this is only called for manipulators initialized on runtime */ + WM_manipulatorgrouptype_init_runtime(main, mmaptype, mgrouptype); + + return mgrouptype; +} + +void WM_manipulatorgrouptype_init_runtime( + const Main *bmain, wmManipulatorMapType *mmaptype, + wmManipulatorGroupType *mgrouptype) +{ + /* init keymap - on startup there's an extra call to init keymaps for 'permanent' manipulator-groups */ + wm_manipulatorgrouptype_keymap_init(mgrouptype, ((wmWindowManager *)bmain->wm.first)->defaultconf); + + /* 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->type == mmaptype) { + wmManipulatorGroup *mgroup = wm_manipulatorgroup_new_from_type(mgrouptype); + + /* just add here, drawing will occur on next update */ + BLI_addtail(&mmap->manipulator_groups, mgroup); + wm_manipulatormap_set_highlighted_manipulator(mmap, NULL, NULL, 0); + ED_region_tag_redraw(ar); + } + } + } + } + } +} + +void WM_manipulatorgrouptype_unregister(bContext *C, Main *bmain, wmManipulatorGroupType *mgrouptype) +{ + 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; + wmManipulatorGroup *mgroup, *mgroup_next; + + for (mgroup = mmap->manipulator_groups.first; mgroup; mgroup = mgroup_next) { + mgroup_next = mgroup->next; + if (mgroup->type == mgrouptype) { + wm_manipulatorgroup_free(C, mmap, mgroup); + ED_region_tag_redraw(ar); + } + } + } + } + } + } + + wmManipulatorMapType *mmaptype = WM_manipulatormaptype_find(&(const struct wmManipulatorMapType_Params) { + mgrouptype->mapidname, mgrouptype->spaceid, + mgrouptype->regionid}); + + BLI_remlink(&mmaptype->manipulator_grouptypes, mgrouptype); + mgrouptype->prev = mgrouptype->next = NULL; + + MEM_freeN(mgrouptype); +} + +void wm_manipulatorgrouptype_keymap_init(wmManipulatorGroupType *mgrouptype, wmKeyConfig *keyconf) +{ + mgrouptype->keymap = mgrouptype->keymap_init(mgrouptype, keyconf); +} + +/** \} */ /* wmManipulatorGroupType */ diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulatormap.c b/source/blender/windowmanager/manipulators/intern/wm_manipulatormap.c new file mode 100644 index 00000000000..20f2df10af8 --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulatormap.c @@ -0,0 +1,760 @@ +/* + * ***** 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/manipulators/intern/wm_manipulatormap.c + * \ingroup wm + */ + +#include + +#include "BKE_context.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_ghash.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "GPU_glew.h" +#include "GPU_select.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm_event_system.h" + +/* own includes */ +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_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}; + +/** + * Manipulator-map update tagging. + */ +enum eManipulatorMapUpdateFlags { + /* Tag manipulator-map for refresh. */ + MANIPULATORMAP_REFRESH = (1 << 0), +}; + + +/* -------------------------------------------------------------------- */ +/** \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 *mmaptype = WM_manipulatormaptype_ensure(mmap_params); + wmManipulatorMap *mmap; + + mmap = MEM_callocN(sizeof(wmManipulatorMap), "ManipulatorMap"); + mmap->type = mmaptype; + mmap->update_flag = MANIPULATORMAP_REFRESH; + + /* create all manipulator-groups for this manipulator-map. We may create an empty one + * too in anticipation of manipulators from operators etc */ + for (wmManipulatorGroupType *mgrouptype = mmaptype->manipulator_grouptypes.first; + mgrouptype; + mgrouptype = mgrouptype->next) + { + wmManipulatorGroup *mgroup = wm_manipulatorgroup_new_from_type(mgrouptype); + BLI_addtail(&mmap->manipulator_groups, mgroup); + } + + return mmap; +} + +void wm_manipulatormap_selected_delete(wmManipulatorMap *mmap) +{ + MEM_SAFE_FREE(mmap->mmap_context.selected_manipulator); + mmap->mmap_context.tot_selected = 0; +} + +void wm_manipulatormap_delete(wmManipulatorMap *mmap) +{ + if (!mmap) + return; + + for (wmManipulatorGroup *mgroup = mmap->manipulator_groups.first, *mgroup_next; mgroup; mgroup = mgroup_next) { + mgroup_next = mgroup->next; + wm_manipulatorgroup_free(NULL, mmap, mgroup); + } + BLI_assert(BLI_listbase_is_empty(&mmap->manipulator_groups)); + + wm_manipulatormap_selected_delete(mmap); + + MEM_freeN(mmap); +} + +/** + * 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 + */ +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_str_new(__func__); + + /* collect manipulators */ + for (wmManipulatorGroup *mgroup = mmap->manipulator_groups.first; mgroup; mgroup = mgroup->next) { + if (!mgroup->type->poll || mgroup->type->poll(C, mgroup->type)) { + for (wmManipulator *manipulator = mgroup->manipulators.first; + manipulator; + manipulator = manipulator->next) + { + if ((include_hidden || (manipulator->flag & WM_MANIPULATOR_HIDDEN) == 0) && + (!poll || poll(manipulator, data))) + { + BLI_ghash_insert(hash, manipulator->idname, manipulator); + } + } + } + } + + return hash; +} + +void WM_manipulatormap_tag_refresh(wmManipulatorMap *mmap) +{ + if (mmap) { + mmap->update_flag |= MANIPULATORMAP_REFRESH; + } +} + +static void manipulatormap_tag_updated(wmManipulatorMap *mmap) +{ + mmap->update_flag = 0; +} + +static bool manipulator_prepare_drawing( + wmManipulatorMap *mmap, wmManipulator *manipulator, + const bContext *C, ListBase *draw_manipulators) +{ + if (!wm_manipulator_is_visible(manipulator)) { + /* skip */ + } + else { + wm_manipulator_update(manipulator, C, (mmap->update_flag & MANIPULATORMAP_REFRESH) != 0); + BLI_addhead(draw_manipulators, BLI_genericNodeN(manipulator)); + 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 int drawstep) +{ + if (!mmap || BLI_listbase_is_empty(&mmap->manipulator_groups)) + return; + wmManipulator *active_manipulator = mmap->mmap_context.active_manipulator; + + /* only active manipulator needs updating */ + if (active_manipulator) { + if (manipulator_prepare_drawing(mmap, active_manipulator, C, draw_manipulators)) { + manipulatormap_tag_updated(mmap); + } + /* don't draw any other manipulators */ + return; + } + + for (wmManipulatorGroup *mgroup = mmap->manipulator_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_manipulatorgroup_is_visible(mgroup, C)) + { + continue; + } + + /* needs to be initialized on first draw */ + wm_manipulatorgroup_ensure_initialized(mgroup, C); + /* update data if needed */ + /* XXX weak: Manipulator-group may skip refreshing if it's invisible (map gets untagged nevertheless) */ + if (mmap->update_flag & MANIPULATORMAP_REFRESH && mgroup->type->refresh) { + mgroup->type->refresh(C, mgroup); + } + /* prepare drawing */ + if (mgroup->type->draw_prepare) { + mgroup->type->draw_prepare(C, mgroup); + } + + for (wmManipulator *manipulator = mgroup->manipulators.first; manipulator; manipulator = manipulator->next) { + manipulator_prepare_drawing(mmap, manipulator, C, draw_manipulators); + } + } + + manipulatormap_tag_updated(mmap); +} + +/** + * 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) +{ + if (!mmap) + return; + BLI_assert(!BLI_listbase_is_empty(&mmap->manipulator_groups)); + + /* draw_manipulators contains all visible manipulators - draw them */ + for (LinkData *link = draw_manipulators->first, *link_next; link; link = link_next) { + wmManipulator *manipulator = link->data; + link_next = link->next; + + manipulator->draw(C, manipulator); + /* free/remove manipulator link after drawing */ + BLI_freelinkN(draw_manipulators, link); + } +} + +void WM_manipulatormap_draw(wmManipulatorMap *mmap, const bContext *C, const int drawstep) +{ + 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_find_active_3D_loop(const bContext *C, ListBase *visible_manipulators) +{ + int selectionbase = 0; + wmManipulator *manipulator; + + for (LinkData *link = visible_manipulators->first; link; link = link->next) { + manipulator = link->data; + /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected manipulator part id */ + manipulator->render_3d_intersection(C, manipulator, selectionbase << 8); + + selectionbase++; + } +} + +static int manipulator_find_intersected_3D_intern( + ListBase *visible_manipulators, const bContext *C, const int co[2], + const float hotspot) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + rctf rect, selrect; + GLuint buffer[64]; // max 4 items per select, so large enuf + short hits; + const bool do_passes = GPU_select_query_check_active(); + + extern void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect); + + + rect.xmin = co[0] - hotspot; + rect.xmax = co[0] + hotspot; + rect.ymin = co[1] - hotspot; + rect.ymax = co[1] + hotspot; + + selrect = rect; + + view3d_winmatrix_set(ar, v3d, &rect); + mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat); + + if (do_passes) + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &selrect, GPU_SELECT_NEAREST_FIRST_PASS, 0); + else + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &selrect, GPU_SELECT_ALL, 0); + /* do the drawing */ + manipulator_find_active_3D_loop(C, visible_manipulators); + + hits = GPU_select_end(); + + if (do_passes) { + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &selrect, GPU_SELECT_NEAREST_SECOND_PASS, hits); + manipulator_find_active_3D_loop(C, visible_manipulators); + GPU_select_end(); + } + + view3d_winmatrix_set(ar, v3d, NULL); + mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat); + + return hits > 0 ? buffer[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, + unsigned char *part) +{ + wmManipulator *result = NULL; + const float hotspot = 14.0f; + int ret; + + *part = 0; + /* set up view matrices */ + view3d_operator_needs_opengl(C); + + ret = manipulator_find_intersected_3D_intern(visible_manipulators, C, co, 0.5f * hotspot); + + if (ret != -1) { + LinkData *link; + int retsec; + retsec = manipulator_find_intersected_3D_intern(visible_manipulators, C, co, 0.2f * hotspot); + + if (retsec != -1) + ret = retsec; + + link = BLI_findlink(visible_manipulators, ret >> 8); + *part = ret & 255; + result = link->data; + } + + 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_find_highlighted_manipulator( + wmManipulatorMap *mmap, bContext *C, const wmEvent *event, + unsigned char *part) +{ + wmManipulator *manipulator = NULL; + ListBase visible_3d_manipulators = {NULL}; + + for (wmManipulatorGroup *mgroup = mmap->manipulator_groups.first; mgroup; mgroup = mgroup->next) { + if (wm_manipulatorgroup_is_visible(mgroup, C)) { + if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) { + wm_manipulatorgroup_intersectable_manipulators_to_list(mgroup, &visible_3d_manipulators); + } + else if ((manipulator = wm_manipulatorgroup_find_intersected_mainpulator(mgroup, C, event, part))) { + break; + } + } + } + + if (!BLI_listbase_is_empty(&visible_3d_manipulators)) { + manipulator = manipulator_find_intersected_3D(C, event->mval, &visible_3d_manipulators, part); + BLI_freelistN(&visible_3d_manipulators); + } + + return manipulator; +} + +void WM_manipulatormap_add_handlers(ARegion *ar, wmManipulatorMap *mmap) +{ + wmEventHandler *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 wmOperatorType *ot) +{ + const bool modal_running = (handler->op != NULL); + + /* happens on render */ + if (!handler->op_region || !handler->op_region->manipulator_map) + return; + + /* hide operator manipulators */ + if (!modal_running && ot->mgrouptype) { + ot->mgrouptype->op = NULL; + } + + wmManipulatorMap *mmap = handler->op_region->manipulator_map; + wmManipulator *manipulator = wm_manipulatormap_get_active_manipulator(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) { + if (manipulator && manipulator->handler && manipulator->opname && + STREQ(manipulator->opname, handler->op->idname)) + { + manipulator->handler(C, event, manipulator, 0); + } + } + /* operator not running anymore */ + else { + wm_manipulatormap_set_highlighted_manipulator(mmap, C, NULL, 0); + wm_manipulatormap_set_active_manipulator(mmap, C, event, NULL); + } + + /* 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, wmManipulator ***sel) +{ + if (*sel == NULL || mmap->mmap_context.tot_selected == 0) + return false; + + for (int i = 0; i < mmap->mmap_context.tot_selected; i++) { + (*sel)[i]->state &= ~WM_MANIPULATOR_SELECTED; + (*sel)[i] = NULL; + } + wm_manipulatormap_selected_delete(mmap); + + /* always return true, we already checked + * if there's anything to deselect */ + return true; +} + +BLI_INLINE bool manipulator_selectable_poll(const wmManipulator *manipulator, void *UNUSED(data)) +{ + return (manipulator->mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECTABLE); +} + +/** + * Select all selectable manipulators in \a mmap. + * \return if selection has changed. + */ +static bool wm_manipulatormap_select_all_intern( + bContext *C, wmManipulatorMap *mmap, wmManipulator ***sel, + const int action) +{ + /* 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, *tot_sel = &mmap->mmap_context.tot_selected; + bool changed = false; + + *tot_sel = BLI_ghash_size(hash); + *sel = MEM_reallocN(*sel, sizeof(**sel) * (*tot_sel)); + + GHASH_ITER_INDEX (gh_iter, hash, i) { + wmManipulator *manipulator_iter = BLI_ghashIterator_getValue(&gh_iter); + + if ((manipulator_iter->state & WM_MANIPULATOR_SELECTED) == 0) { + changed = true; + } + manipulator_iter->state |= WM_MANIPULATOR_SELECTED; + if (manipulator_iter->select) { + manipulator_iter->select(C, manipulator_iter, action); + } + (*sel)[i] = manipulator_iter; + BLI_assert(i < (*tot_sel)); + } + /* highlight first manipulator */ + wm_manipulatormap_set_highlighted_manipulator(mmap, C, (*sel)[0], (*sel)[0]->highlighted_part); + + 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) +{ + wmManipulator ***sel = &mmap->mmap_context.selected_manipulator; + bool changed = false; + + switch (action) { + case SEL_SELECT: + changed = wm_manipulatormap_select_all_intern(C, mmap, sel, action); + break; + case SEL_DESELECT: + changed = wm_manipulatormap_deselect_all(mmap, sel); + 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) +{ + for (; mmap; mmap = mmap->next) { + wmManipulator *manipulator = mmap->mmap_context.highlighted_manipulator; + if (manipulator && manipulator->get_cursor) { + WM_cursor_set(win, manipulator->get_cursor(manipulator)); + return true; + } + } + + return false; +} + +void wm_manipulatormap_set_highlighted_manipulator( + wmManipulatorMap *mmap, const bContext *C, wmManipulator *manipulator, + unsigned char part) +{ + if ((manipulator != mmap->mmap_context.highlighted_manipulator) || + (manipulator && part != manipulator->highlighted_part)) + { + if (mmap->mmap_context.highlighted_manipulator) { + mmap->mmap_context.highlighted_manipulator->state &= ~WM_MANIPULATOR_HIGHLIGHT; + mmap->mmap_context.highlighted_manipulator->highlighted_part = 0; + } + + mmap->mmap_context.highlighted_manipulator = manipulator; + + if (manipulator) { + manipulator->state |= WM_MANIPULATOR_HIGHLIGHT; + manipulator->highlighted_part = part; + + if (C && manipulator->get_cursor) { + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, manipulator->get_cursor(manipulator)); + } + } + else { + if (C) { + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, CURSOR_STD); + } + } + + /* tag the region for redraw */ + if (C) { + ARegion *ar = CTX_wm_region(C); + ED_region_tag_redraw(ar); + } + } +} + +wmManipulator *wm_manipulatormap_get_highlighted_manipulator(wmManipulatorMap *mmap) +{ + return mmap->mmap_context.highlighted_manipulator; +} + +void wm_manipulatormap_set_active_manipulator( + wmManipulatorMap *mmap, bContext *C, const wmEvent *event, wmManipulator *manipulator) +{ + if (manipulator && C) { + manipulator->state |= WM_MANIPULATOR_ACTIVE; + mmap->mmap_context.active_manipulator = manipulator; + + if (manipulator->opname) { + wmOperatorType *ot = WM_operatortype_find(manipulator->opname, 0); + + if (ot) { + /* first activate the manipulator itself */ + if (manipulator->invoke && manipulator->handler) { + manipulator->invoke(C, event, manipulator); + } + + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &manipulator->opptr); + + /* we failed to hook the manipulator to the operator handler or operator was cancelled, return */ + if (!mmap->mmap_context.active_manipulator) { + manipulator->state &= ~WM_MANIPULATOR_ACTIVE; + /* first activate the manipulator itself */ + if (manipulator->interaction_data) { + MEM_freeN(manipulator->interaction_data); + manipulator->interaction_data = NULL; + } + } + return; + } + else { + printf("Manipulator error: operator not found"); + mmap->mmap_context.active_manipulator = NULL; + return; + } + } + else { + if (manipulator->invoke && manipulator->handler) { + manipulator->invoke(C, event, manipulator); + } + } + WM_cursor_grab_enable(CTX_wm_window(C), true, true, NULL); + } + else { + manipulator = mmap->mmap_context.active_manipulator; + + + /* deactivate, manipulator but first take care of some stuff */ + if (manipulator) { + manipulator->state &= ~WM_MANIPULATOR_ACTIVE; + /* first activate the manipulator itself */ + if (manipulator->interaction_data) { + MEM_freeN(manipulator->interaction_data); + manipulator->interaction_data = NULL; + } + } + mmap->mmap_context.active_manipulator = NULL; + + if (C) { + WM_cursor_grab_disable(CTX_wm_window(C), NULL); + ED_region_tag_redraw(CTX_wm_region(C)); + WM_event_add_mousemove(C); + } + } +} + +wmManipulator *wm_manipulatormap_get_active_manipulator(wmManipulatorMap *mmap) +{ + return mmap->mmap_context.active_manipulator; +} + +/** \} */ /* wmManipulatorMap */ + + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorMapType + * + * \{ */ + +wmManipulatorMapType *WM_manipulatormaptype_find( + const struct wmManipulatorMapType_Params *mmap_params) +{ + for (wmManipulatorMapType *mmaptype = manipulatormaptypes.first; mmaptype; mmaptype = mmaptype->next) { + if (mmaptype->spaceid == mmap_params->spaceid && + mmaptype->regionid == mmap_params->regionid && + STREQ(mmaptype->idname, mmap_params->idname)) + { + return mmaptype; + } + } + + return NULL; +} + +wmManipulatorMapType *WM_manipulatormaptype_ensure( + const struct wmManipulatorMapType_Params *mmap_params) +{ + wmManipulatorMapType *mmaptype = WM_manipulatormaptype_find(mmap_params); + + if (mmaptype) { + return mmaptype; + } + + mmaptype = MEM_callocN(sizeof(wmManipulatorMapType), "manipulatortype list"); + mmaptype->spaceid = mmap_params->spaceid; + mmaptype->regionid = mmap_params->regionid; + BLI_strncpy(mmaptype->idname, mmap_params->idname, sizeof(mmaptype->idname)); + BLI_addhead(&manipulatormaptypes, mmaptype); + + return mmaptype; +} + +void wm_manipulatormaptypes_free(void) +{ + for (wmManipulatorMapType *mmaptype = manipulatormaptypes.first; mmaptype; mmaptype = mmaptype->next) { + BLI_freelistN(&mmaptype->manipulator_grouptypes); + } + BLI_freelistN(&manipulatormaptypes); +} + +/** + * Initialize keymaps for all existing manipulator-groups + */ +void wm_manipulators_keymap(wmKeyConfig *keyconf) +{ + wmManipulatorMapType *mmaptype; + wmManipulatorGroupType *mgrouptype; + + /* 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 (mmaptype = manipulatormaptypes.first; mmaptype; mmaptype = mmaptype->next) { + for (mgrouptype = mmaptype->manipulator_grouptypes.first; mgrouptype; mgrouptype = mgrouptype->next) { + wm_manipulatorgrouptype_keymap_init(mgrouptype, keyconf); + } + } +} + +/** \} */ /* wmManipulatorMapType */ + diff --git a/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h b/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h new file mode 100644 index 00000000000..c5008cef896 --- /dev/null +++ b/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h @@ -0,0 +1,90 @@ +/* + * ***** 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/manipulators/wm_manipulator_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_MANIPULATOR_WMAPI_H__ +#define __WM_MANIPULATOR_WMAPI_H__ + +struct wmEventHandler; +struct wmManipulatorMap; +struct wmOperatorType; +struct wmOperator; + + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + +typedef void (*wmManipulatorSelectFunc)(struct bContext *, struct wmManipulator *, const int); + +struct wmManipulatorGroup *wm_manipulator_get_parent_group(const struct wmManipulator *manipulator); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +void MANIPULATORGROUP_OT_manipulator_select(struct wmOperatorType *ot); +void MANIPULATORGROUP_OT_manipulator_tweak(struct wmOperatorType *ot); + +void wm_manipulatorgroup_attach_to_modal_handler( + struct bContext *C, struct wmEventHandler *handler, + struct wmManipulatorGroupType *mgrouptype, struct wmOperator *op); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +void wm_manipulatormap_delete(struct wmManipulatorMap *mmap); +void wm_manipulatormaptypes_free(void); + +void wm_manipulators_keymap(struct wmKeyConfig *keyconf); + +void wm_manipulatormaps_handled_modal_update( + bContext *C, struct wmEvent *event, struct wmEventHandler *handler, + const struct wmOperatorType *ot); +void wm_manipulatormap_handler_context(bContext *C, struct wmEventHandler *handler); + +struct wmManipulator *wm_manipulatormap_find_highlighted_manipulator( + struct wmManipulatorMap *mmap, bContext *C, + const struct wmEvent *event, unsigned char *part); +void wm_manipulatormap_set_highlighted_manipulator( + struct wmManipulatorMap *mmap, const bContext *C, + struct wmManipulator *manipulator, unsigned char part); +struct wmManipulator *wm_manipulatormap_get_highlighted_manipulator(struct wmManipulatorMap *mmap); +void wm_manipulatormap_set_active_manipulator( + struct wmManipulatorMap *mmap, bContext *C, + const struct wmEvent *event, struct wmManipulator *manipulator); +struct wmManipulator *wm_manipulatormap_get_active_manipulator(struct wmManipulatorMap *mmap); + +#endif /* __WM_MANIPULATOR_WMAPI_H__ */ + diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index 2f06ddab1e8..3dd294128e2 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -34,6 +34,8 @@ struct wmWindow; struct ReportList; +#include "manipulators/wm_manipulator_wmapi.h" + typedef struct wmPaintCursor { struct wmPaintCursor *next, *prev; diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index efc01b1f8a8..abab7c55f44 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -68,7 +68,8 @@ typedef struct wmEventHandler { /* drop box handler */ ListBase *dropboxes; - + /* manipulator handler */ + struct wmManipulatorMap *manipulator_map; } wmEventHandler; /* custom types for handlers, for signalling, freeing */ diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 3085f138846..9d34bc24e6c 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -339,6 +339,8 @@ enum { EVT_DROP = 0x5023, EVT_BUT_CANCEL = 0x5024, + /* could become manipulator callback */ + EVT_MANIPULATOR_UPDATE = 0x5025, /* ********** End of Blender internal events. ********** */ }; -- cgit v1.2.3