diff options
Diffstat (limited to 'source/blender/editors/interface/interface_eyedropper.c')
-rw-r--r-- | source/blender/editors/interface/interface_eyedropper.c | 442 |
1 files changed, 366 insertions, 76 deletions
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index ceea4ff42d9..2cbc56b14d3 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -29,6 +29,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_anim_types.h" #include "DNA_space_types.h" #include "DNA_screen_types.h" #include "DNA_object_types.h" @@ -41,10 +42,13 @@ #include "BKE_context.h" #include "BKE_screen.h" #include "BKE_report.h" +#include "BKE_animsys.h" +#include "BKE_depsgraph.h" #include "BKE_idcode.h" #include "BKE_unit.h" #include "RNA_access.h" +#include "RNA_define.h" #include "BIF_gl.h" @@ -67,6 +71,62 @@ #include "ED_screen.h" #include "ED_view3d.h" +/* for Driver eyedropper */ +#include "ED_keyframing.h" + + +/* -------------------------------------------------------------------- */ +/* Keymap + */ +/** \name Modal Keymap + * \{ */ + +enum { + EYE_MODAL_CANCEL = 1, + EYE_MODAL_SAMPLE_CONFIRM, + EYE_MODAL_SAMPLE_BEGIN, + EYE_MODAL_SAMPLE_RESET, +}; + +wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf) +{ + static EnumPropertyItem modal_items[] = { + {EYE_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, + {EYE_MODAL_SAMPLE_CONFIRM, "SAMPLE_CONFIRM", 0, "Confirm Sampling", ""}, + {EYE_MODAL_SAMPLE_BEGIN, "SAMPLE_BEGIN", 0, "Start Sampling", ""}, + {EYE_MODAL_SAMPLE_RESET, "SAMPLE_RESET", 0, "Reset Sampling", ""}, + {0, NULL, 0, NULL, NULL} + }; + + wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Eyedropper Modal Map"); + + /* 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, "Eyedropper Modal Map", modal_items); + + /* items for modal map */ + WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, EYE_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, EYE_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, RETKEY, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_CONFIRM); + WM_modalkeymap_add_item(keymap, PADENTER, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_CONFIRM); + WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_CONFIRM); + WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, EYE_MODAL_SAMPLE_BEGIN); + WM_modalkeymap_add_item(keymap, SPACEKEY, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_RESET); + + /* assign to operators */ + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color"); + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_id"); + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth"); + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_driver"); + + return keymap; +} + +/** \} */ + + /* -------------------------------------------------------------------- */ /* Utility Functions */ @@ -97,6 +157,32 @@ static void eyedropper_draw_cursor_text(const struct bContext *C, ARegion *ar, c UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, fg, bg); } + +/** + * Utility to retrieve a button representing a RNA property that is currently under the cursor. + * + * This is to be used by any eyedroppers which fetch properties (e.g. UI_OT_eyedropper_driver). + * Especially during modal operations (e.g. as with the eyedroppers), context cannot be relied + * upon to provide this information, as it is not updated until the operator finishes. + * + * \return A button under the mouse which relates to some RNA Property, or NULL + */ +static uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event) +{ + wmWindow *win = CTX_wm_window(C); + ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, event->x, event->y); + ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_ANY, event->x, event->y); + + uiBut *but = ui_but_find_mouse_over(ar, event); + + if (ELEM(NULL, but, but->rnapoin.data, but->rnaprop)) { + return NULL; + } + else { + return but; + } +} + /** \} */ @@ -114,7 +200,9 @@ typedef struct Eyedropper { PropertyRNA *prop; int index; - bool accum_start; /* has mouse been presed */ + float init_col[3]; /* for resetting on cancel */ + + bool accum_start; /* has mouse been pressed */ float accum_col[3]; int accum_tot; } Eyedropper; @@ -139,9 +227,17 @@ static bool eyedropper_init(bContext *C, wmOperator *op) if (RNA_property_subtype(eye->prop) == PROP_COLOR) { const char *display_device; + float col[4]; display_device = scene->display_settings.display_device; eye->display = IMB_colormanagement_display_get_named(display_device); + + /* store inital color */ + RNA_property_float_get_array(&eye->ptr, eye->prop, col); + if (eye->display) { + IMB_colormanagement_scene_linear_to_display_v3(col, eye->display); + } + copy_v3_v3(eye->init_col, col); } return true; @@ -157,11 +253,6 @@ static void eyedropper_exit(bContext *C, wmOperator *op) } } -static void eyedropper_cancel(bContext *C, wmOperator *op) -{ - eyedropper_exit(C, op); -} - /* *** eyedropper_color_ helper functions *** */ /** @@ -268,18 +359,25 @@ static void eyedropper_color_sample_accum(bContext *C, Eyedropper *eye, int mx, eye->accum_tot++; } +static void eyedropper_cancel(bContext *C, wmOperator *op) +{ + Eyedropper *eye = op->customdata; + eyedropper_color_set(C, eye, eye->init_col); + eyedropper_exit(C, op); +} + /* main modal status check */ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) { Eyedropper *eye = (Eyedropper *)op->customdata; - switch (event->type) { - case ESCKEY: - case RIGHTMOUSE: - eyedropper_cancel(C, op); - return OPERATOR_CANCELLED; - case LEFTMOUSE: - if (event->val == KM_RELEASE) { + /* handle modal keymap */ + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EYE_MODAL_CANCEL: + eyedropper_cancel(C, op); + return OPERATOR_CANCELLED; + case EYE_MODAL_SAMPLE_CONFIRM: if (eye->accum_tot == 0) { eyedropper_color_sample(C, eye, event->x, event->y); } @@ -288,28 +386,25 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) } eyedropper_exit(C, op); return OPERATOR_FINISHED; - } - else if (event->val == KM_PRESS) { + case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ eye->accum_start = true; eyedropper_color_sample_accum(C, eye, event->x, event->y); - } - break; - case MOUSEMOVE: - if (eye->accum_start) { - /* button is pressed so keep sampling */ - eyedropper_color_sample_accum(C, eye, event->x, event->y); - eyedropper_color_set_accum(C, eye); - } - break; - case SPACEKEY: - if (event->val == KM_RELEASE) { + break; + case EYE_MODAL_SAMPLE_RESET: eye->accum_tot = 0; zero_v3(eye->accum_col); eyedropper_color_sample_accum(C, eye, event->x, event->y); eyedropper_color_set_accum(C, eye); - } - break; + break; + } + } + else if (event->type == MOUSEMOVE) { + if (eye->accum_start) { + /* button is pressed so keep sampling */ + eyedropper_color_sample_accum(C, eye, event->x, event->y); + eyedropper_color_set_accum(C, eye); + } } return OPERATOR_RUNNING_MODAL; @@ -362,7 +457,7 @@ void UI_OT_eyedropper_color(wmOperatorType *ot) /* identifiers */ ot->name = "Eyedropper"; ot->idname = "UI_OT_eyedropper_color"; - ot->description = "Sample a data-block from the 3D view"; + ot->description = "Sample a color from the Blender Window to store in a property"; /* api callbacks */ ot->invoke = eyedropper_invoke; @@ -394,6 +489,8 @@ typedef struct DataDropper { short idcode; const char *idcode_name; + ID *init_id; /* for resetting on cancel */ + ARegionType *art; void *draw_handle_pixel; char name[200]; @@ -440,6 +537,9 @@ static int datadropper_init(bContext *C, wmOperator *op) /* Note we can translate here (instead of on draw time), because this struct has very short lifetime. */ ddr->idcode_name = TIP_(BKE_idcode_to_name(ddr->idcode)); + PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop); + ddr->init_id = ptr.id.data; + return true; } @@ -462,11 +562,6 @@ static void datadropper_exit(bContext *C, wmOperator *op) WM_event_add_mousemove(C); } -static void datadropper_cancel(bContext *C, wmOperator *op) -{ - datadropper_exit(C, op); -} - /* *** datadropper id helper functions *** */ /** * \brief get the ID from the screen. @@ -474,7 +569,6 @@ static void datadropper_cancel(bContext *C, wmOperator *op) */ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int my, ID **r_id) { - /* we could use some clever */ wmWindow *win = CTX_wm_window(C); ScrArea *sa = BKE_screen_find_area_xy(win->screen, -1, mx, my); @@ -555,18 +649,26 @@ static bool datadropper_id_sample(bContext *C, DataDropper *ddr, int mx, int my) return datadropper_id_set(C, ddr, id); } +static void datadropper_cancel(bContext *C, wmOperator *op) +{ + DataDropper *ddr = op->customdata; + datadropper_id_set(C, ddr, ddr->init_id); + datadropper_exit(C, op); +} + /* main modal status check */ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) { DataDropper *ddr = (DataDropper *)op->customdata; - switch (event->type) { - case ESCKEY: - case RIGHTMOUSE: - datadropper_cancel(C, op); - return OPERATOR_CANCELLED; - case LEFTMOUSE: - if (event->val == KM_RELEASE) { + /* handle modal keymap */ + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EYE_MODAL_CANCEL: + datadropper_cancel(C, op); + return OPERATOR_CANCELLED; + case EYE_MODAL_SAMPLE_CONFIRM: + { bool success; success = datadropper_id_sample(C, ddr, event->x, event->y); @@ -580,14 +682,12 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } } - break; - case MOUSEMOVE: - { - ID *id = NULL; - datadropper_id_sample_pt(C, ddr, event->x, event->y, &id); - break; } } + else if (event->type == MOUSEMOVE) { + ID *id = NULL; + datadropper_id_sample_pt(C, ddr, event->x, event->y, &id); + } return OPERATOR_RUNNING_MODAL; } @@ -636,7 +736,7 @@ void UI_OT_eyedropper_id(wmOperatorType *ot) /* identifiers */ ot->name = "Eyedropper Datablock"; ot->idname = "UI_OT_eyedropper_id"; - ot->description = "Sample a datablock from the Blender Window to store in a property"; + ot->description = "Sample a datablock from the 3D View to store in a property"; /* api callbacks */ ot->invoke = datadropper_invoke; @@ -666,6 +766,8 @@ typedef struct DepthDropper { PointerRNA ptr; PropertyRNA *prop; + float init_depth; /* for resetting on cancel */ + bool accum_start; /* has mouse been presed */ float accum_depth; int accum_tot; @@ -720,6 +822,7 @@ static int depthdropper_init(bContext *C, wmOperator *op) ddr->art = art; ddr->draw_handle_pixel = ED_region_draw_cb_activate(art, depthdropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); + ddr->init_depth = RNA_property_float_get(&ddr->ptr, ddr->prop); return true; } @@ -741,11 +844,6 @@ static void depthdropper_exit(bContext *C, wmOperator *op) } } -static void depthdropper_cancel(bContext *C, wmOperator *op) -{ - depthdropper_exit(C, op); -} - /* *** depthdropper id helper functions *** */ /** * \brief get the ID from the screen. @@ -850,18 +948,25 @@ static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, int } } +static void depthdropper_cancel(bContext *C, wmOperator *op) +{ + DepthDropper *ddr = op->customdata; + depthdropper_depth_set(C, ddr, ddr->init_depth); + depthdropper_exit(C, op); +} + /* main modal status check */ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) { DepthDropper *ddr = (DepthDropper *)op->customdata; - switch (event->type) { - case ESCKEY: - case RIGHTMOUSE: - depthdropper_cancel(C, op); - return OPERATOR_CANCELLED; - case LEFTMOUSE: - if (event->val == KM_RELEASE) { + /* handle modal keymap */ + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EYE_MODAL_CANCEL: + depthdropper_cancel(C, op); + return OPERATOR_CANCELLED; + case EYE_MODAL_SAMPLE_CONFIRM: if (ddr->accum_tot == 0) { depthdropper_depth_sample(C, ddr, event->x, event->y); } @@ -870,28 +975,25 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) } depthdropper_exit(C, op); return OPERATOR_FINISHED; - } - else if (event->val == KM_PRESS) { + case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ ddr->accum_start = true; depthdropper_depth_sample_accum(C, ddr, event->x, event->y); - } - break; - case MOUSEMOVE: - if (ddr->accum_start) { - /* button is pressed so keep sampling */ - depthdropper_depth_sample_accum(C, ddr, event->x, event->y); - depthdropper_depth_set_accum(C, ddr); - } - break; - case SPACEKEY: - if (event->val == KM_RELEASE) { + break; + case EYE_MODAL_SAMPLE_RESET: ddr->accum_tot = 0; ddr->accum_depth = 0.0f; depthdropper_depth_sample_accum(C, ddr, event->x, event->y); depthdropper_depth_set_accum(C, ddr); - } - break; + break; + } + } + else if (event->type == MOUSEMOVE) { + if (ddr->accum_start) { + /* button is pressed so keep sampling */ + depthdropper_depth_sample_accum(C, ddr, event->x, event->y); + depthdropper_depth_set_accum(C, ddr); + } } return OPERATOR_RUNNING_MODAL; @@ -957,3 +1059,191 @@ void UI_OT_eyedropper_depth(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/* Eyedropper + */ + +/* NOTE: This is here (instead of in drivers.c) because we need access the button internals, + * which we cannot access outside of the interface module + */ + +/** \name Eyedropper (Driver Target) + * \{ */ + +typedef struct DriverDropper { + /* Destination property (i.e. where we'll add a driver) */ + PointerRNA ptr; + PropertyRNA *prop; + int index; + + // TODO: new target? +} DriverDropper; + +static bool driverdropper_init(bContext *C, wmOperator *op) +{ + DriverDropper *ddr; + uiBut *but; + + op->customdata = ddr = MEM_callocN(sizeof(DriverDropper), "DriverDropper"); + + UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &ddr->index); + but = UI_context_active_but_get(C); + + if ((ddr->ptr.data == NULL) || + (ddr->prop == NULL) || + (RNA_property_editable(&ddr->ptr, ddr->prop) == false) || + (RNA_property_animateable(&ddr->ptr, ddr->prop) == false) || + (but->flag & UI_BUT_DRIVEN)) + { + return false; + } + + return true; +} + +static void driverdropper_exit(bContext *C, wmOperator *op) +{ + WM_cursor_modal_restore(CTX_wm_window(C)); + + if (op->customdata) { + MEM_freeN(op->customdata); + op->customdata = NULL; + } +} + +static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *event) +{ + DriverDropper *ddr = (DriverDropper *)op->customdata; + uiBut *but = eyedropper_get_property_button_under_mouse(C, event); + + short mapping_type = RNA_enum_get(op->ptr, "mapping_type"); + short flag = 0; + + /* we can only add a driver if we know what RNA property it corresponds to */ + if (but == NULL) { + return; + } + else { + /* Get paths for src... */ + PointerRNA *target_ptr = &but->rnapoin; + PropertyRNA *target_prop = but->rnaprop; + int target_index = but->rnaindex; + + char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop); + + /* ... and destination */ + char *dst_path = BKE_animdata_driver_path_hack(C, &ddr->ptr, ddr->prop, NULL); + + /* Now create driver(s) */ + if (target_path && dst_path) { + int success = ANIM_add_driver_with_target(op->reports, + ddr->ptr.id.data, dst_path, ddr->index, + target_ptr->id.data, target_path, target_index, + flag, DRIVER_TYPE_PYTHON, mapping_type); + + if (success) { + /* send updates */ + UI_context_update_anim_flag(C); + DAG_relations_tag_update(CTX_data_main(C)); + DAG_id_tag_update(ddr->ptr.id.data, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX + } + } + + /* cleanup */ + if (target_path) + MEM_freeN(target_path); + if (dst_path) + MEM_freeN(dst_path); + } +} + +static void driverdropper_cancel(bContext *C, wmOperator *op) +{ + driverdropper_exit(C, op); +} + +/* main modal status check */ +static int driverdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + /* handle modal keymap */ + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EYE_MODAL_CANCEL: + driverdropper_cancel(C, op); + return OPERATOR_CANCELLED; + + case EYE_MODAL_SAMPLE_CONFIRM: + driverdropper_sample(C, op, event); + driverdropper_exit(C, op); + + return OPERATOR_FINISHED; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +/* Modal Operator init */ +static int driverdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + /* init */ + if (driverdropper_init(C, op)) { + WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; + } + else { + driverdropper_exit(C, op); + return OPERATOR_CANCELLED; + } +} + +/* Repeat operator */ +static int driverdropper_exec(bContext *C, wmOperator *op) +{ + /* init */ + if (driverdropper_init(C, op)) { + /* cleanup */ + driverdropper_exit(C, op); + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static int driverdropper_poll(bContext *C) +{ + if (!CTX_wm_window(C)) return 0; + else return 1; +} + +void UI_OT_eyedropper_driver(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Eyedropper Driver"; + ot->idname = "UI_OT_eyedropper_driver"; + ot->description = "Pick a property to use as a driver target"; + + /* api callbacks */ + ot->invoke = driverdropper_invoke; + ot->modal = driverdropper_modal; + ot->cancel = driverdropper_cancel; + ot->exec = driverdropper_exec; + ot->poll = driverdropper_poll; + + /* flags */ + ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL | OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "mapping_type", prop_driver_create_mapping_types, 0, + "Mapping Type", "Method used to match target and driven properties"); +} + +/** \} */ |