Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/interface/eyedroppers')
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_color.c554
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_colorband.c367
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_datablock.c378
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_depth.c381
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_driver.c220
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c373
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_intern.h57
-rw-r--r--source/blender/editors/interface/eyedroppers/interface_eyedropper.c161
8 files changed, 2491 insertions, 0 deletions
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_color.c
new file mode 100644
index 00000000000..a2161008ec4
--- /dev/null
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_color.c
@@ -0,0 +1,554 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2009 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup edinterface
+ *
+ * Eyedropper (RGB Color)
+ *
+ * Defines:
+ * - #UI_OT_eyedropper_color
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math_vector.h"
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+#include "BKE_cryptomatte.h"
+#include "BKE_image.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+#include "BKE_screen.h"
+
+#include "NOD_composite.h"
+
+#include "RNA_access.h"
+#include "RNA_prototypes.h"
+
+#include "UI_interface.h"
+
+#include "IMB_colormanagement.h"
+#include "IMB_imbuf_types.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_define.h"
+
+#include "interface_intern.h"
+
+#include "ED_clip.h"
+#include "ED_image.h"
+#include "ED_node.h"
+#include "ED_screen.h"
+
+#include "RE_pipeline.h"
+
+#include "eyedropper_intern.h"
+
+typedef struct Eyedropper {
+ struct ColorManagedDisplay *display;
+
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ int index;
+ bool is_undo;
+
+ bool is_set;
+ float init_col[3]; /* for resetting on cancel */
+
+ bool accum_start; /* has mouse been pressed */
+ float accum_col[3];
+ int accum_tot;
+
+ void *draw_handle_sample_text;
+ char sample_text[MAX_NAME];
+
+ bNode *crypto_node;
+ struct CryptomatteSession *cryptomatte_session;
+} Eyedropper;
+
+static void eyedropper_draw_cb(const wmWindow *window, void *arg)
+{
+ Eyedropper *eye = arg;
+ eyedropper_draw_cursor_text_window(window, eye->sample_text);
+}
+
+static bool eyedropper_init(bContext *C, wmOperator *op)
+{
+ Eyedropper *eye = MEM_callocN(sizeof(Eyedropper), __func__);
+
+ uiBut *but = UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index);
+ const enum PropertySubType prop_subtype = eye->prop ? RNA_property_subtype(eye->prop) : 0;
+
+ if ((eye->ptr.data == NULL) || (eye->prop == NULL) ||
+ (RNA_property_editable(&eye->ptr, eye->prop) == false) ||
+ (RNA_property_array_length(&eye->ptr, eye->prop) < 3) ||
+ (RNA_property_type(eye->prop) != PROP_FLOAT) ||
+ (ELEM(prop_subtype, PROP_COLOR, PROP_COLOR_GAMMA) == 0)) {
+ MEM_freeN(eye);
+ return false;
+ }
+ op->customdata = eye;
+
+ eye->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO);
+
+ float col[4];
+ RNA_property_float_get_array(&eye->ptr, eye->prop, col);
+ if (eye->ptr.type == &RNA_CompositorNodeCryptomatteV2) {
+ eye->crypto_node = (bNode *)eye->ptr.data;
+ eye->cryptomatte_session = ntreeCompositCryptomatteSession(CTX_data_scene(C),
+ eye->crypto_node);
+ eye->draw_handle_sample_text = WM_draw_cb_activate(CTX_wm_window(C), eyedropper_draw_cb, eye);
+ }
+
+ if (prop_subtype != PROP_COLOR) {
+ Scene *scene = CTX_data_scene(C);
+ const char *display_device;
+
+ display_device = scene->display_settings.display_device;
+ eye->display = IMB_colormanagement_display_get_named(display_device);
+
+ /* store initial color */
+ if (eye->display) {
+ IMB_colormanagement_display_to_scene_linear_v3(col, eye->display);
+ }
+ }
+ copy_v3_v3(eye->init_col, col);
+
+ return true;
+}
+
+static void eyedropper_exit(bContext *C, wmOperator *op)
+{
+ Eyedropper *eye = op->customdata;
+ wmWindow *window = CTX_wm_window(C);
+ WM_cursor_modal_restore(window);
+
+ if (eye->draw_handle_sample_text) {
+ WM_draw_cb_exit(window, eye->draw_handle_sample_text);
+ eye->draw_handle_sample_text = NULL;
+ }
+
+ if (eye->cryptomatte_session) {
+ BKE_cryptomatte_free(eye->cryptomatte_session);
+ eye->cryptomatte_session = NULL;
+ }
+
+ MEM_SAFE_FREE(op->customdata);
+}
+
+/* *** eyedropper_color_ helper functions *** */
+
+static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_layer,
+ const char *prefix,
+ const float fpos[2],
+ float r_col[3])
+{
+ if (!render_layer) {
+ return false;
+ }
+
+ const int render_layer_name_len = BLI_strnlen(render_layer->name, sizeof(render_layer->name));
+ if (strncmp(prefix, render_layer->name, render_layer_name_len) != 0) {
+ return false;
+ }
+
+ const int prefix_len = strlen(prefix);
+ if (prefix_len <= render_layer_name_len + 1) {
+ return false;
+ }
+
+ /* RenderResult from images can have no render layer name. */
+ const char *render_pass_name_prefix = render_layer_name_len ?
+ prefix + 1 + render_layer_name_len :
+ prefix;
+
+ LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
+ if (STRPREFIX(render_pass->name, render_pass_name_prefix) &&
+ !STREQLEN(render_pass->name, render_pass_name_prefix, sizeof(render_pass->name))) {
+ BLI_assert(render_pass->channels == 4);
+ const int x = (int)(fpos[0] * render_pass->rectx);
+ const int y = (int)(fpos[1] * render_pass->recty);
+ const int offset = 4 * (y * render_pass->rectx + x);
+ zero_v3(r_col);
+ r_col[0] = render_pass->rect[offset];
+ return true;
+ }
+ }
+
+ return false;
+}
+static bool eyedropper_cryptomatte_sample_render_fl(const bNode *node,
+ const char *prefix,
+ const float fpos[2],
+ float r_col[3])
+{
+ bool success = false;
+ Scene *scene = (Scene *)node->id;
+ BLI_assert(GS(scene->id.name) == ID_SCE);
+ Render *re = RE_GetSceneRender(scene);
+
+ if (re) {
+ RenderResult *rr = RE_AcquireResultRead(re);
+ if (rr) {
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name);
+ success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col);
+ if (success) {
+ break;
+ }
+ }
+ }
+ RE_ReleaseResult(re);
+ }
+ return success;
+}
+
+static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node,
+ NodeCryptomatte *crypto,
+ const char *prefix,
+ const float fpos[2],
+ float r_col[3])
+{
+ bool success = false;
+ Image *image = (Image *)node->id;
+ BLI_assert((image == NULL) || (GS(image->id.name) == ID_IM));
+ ImageUser *iuser = &crypto->iuser;
+
+ if (image && image->type == IMA_TYPE_MULTILAYER) {
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL);
+ if (image->rr) {
+ LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) {
+ success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col);
+ if (success) {
+ break;
+ }
+ }
+ }
+ BKE_image_release_ibuf(image, ibuf, NULL);
+ }
+ return success;
+}
+
+static bool eyedropper_cryptomatte_sample_fl(bContext *C,
+ Eyedropper *eye,
+ const int m_xy[2],
+ float r_col[3])
+{
+ bNode *node = eye->crypto_node;
+ NodeCryptomatte *crypto = node ? ((NodeCryptomatte *)node->storage) : NULL;
+
+ if (!crypto) {
+ return false;
+ }
+
+ bScreen *screen = CTX_wm_screen(C);
+ ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, m_xy);
+ if (!area || !ELEM(area->spacetype, SPACE_IMAGE, SPACE_NODE, SPACE_CLIP)) {
+ return false;
+ }
+
+ ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy);
+ if (!region) {
+ return false;
+ }
+
+ int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin};
+ float fpos[2] = {-1.0f, -1.0};
+ switch (area->spacetype) {
+ case SPACE_IMAGE: {
+ SpaceImage *sima = area->spacedata.first;
+ ED_space_image_get_position(sima, region, mval, fpos);
+ break;
+ }
+ case SPACE_NODE: {
+ Main *bmain = CTX_data_main(C);
+ SpaceNode *snode = area->spacedata.first;
+ ED_space_node_get_position(bmain, snode, region, mval, fpos);
+ break;
+ }
+ case SPACE_CLIP: {
+ SpaceClip *sc = area->spacedata.first;
+ ED_space_clip_get_position(sc, region, mval, fpos);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ if (fpos[0] < 0.0f || fpos[1] < 0.0f || fpos[0] >= 1.0f || fpos[1] >= 1.0f) {
+ return false;
+ }
+
+ /* CMP_CRYPTOMATTE_SRC_RENDER and CMP_CRYPTOMATTE_SRC_IMAGE require a referenced image/scene to
+ * work properly. */
+ if (!node->id) {
+ return false;
+ }
+
+ /* TODO(jbakker): Migrate this file to cc and use std::string as return param. */
+ char prefix[MAX_NAME + 1];
+ const Scene *scene = CTX_data_scene(C);
+ ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1);
+ prefix[MAX_NAME] = '\0';
+
+ if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) {
+ return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col);
+ }
+ if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) {
+ return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col);
+ }
+ return false;
+}
+
+void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3])
+{
+ /* we could use some clever */
+ Main *bmain = CTX_data_main(C);
+ wmWindowManager *wm = CTX_wm_manager(C);
+ const char *display_device = CTX_data_scene(C)->display_settings.display_device;
+ struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
+
+ int mval[2];
+ wmWindow *win;
+ ScrArea *area;
+ datadropper_win_area_find(C, m_xy, mval, &win, &area);
+
+ if (area) {
+ if (area->spacetype == SPACE_IMAGE) {
+ ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval);
+ if (region) {
+ SpaceImage *sima = area->spacedata.first;
+ const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
+
+ if (ED_space_image_color_sample(sima, region, region_mval, r_col, NULL)) {
+ return;
+ }
+ }
+ }
+ else if (area->spacetype == SPACE_NODE) {
+ ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval);
+ if (region) {
+ SpaceNode *snode = area->spacedata.first;
+ const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
+
+ if (ED_space_node_color_sample(bmain, snode, region, region_mval, r_col)) {
+ return;
+ }
+ }
+ }
+ else if (area->spacetype == SPACE_CLIP) {
+ ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval);
+ if (region) {
+ SpaceClip *sc = area->spacedata.first;
+ const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
+
+ if (ED_space_clip_color_sample(sc, region, region_mval, r_col)) {
+ return;
+ }
+ }
+ }
+ }
+
+ if (win) {
+ /* Fallback to simple opengl picker. */
+ WM_window_pixel_sample_read(wm, win, mval, r_col);
+ IMB_colormanagement_display_to_scene_linear_v3(r_col, display);
+ }
+ else {
+ zero_v3(r_col);
+ }
+}
+
+/* sets the sample color RGB, maintaining A */
+static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3])
+{
+ float col_conv[4];
+
+ /* to maintain alpha */
+ RNA_property_float_get_array(&eye->ptr, eye->prop, col_conv);
+
+ /* convert from linear rgb space to display space */
+ if (eye->display) {
+ copy_v3_v3(col_conv, col);
+ IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display);
+ }
+ else {
+ copy_v3_v3(col_conv, col);
+ }
+
+ RNA_property_float_set_array(&eye->ptr, eye->prop, col_conv);
+ eye->is_set = true;
+
+ RNA_property_update(C, &eye->ptr, eye->prop);
+}
+
+static void eyedropper_color_sample(bContext *C, Eyedropper *eye, const int m_xy[2])
+{
+ /* Accumulate color. */
+ float col[3];
+ if (eye->crypto_node) {
+ if (!eyedropper_cryptomatte_sample_fl(C, eye, m_xy, col)) {
+ return;
+ }
+ }
+ else {
+ eyedropper_color_sample_fl(C, m_xy, col);
+ }
+
+ if (!eye->crypto_node) {
+ add_v3_v3(eye->accum_col, col);
+ eye->accum_tot++;
+ }
+ else {
+ copy_v3_v3(eye->accum_col, col);
+ eye->accum_tot = 1;
+ }
+
+ /* Apply to property. */
+ float accum_col[3];
+ if (eye->accum_tot > 1) {
+ mul_v3_v3fl(accum_col, eye->accum_col, 1.0f / (float)eye->accum_tot);
+ }
+ else {
+ copy_v3_v3(accum_col, eye->accum_col);
+ }
+ eyedropper_color_set(C, eye, accum_col);
+}
+
+static void eyedropper_color_sample_text_update(bContext *C, Eyedropper *eye, const int m_xy[2])
+{
+ float col[3];
+ eye->sample_text[0] = '\0';
+
+ if (eye->cryptomatte_session) {
+ if (eyedropper_cryptomatte_sample_fl(C, eye, m_xy, col)) {
+ BKE_cryptomatte_find_name(
+ eye->cryptomatte_session, col[0], eye->sample_text, sizeof(eye->sample_text));
+ eye->sample_text[sizeof(eye->sample_text) - 1] = '\0';
+ }
+ }
+}
+
+static void eyedropper_cancel(bContext *C, wmOperator *op)
+{
+ Eyedropper *eye = op->customdata;
+ if (eye->is_set) {
+ 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;
+
+ /* 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: {
+ const bool is_undo = eye->is_undo;
+ if (eye->accum_tot == 0) {
+ eyedropper_color_sample(C, eye, event->xy);
+ }
+ eyedropper_exit(C, op);
+ /* Could support finished & undo-skip. */
+ return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ }
+ case EYE_MODAL_SAMPLE_BEGIN:
+ /* enable accum and make first sample */
+ eye->accum_start = true;
+ eyedropper_color_sample(C, eye, event->xy);
+ break;
+ case EYE_MODAL_SAMPLE_RESET:
+ eye->accum_tot = 0;
+ zero_v3(eye->accum_col);
+ eyedropper_color_sample(C, eye, event->xy);
+ break;
+ }
+ }
+ else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (eye->accum_start) {
+ /* button is pressed so keep sampling */
+ eyedropper_color_sample(C, eye, event->xy);
+ }
+
+ if (eye->draw_handle_sample_text) {
+ eyedropper_color_sample_text_update(C, eye, event->xy);
+ ED_region_tag_redraw(CTX_wm_region(C));
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* Modal Operator init */
+static int eyedropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ /* init */
+ if (eyedropper_init(C, op)) {
+ wmWindow *win = CTX_wm_window(C);
+ /* Workaround for de-activating the button clearing the cursor, see T76794 */
+ UI_context_active_but_clear(C, win, CTX_wm_region(C));
+ WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER);
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_PASS_THROUGH;
+}
+
+/* Repeat operator */
+static int eyedropper_exec(bContext *C, wmOperator *op)
+{
+ /* init */
+ if (eyedropper_init(C, op)) {
+
+ /* do something */
+
+ /* cleanup */
+ eyedropper_exit(C, op);
+
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_PASS_THROUGH;
+}
+
+static bool eyedropper_poll(bContext *C)
+{
+ /* Actual test for active button happens later, since we don't
+ * know which one is active until mouse over. */
+ return (CTX_wm_window(C) != NULL);
+}
+
+void UI_OT_eyedropper_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Eyedropper";
+ ot->idname = "UI_OT_eyedropper_color";
+ ot->description = "Sample a color from the Blender window to store in a property";
+
+ /* api callbacks */
+ ot->invoke = eyedropper_invoke;
+ ot->modal = eyedropper_modal;
+ ot->cancel = eyedropper_cancel;
+ ot->exec = eyedropper_exec;
+ ot->poll = eyedropper_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL;
+}
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c
new file mode 100644
index 00000000000..3f63a8020ed
--- /dev/null
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c
@@ -0,0 +1,367 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2009 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup edinterface
+ *
+ * Eyedropper (Color Band).
+ *
+ * Operates by either:
+ * - Dragging a straight line, sampling pixels formed by the line to extract a gradient.
+ * - Clicking on points, adding each color to the end of the color-band.
+ *
+ * Defines:
+ * - #UI_OT_eyedropper_colorramp
+ * - #UI_OT_eyedropper_colorramp_point
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_screen_types.h"
+
+#include "BLI_bitmap_draw_2d.h"
+#include "BLI_math_vector.h"
+
+#include "BKE_colorband.h"
+#include "BKE_context.h"
+
+#include "RNA_access.h"
+#include "RNA_prototypes.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "interface_intern.h"
+
+#include "eyedropper_intern.h"
+
+typedef struct Colorband_RNAUpdateCb {
+ PointerRNA ptr;
+ PropertyRNA *prop;
+} Colorband_RNAUpdateCb;
+
+typedef struct EyedropperColorband {
+ int event_xy_last[2];
+ /* Alpha is currently fixed at 1.0, may support in future. */
+ float (*color_buffer)[4];
+ int color_buffer_alloc;
+ int color_buffer_len;
+ bool sample_start;
+ ColorBand init_color_band;
+ ColorBand *color_band;
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ bool is_undo;
+ bool is_set;
+} EyedropperColorband;
+
+/* For user-data only. */
+struct EyedropperColorband_Context {
+ bContext *context;
+ EyedropperColorband *eye;
+};
+
+static bool eyedropper_colorband_init(bContext *C, wmOperator *op)
+{
+ ColorBand *band = NULL;
+
+ uiBut *but = UI_context_active_but_get(C);
+
+ PointerRNA rna_update_ptr = PointerRNA_NULL;
+ PropertyRNA *rna_update_prop = NULL;
+ bool is_undo = true;
+
+ if (but == NULL) {
+ /* pass */
+ }
+ else {
+ if (but->type == UI_BTYPE_COLORBAND) {
+ /* When invoked with a hotkey, we can find the band in 'but->poin'. */
+ band = (ColorBand *)but->poin;
+ }
+ else {
+ /* When invoked from a button it's in custom_data field. */
+ band = (ColorBand *)but->custom_data;
+ }
+
+ if (band) {
+ rna_update_ptr = ((Colorband_RNAUpdateCb *)but->func_argN)->ptr;
+ rna_update_prop = ((Colorband_RNAUpdateCb *)but->func_argN)->prop;
+ is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO);
+ }
+ }
+
+ if (!band) {
+ const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp);
+ if (ptr.data != NULL) {
+ band = ptr.data;
+
+ /* Set this to a sub-member of the property to trigger an update. */
+ rna_update_ptr = ptr;
+ rna_update_prop = &rna_ColorRamp_color_mode;
+ is_undo = RNA_struct_undo_check(ptr.type);
+ }
+ }
+
+ if (!band) {
+ return false;
+ }
+
+ EyedropperColorband *eye = MEM_callocN(sizeof(EyedropperColorband), __func__);
+ eye->color_buffer_alloc = 16;
+ eye->color_buffer = MEM_mallocN(sizeof(*eye->color_buffer) * eye->color_buffer_alloc, __func__);
+ eye->color_buffer_len = 0;
+ eye->color_band = band;
+ eye->init_color_band = *eye->color_band;
+ eye->ptr = rna_update_ptr;
+ eye->prop = rna_update_prop;
+ eye->is_undo = is_undo;
+
+ op->customdata = eye;
+
+ return true;
+}
+
+static void eyedropper_colorband_sample_point(bContext *C,
+ EyedropperColorband *eye,
+ const int m_xy[2])
+{
+ if (eye->event_xy_last[0] != m_xy[0] || eye->event_xy_last[1] != m_xy[1]) {
+ float col[4];
+ col[3] = 1.0f; /* TODO: sample alpha */
+ eyedropper_color_sample_fl(C, m_xy, col);
+ if (eye->color_buffer_len + 1 == eye->color_buffer_alloc) {
+ eye->color_buffer_alloc *= 2;
+ eye->color_buffer = MEM_reallocN(eye->color_buffer,
+ sizeof(*eye->color_buffer) * eye->color_buffer_alloc);
+ }
+ copy_v4_v4(eye->color_buffer[eye->color_buffer_len], col);
+ eye->color_buffer_len += 1;
+ copy_v2_v2_int(eye->event_xy_last, m_xy);
+ eye->is_set = true;
+ }
+}
+
+static bool eyedropper_colorband_sample_callback(int mx, int my, void *userdata)
+{
+ struct EyedropperColorband_Context *data = userdata;
+ bContext *C = data->context;
+ EyedropperColorband *eye = data->eye;
+ const int cursor[2] = {mx, my};
+ eyedropper_colorband_sample_point(C, eye, cursor);
+ return true;
+}
+
+static void eyedropper_colorband_sample_segment(bContext *C,
+ EyedropperColorband *eye,
+ const int m_xy[2])
+{
+ /* Since the mouse tends to move rather rapidly we use #BLI_bitmap_draw_2d_line_v2v2i
+ * to interpolate between the reported coordinates */
+ struct EyedropperColorband_Context userdata = {C, eye};
+ BLI_bitmap_draw_2d_line_v2v2i(
+ eye->event_xy_last, m_xy, eyedropper_colorband_sample_callback, &userdata);
+}
+
+static void eyedropper_colorband_exit(bContext *C, wmOperator *op)
+{
+ WM_cursor_modal_restore(CTX_wm_window(C));
+
+ if (op->customdata) {
+ EyedropperColorband *eye = op->customdata;
+ MEM_freeN(eye->color_buffer);
+ MEM_freeN(eye);
+ op->customdata = NULL;
+ }
+}
+
+static void eyedropper_colorband_apply(bContext *C, wmOperator *op)
+{
+ EyedropperColorband *eye = op->customdata;
+ /* Always filter, avoids noise in resulting color-band. */
+ const bool filter_samples = true;
+ BKE_colorband_init_from_table_rgba(
+ eye->color_band, eye->color_buffer, eye->color_buffer_len, filter_samples);
+ eye->is_set = true;
+ if (eye->prop) {
+ RNA_property_update(C, &eye->ptr, eye->prop);
+ }
+}
+
+static void eyedropper_colorband_cancel(bContext *C, wmOperator *op)
+{
+ EyedropperColorband *eye = op->customdata;
+ if (eye->is_set) {
+ *eye->color_band = eye->init_color_band;
+ if (eye->prop) {
+ RNA_property_update(C, &eye->ptr, eye->prop);
+ }
+ }
+ eyedropper_colorband_exit(C, op);
+}
+
+/* main modal status check */
+static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ EyedropperColorband *eye = op->customdata;
+ /* handle modal keymap */
+ if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case EYE_MODAL_CANCEL:
+ eyedropper_colorband_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ case EYE_MODAL_SAMPLE_CONFIRM: {
+ const bool is_undo = eye->is_undo;
+ eyedropper_colorband_sample_segment(C, eye, event->xy);
+ eyedropper_colorband_apply(C, op);
+ eyedropper_colorband_exit(C, op);
+ /* Could support finished & undo-skip. */
+ return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ }
+ case EYE_MODAL_SAMPLE_BEGIN:
+ /* enable accum and make first sample */
+ eye->sample_start = true;
+ eyedropper_colorband_sample_point(C, eye, event->xy);
+ eyedropper_colorband_apply(C, op);
+ copy_v2_v2_int(eye->event_xy_last, event->xy);
+ break;
+ case EYE_MODAL_SAMPLE_RESET:
+ break;
+ }
+ }
+ else if (event->type == MOUSEMOVE) {
+ if (eye->sample_start) {
+ eyedropper_colorband_sample_segment(C, eye, event->xy);
+ eyedropper_colorband_apply(C, op);
+ }
+ }
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int eyedropper_colorband_point_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ EyedropperColorband *eye = op->customdata;
+ /* handle modal keymap */
+ if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case EYE_MODAL_POINT_CANCEL:
+ eyedropper_colorband_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ case EYE_MODAL_POINT_CONFIRM:
+ eyedropper_colorband_apply(C, op);
+ eyedropper_colorband_exit(C, op);
+ return OPERATOR_FINISHED;
+ case EYE_MODAL_POINT_REMOVE_LAST:
+ if (eye->color_buffer_len > 0) {
+ eye->color_buffer_len -= 1;
+ eyedropper_colorband_apply(C, op);
+ }
+ break;
+ case EYE_MODAL_POINT_SAMPLE:
+ eyedropper_colorband_sample_point(C, eye, event->xy);
+ eyedropper_colorband_apply(C, op);
+ if (eye->color_buffer_len == MAXCOLORBAND) {
+ eyedropper_colorband_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ break;
+ case EYE_MODAL_SAMPLE_RESET:
+ *eye->color_band = eye->init_color_band;
+ if (eye->prop) {
+ RNA_property_update(C, &eye->ptr, eye->prop);
+ }
+ eye->color_buffer_len = 0;
+ break;
+ }
+ }
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* Modal Operator init */
+static int eyedropper_colorband_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ /* init */
+ if (eyedropper_colorband_init(C, op)) {
+ wmWindow *win = CTX_wm_window(C);
+ /* Workaround for de-activating the button clearing the cursor, see T76794 */
+ UI_context_active_but_clear(C, win, CTX_wm_region(C));
+ WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER);
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+/* Repeat operator */
+static int eyedropper_colorband_exec(bContext *C, wmOperator *op)
+{
+ /* init */
+ if (eyedropper_colorband_init(C, op)) {
+
+ /* do something */
+
+ /* cleanup */
+ eyedropper_colorband_exit(C, op);
+
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static bool eyedropper_colorband_poll(bContext *C)
+{
+ uiBut *but = UI_context_active_but_get(C);
+ if (but && but->type == UI_BTYPE_COLORBAND) {
+ return true;
+ }
+ const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp);
+ if (ptr.data != NULL) {
+ return true;
+ }
+ return false;
+}
+
+void UI_OT_eyedropper_colorramp(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Eyedropper Colorband";
+ ot->idname = "UI_OT_eyedropper_colorramp";
+ ot->description = "Sample a color band";
+
+ /* api callbacks */
+ ot->invoke = eyedropper_colorband_invoke;
+ ot->modal = eyedropper_colorband_modal;
+ ot->cancel = eyedropper_colorband_cancel;
+ ot->exec = eyedropper_colorband_exec;
+ ot->poll = eyedropper_colorband_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL;
+
+ /* properties */
+}
+
+void UI_OT_eyedropper_colorramp_point(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Eyedropper Colorband (Points)";
+ ot->idname = "UI_OT_eyedropper_colorramp_point";
+ ot->description = "Point-sample a color band";
+
+ /* api callbacks */
+ ot->invoke = eyedropper_colorband_invoke;
+ ot->modal = eyedropper_colorband_point_modal;
+ ot->cancel = eyedropper_colorband_cancel;
+ ot->exec = eyedropper_colorband_exec;
+ ot->poll = eyedropper_colorband_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL;
+
+ /* properties */
+}
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c
new file mode 100644
index 00000000000..1710d0faa4d
--- /dev/null
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c
@@ -0,0 +1,378 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2009 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup edinterface
+ *
+ * Eyedropper (ID data-blocks)
+ *
+ * Defines:
+ * - #UI_OT_eyedropper_id
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "BLI_math_vector.h"
+#include "BLI_string.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_context.h"
+#include "BKE_idtype.h"
+#include "BKE_report.h"
+#include "BKE_screen.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_outliner.h"
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_view3d.h"
+
+#include "eyedropper_intern.h"
+#include "interface_intern.h"
+
+/**
+ * \note #DataDropper is only internal name to avoid confusion with other kinds of eye-droppers.
+ */
+typedef struct DataDropper {
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ short idcode;
+ const char *idcode_name;
+ bool is_undo;
+
+ ID *init_id; /* for resetting on cancel */
+
+ ScrArea *cursor_area; /* Area under the cursor */
+ ARegionType *art;
+ void *draw_handle_pixel;
+ int name_pos[2];
+ char name[200];
+} DataDropper;
+
+static void datadropper_draw_cb(const struct bContext *UNUSED(C),
+ ARegion *UNUSED(region),
+ void *arg)
+{
+ DataDropper *ddr = arg;
+ eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name);
+}
+
+static int datadropper_init(bContext *C, wmOperator *op)
+{
+ int index_dummy;
+ StructRNA *type;
+
+ SpaceType *st;
+ ARegionType *art;
+
+ st = BKE_spacetype_from_id(SPACE_VIEW3D);
+ art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW);
+
+ DataDropper *ddr = MEM_callocN(sizeof(DataDropper), __func__);
+
+ uiBut *but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy);
+
+ if ((ddr->ptr.data == NULL) || (ddr->prop == NULL) ||
+ (RNA_property_editable(&ddr->ptr, ddr->prop) == false) ||
+ (RNA_property_type(ddr->prop) != PROP_POINTER)) {
+ MEM_freeN(ddr);
+ return false;
+ }
+ op->customdata = ddr;
+
+ ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO);
+
+ ddr->cursor_area = CTX_wm_area(C);
+ ddr->art = art;
+ ddr->draw_handle_pixel = ED_region_draw_cb_activate(
+ art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
+
+ type = RNA_property_pointer_type(&ddr->ptr, ddr->prop);
+ ddr->idcode = RNA_type_to_ID_code(type);
+ BLI_assert(ddr->idcode != 0);
+ /* Note we can translate here (instead of on draw time),
+ * because this struct has very short lifetime. */
+ ddr->idcode_name = TIP_(BKE_idtype_idcode_to_name(ddr->idcode));
+
+ const PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
+ ddr->init_id = ptr.owner_id;
+
+ return true;
+}
+
+static void datadropper_exit(bContext *C, wmOperator *op)
+{
+ wmWindow *win = CTX_wm_window(C);
+
+ WM_cursor_modal_restore(win);
+
+ if (op->customdata) {
+ DataDropper *ddr = (DataDropper *)op->customdata;
+
+ if (ddr->art) {
+ ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
+ }
+
+ MEM_freeN(op->customdata);
+
+ op->customdata = NULL;
+ }
+
+ WM_event_add_mousemove(win);
+}
+
+/* *** datadropper id helper functions *** */
+/**
+ * \brief get the ID from the 3D view or outliner.
+ */
+static void datadropper_id_sample_pt(
+ bContext *C, wmWindow *win, ScrArea *area, DataDropper *ddr, const int m_xy[2], ID **r_id)
+{
+ wmWindow *win_prev = CTX_wm_window(C);
+ ScrArea *area_prev = CTX_wm_area(C);
+ ARegion *region_prev = CTX_wm_region(C);
+
+ ddr->name[0] = '\0';
+
+ if (area) {
+ if (ELEM(area->spacetype, SPACE_VIEW3D, SPACE_OUTLINER)) {
+ ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy);
+ if (region) {
+ const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin};
+ Base *base;
+
+ CTX_wm_window_set(C, win);
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+
+ /* Unfortunately it's necessary to always draw else we leave stale text. */
+ ED_region_tag_redraw(region);
+
+ if (area->spacetype == SPACE_VIEW3D) {
+ base = ED_view3d_give_base_under_cursor(C, mval);
+ }
+ else {
+ base = ED_outliner_give_base_under_cursor(C, mval);
+ }
+
+ if (base) {
+ Object *ob = base->object;
+ ID *id = NULL;
+ if (ddr->idcode == ID_OB) {
+ id = (ID *)ob;
+ }
+ else if (ob->data) {
+ if (GS(((ID *)ob->data)->name) == ddr->idcode) {
+ id = (ID *)ob->data;
+ }
+ else {
+ BLI_snprintf(
+ ddr->name, sizeof(ddr->name), "Incompatible, expected a %s", ddr->idcode_name);
+ }
+ }
+
+ PointerRNA idptr;
+ RNA_id_pointer_create(id, &idptr);
+
+ if (id && RNA_property_pointer_poll(&ddr->ptr, ddr->prop, &idptr)) {
+ BLI_snprintf(ddr->name, sizeof(ddr->name), "%s: %s", ddr->idcode_name, id->name + 2);
+ *r_id = id;
+ }
+
+ copy_v2_v2_int(ddr->name_pos, mval);
+ }
+ }
+ }
+ }
+
+ CTX_wm_window_set(C, win_prev);
+ CTX_wm_area_set(C, area_prev);
+ CTX_wm_region_set(C, region_prev);
+}
+
+/* sets the ID, returns success */
+static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id)
+{
+ PointerRNA ptr_value;
+
+ RNA_id_pointer_create(id, &ptr_value);
+
+ RNA_property_pointer_set(&ddr->ptr, ddr->prop, ptr_value, NULL);
+
+ RNA_property_update(C, &ddr->ptr, ddr->prop);
+
+ ptr_value = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
+
+ return (ptr_value.owner_id == id);
+}
+
+/* single point sample & set */
+static bool datadropper_id_sample(bContext *C, DataDropper *ddr, const int m_xy[2])
+{
+ ID *id = NULL;
+
+ int mval[2];
+ wmWindow *win;
+ ScrArea *area;
+ datadropper_win_area_find(C, m_xy, mval, &win, &area);
+
+ datadropper_id_sample_pt(C, win, area, ddr, mval, &id);
+ 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);
+}
+
+/* To switch the draw callback when region under mouse event changes */
+static void datadropper_set_draw_callback_region(ScrArea *area, DataDropper *ddr)
+{
+ if (area) {
+ /* If spacetype changed */
+ if (area->spacetype != ddr->cursor_area->spacetype) {
+ /* Remove old callback */
+ ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
+
+ /* Redraw old area */
+ ARegion *region = BKE_area_find_region_type(ddr->cursor_area, RGN_TYPE_WINDOW);
+ ED_region_tag_redraw(region);
+
+ /* Set draw callback in new region */
+ ARegionType *art = BKE_regiontype_from_id(area->type, RGN_TYPE_WINDOW);
+
+ ddr->cursor_area = area;
+ ddr->art = art;
+ ddr->draw_handle_pixel = ED_region_draw_cb_activate(
+ art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
+ }
+ }
+}
+
+/* main modal status check */
+static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ DataDropper *ddr = (DataDropper *)op->customdata;
+
+ /* 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: {
+ const bool is_undo = ddr->is_undo;
+ const bool success = datadropper_id_sample(C, ddr, event->xy);
+ datadropper_exit(C, op);
+ if (success) {
+ /* Could support finished & undo-skip. */
+ return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ }
+ BKE_report(op->reports, RPT_WARNING, "Failed to set value");
+ return OPERATOR_CANCELLED;
+ }
+ }
+ }
+ else if (event->type == MOUSEMOVE) {
+ ID *id = NULL;
+
+ int mval[2];
+ wmWindow *win;
+ ScrArea *area;
+ datadropper_win_area_find(C, event->xy, mval, &win, &area);
+
+ /* Set the region for eyedropper cursor text drawing */
+ datadropper_set_draw_callback_region(area, ddr);
+
+ datadropper_id_sample_pt(C, win, area, ddr, mval, &id);
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* Modal Operator init */
+static int datadropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ /* init */
+ if (datadropper_init(C, op)) {
+ wmWindow *win = CTX_wm_window(C);
+ /* Workaround for de-activating the button clearing the cursor, see T76794 */
+ UI_context_active_but_clear(C, win, CTX_wm_region(C));
+ WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER);
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+/* Repeat operator */
+static int datadropper_exec(bContext *C, wmOperator *op)
+{
+ /* init */
+ if (datadropper_init(C, op)) {
+ /* cleanup */
+ datadropper_exit(C, op);
+
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static bool datadropper_poll(bContext *C)
+{
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ int index_dummy;
+ uiBut *but;
+
+ /* data dropper only supports object data */
+ if ((CTX_wm_window(C) != NULL) &&
+ (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) &&
+ (but->type == UI_BTYPE_SEARCH_MENU) && (but->flag & UI_BUT_VALUE_CLEAR)) {
+ if (prop && RNA_property_type(prop) == PROP_POINTER) {
+ StructRNA *type = RNA_property_pointer_type(&ptr, prop);
+ const short idcode = RNA_type_to_ID_code(type);
+ if ((idcode == ID_OB) || OB_DATA_SUPPORT_ID(idcode)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void UI_OT_eyedropper_id(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Eyedropper Data-Block";
+ ot->idname = "UI_OT_eyedropper_id";
+ ot->description = "Sample a data-block from the 3D View to store in a property";
+
+ /* api callbacks */
+ ot->invoke = datadropper_invoke;
+ ot->modal = datadropper_modal;
+ ot->cancel = datadropper_cancel;
+ ot->exec = datadropper_exec;
+ ot->poll = datadropper_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL;
+
+ /* properties */
+}
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_depth.c b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c
new file mode 100644
index 00000000000..3fb5a74944b
--- /dev/null
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c
@@ -0,0 +1,381 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2009 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup edinterface
+ *
+ * This file defines an eyedropper for picking 3D depth value (primary use is depth-of-field).
+ *
+ * Defines:
+ * - #UI_OT_eyedropper_depth
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BLI_math_vector.h"
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_screen.h"
+#include "BKE_unit.h"
+
+#include "RNA_access.h"
+#include "RNA_prototypes.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_view3d.h"
+
+#include "eyedropper_intern.h"
+#include "interface_intern.h"
+
+/**
+ * \note #DepthDropper is only internal name to avoid confusion with other kinds of eye-droppers.
+ */
+typedef struct DepthDropper {
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ bool is_undo;
+
+ bool is_set;
+ float init_depth; /* For resetting on cancel. */
+
+ bool accum_start; /* Has mouse been pressed. */
+ float accum_depth;
+ int accum_tot;
+
+ ARegionType *art;
+ void *draw_handle_pixel;
+ int name_pos[2];
+ char name[200];
+} DepthDropper;
+
+static void depthdropper_draw_cb(const struct bContext *UNUSED(C),
+ ARegion *UNUSED(region),
+ void *arg)
+{
+ DepthDropper *ddr = arg;
+ eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name);
+}
+
+static int depthdropper_init(bContext *C, wmOperator *op)
+{
+ int index_dummy;
+
+ SpaceType *st;
+ ARegionType *art;
+
+ st = BKE_spacetype_from_id(SPACE_VIEW3D);
+ art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW);
+
+ DepthDropper *ddr = MEM_callocN(sizeof(DepthDropper), __func__);
+
+ uiBut *but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy);
+
+ /* fallback to the active camera's dof */
+ if (ddr->prop == NULL) {
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ if (rv3d && rv3d->persp == RV3D_CAMOB) {
+ View3D *v3d = CTX_wm_view3d(C);
+ if (v3d->camera && v3d->camera->data &&
+ BKE_id_is_editable(CTX_data_main(C), v3d->camera->data)) {
+ Camera *camera = (Camera *)v3d->camera->data;
+ RNA_pointer_create(&camera->id, &RNA_CameraDOFSettings, &camera->dof, &ddr->ptr);
+ ddr->prop = RNA_struct_find_property(&ddr->ptr, "focus_distance");
+ ddr->is_undo = true;
+ }
+ }
+ }
+ else {
+ ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO);
+ }
+
+ if ((ddr->ptr.data == NULL) || (ddr->prop == NULL) ||
+ (RNA_property_editable(&ddr->ptr, ddr->prop) == false) ||
+ (RNA_property_type(ddr->prop) != PROP_FLOAT)) {
+ MEM_freeN(ddr);
+ return false;
+ }
+ op->customdata = ddr;
+
+ 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;
+}
+
+static void depthdropper_exit(bContext *C, wmOperator *op)
+{
+ WM_cursor_modal_restore(CTX_wm_window(C));
+
+ if (op->customdata) {
+ DepthDropper *ddr = (DepthDropper *)op->customdata;
+
+ if (ddr->art) {
+ ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
+ }
+
+ MEM_freeN(op->customdata);
+
+ op->customdata = NULL;
+ }
+}
+
+/* *** depthdropper id helper functions *** */
+/**
+ * \brief get the ID from the screen.
+ */
+static void depthdropper_depth_sample_pt(bContext *C,
+ DepthDropper *ddr,
+ const int m_xy[2],
+ float *r_depth)
+{
+ /* we could use some clever */
+ bScreen *screen = CTX_wm_screen(C);
+ ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, m_xy);
+ Scene *scene = CTX_data_scene(C);
+
+ ScrArea *area_prev = CTX_wm_area(C);
+ ARegion *region_prev = CTX_wm_region(C);
+
+ ddr->name[0] = '\0';
+
+ if (area) {
+ if (area->spacetype == SPACE_VIEW3D) {
+ ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy);
+ if (region) {
+ struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ View3D *v3d = area->spacedata.first;
+ RegionView3D *rv3d = region->regiondata;
+ /* weak, we could pass in some reference point */
+ const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3];
+ const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin};
+ copy_v2_v2_int(ddr->name_pos, mval);
+
+ float co[3];
+
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+
+ /* Unfortunately it's necessary to always draw otherwise we leave stale text. */
+ ED_region_tag_redraw(region);
+
+ view3d_operator_needs_opengl(C);
+
+ if (ED_view3d_autodist(depsgraph, region, v3d, mval, co, true, NULL)) {
+ const float mval_center_fl[2] = {(float)region->winx / 2, (float)region->winy / 2};
+ float co_align[3];
+
+ /* quick way to get view-center aligned point */
+ ED_view3d_win_to_3d(v3d, region, co, mval_center_fl, co_align);
+
+ *r_depth = len_v3v3(view_co, co_align);
+
+ BKE_unit_value_as_string(ddr->name,
+ sizeof(ddr->name),
+ (double)*r_depth,
+ 4,
+ B_UNIT_LENGTH,
+ &scene->unit,
+ false);
+ }
+ else {
+ BLI_strncpy(ddr->name, "Nothing under cursor", sizeof(ddr->name));
+ }
+ }
+ }
+ }
+
+ CTX_wm_area_set(C, area_prev);
+ CTX_wm_region_set(C, region_prev);
+}
+
+/* sets the sample depth RGB, maintaining A */
+static void depthdropper_depth_set(bContext *C, DepthDropper *ddr, const float depth)
+{
+ RNA_property_float_set(&ddr->ptr, ddr->prop, depth);
+ ddr->is_set = true;
+ RNA_property_update(C, &ddr->ptr, ddr->prop);
+}
+
+/* set sample from accumulated values */
+static void depthdropper_depth_set_accum(bContext *C, DepthDropper *ddr)
+{
+ float depth = ddr->accum_depth;
+ if (ddr->accum_tot) {
+ depth /= (float)ddr->accum_tot;
+ }
+ depthdropper_depth_set(C, ddr, depth);
+}
+
+/* single point sample & set */
+static void depthdropper_depth_sample(bContext *C, DepthDropper *ddr, const int m_xy[2])
+{
+ float depth = -1.0f;
+ if (depth != -1.0f) {
+ depthdropper_depth_sample_pt(C, ddr, m_xy, &depth);
+ depthdropper_depth_set(C, ddr, depth);
+ }
+}
+
+static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, const int m_xy[2])
+{
+ float depth = -1.0f;
+ depthdropper_depth_sample_pt(C, ddr, m_xy, &depth);
+ if (depth != -1.0f) {
+ ddr->accum_depth += depth;
+ ddr->accum_tot++;
+ }
+}
+
+static void depthdropper_cancel(bContext *C, wmOperator *op)
+{
+ DepthDropper *ddr = op->customdata;
+ if (ddr->is_set) {
+ 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;
+
+ /* 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: {
+ const bool is_undo = ddr->is_undo;
+ if (ddr->accum_tot == 0) {
+ depthdropper_depth_sample(C, ddr, event->xy);
+ }
+ else {
+ depthdropper_depth_set_accum(C, ddr);
+ }
+ depthdropper_exit(C, op);
+ /* Could support finished & undo-skip. */
+ return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ }
+ case EYE_MODAL_SAMPLE_BEGIN:
+ /* enable accum and make first sample */
+ ddr->accum_start = true;
+ depthdropper_depth_sample_accum(C, ddr, event->xy);
+ break;
+ case EYE_MODAL_SAMPLE_RESET:
+ ddr->accum_tot = 0;
+ ddr->accum_depth = 0.0f;
+ depthdropper_depth_sample_accum(C, ddr, event->xy);
+ depthdropper_depth_set_accum(C, ddr);
+ break;
+ }
+ }
+ else if (event->type == MOUSEMOVE) {
+ if (ddr->accum_start) {
+ /* button is pressed so keep sampling */
+ depthdropper_depth_sample_accum(C, ddr, event->xy);
+ depthdropper_depth_set_accum(C, ddr);
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* Modal Operator init */
+static int depthdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ /* init */
+ if (depthdropper_init(C, op)) {
+ wmWindow *win = CTX_wm_window(C);
+ /* Workaround for de-activating the button clearing the cursor, see T76794 */
+ UI_context_active_but_clear(C, win, CTX_wm_region(C));
+ WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER);
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+/* Repeat operator */
+static int depthdropper_exec(bContext *C, wmOperator *op)
+{
+ /* init */
+ if (depthdropper_init(C, op)) {
+ /* cleanup */
+ depthdropper_exit(C, op);
+
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static bool depthdropper_poll(bContext *C)
+{
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ int index_dummy;
+ uiBut *but;
+
+ /* check if there's an active button taking depth value */
+ if ((CTX_wm_window(C) != NULL) &&
+ (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) &&
+ (but->type == UI_BTYPE_NUM) && (prop != NULL)) {
+ if ((RNA_property_type(prop) == PROP_FLOAT) &&
+ (RNA_property_subtype(prop) & PROP_UNIT_LENGTH) &&
+ (RNA_property_array_check(prop) == false)) {
+ return true;
+ }
+ }
+ else {
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ if (rv3d && rv3d->persp == RV3D_CAMOB) {
+ View3D *v3d = CTX_wm_view3d(C);
+ if (v3d->camera && v3d->camera->data &&
+ BKE_id_is_editable(CTX_data_main(C), v3d->camera->data)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void UI_OT_eyedropper_depth(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Eyedropper Depth";
+ ot->idname = "UI_OT_eyedropper_depth";
+ ot->description = "Sample depth from the 3D view";
+
+ /* api callbacks */
+ ot->invoke = depthdropper_invoke;
+ ot->modal = depthdropper_modal;
+ ot->cancel = depthdropper_cancel;
+ ot->exec = depthdropper_exec;
+ ot->poll = depthdropper_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL;
+
+ /* properties */
+}
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_driver.c b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c
new file mode 100644
index 00000000000..14c00c21a5c
--- /dev/null
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2009 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup edinterface
+ *
+ * Eyedropper (Animation Driver Targets).
+ *
+ * Defines:
+ * - #UI_OT_eyedropper_driver
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_animsys.h"
+#include "BKE_context.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_keyframing.h"
+
+#include "eyedropper_intern.h"
+#include "interface_intern.h"
+
+typedef struct DriverDropper {
+ /* Destination property (i.e. where we'll add a driver) */
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ int index;
+ bool is_undo;
+
+ /* TODO: new target? */
+} DriverDropper;
+
+static bool driverdropper_init(bContext *C, wmOperator *op)
+{
+ DriverDropper *ddr = MEM_callocN(sizeof(DriverDropper), __func__);
+
+ uiBut *but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &ddr->index);
+
+ 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)) {
+ MEM_freeN(ddr);
+ return false;
+ }
+ op->customdata = ddr;
+
+ ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO);
+
+ return true;
+}
+
+static void driverdropper_exit(bContext *C, wmOperator *op)
+{
+ WM_cursor_modal_restore(CTX_wm_window(C));
+
+ MEM_SAFE_FREE(op->customdata);
+}
+
+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);
+
+ const short mapping_type = RNA_enum_get(op->ptr, "mapping_type");
+ const short flag = 0;
+
+ /* we can only add a driver if we know what RNA property it corresponds to */
+ if (but == NULL) {
+ return;
+ }
+ /* Get paths for src... */
+ PointerRNA *target_ptr = &but->rnapoin;
+ PropertyRNA *target_prop = but->rnaprop;
+ const int target_index = but->rnaindex;
+
+ char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop);
+
+ /* ... and destination */
+ char *dst_path = RNA_path_from_ID_to_property(&ddr->ptr, ddr->prop);
+
+ /* Now create driver(s) */
+ if (target_path && dst_path) {
+ int success = ANIM_add_driver_with_target(op->reports,
+ ddr->ptr.owner_id,
+ dst_path,
+ ddr->index,
+ target_ptr->owner_id,
+ target_path,
+ target_index,
+ flag,
+ DRIVER_TYPE_PYTHON,
+ mapping_type);
+
+ if (success) {
+ /* send updates */
+ UI_context_update_anim_flag(C);
+ DEG_relations_tag_update(CTX_data_main(C));
+ DEG_id_tag_update(ddr->ptr.owner_id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ 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)
+{
+ DriverDropper *ddr = op->customdata;
+
+ /* 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: {
+ const bool is_undo = ddr->is_undo;
+ driverdropper_sample(C, op, event);
+ driverdropper_exit(C, op);
+ /* Could support finished & undo-skip. */
+ return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ }
+ }
+ }
+
+ 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)) {
+ wmWindow *win = CTX_wm_window(C);
+ /* Workaround for de-activating the button clearing the cursor, see T76794 */
+ UI_context_active_but_clear(C, win, CTX_wm_region(C));
+ WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER);
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ 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;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static bool driverdropper_poll(bContext *C)
+{
+ if (!CTX_wm_window(C)) {
+ return false;
+ }
+ return true;
+}
+
+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_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL;
+
+ /* properties */
+ RNA_def_enum(ot->srna,
+ "mapping_type",
+ prop_driver_create_mapping_types,
+ 0,
+ "Mapping Type",
+ "Method used to match target and driven properties");
+}
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c
new file mode 100644
index 00000000000..c3879fe8bbd
--- /dev/null
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c
@@ -0,0 +1,373 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2009 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup edinterface
+ *
+ * Eyedropper (RGB Color)
+ *
+ * Defines:
+ * - #UI_OT_eyedropper_gpencil_color
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_context.h"
+#include "BKE_gpencil.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_paint.h"
+#include "BKE_report.h"
+
+#include "UI_interface.h"
+
+#include "IMB_colormanagement.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "ED_gpencil.h"
+#include "ED_screen.h"
+#include "ED_undo.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+
+#include "eyedropper_intern.h"
+#include "interface_intern.h"
+
+typedef struct EyedropperGPencil {
+ struct ColorManagedDisplay *display;
+ /** color under cursor RGB */
+ float color[3];
+ /** Mode */
+ int mode;
+} EyedropperGPencil;
+
+/* Helper: Draw status message while the user is running the operator */
+static void eyedropper_gpencil_status_indicators(bContext *C)
+{
+ char msg_str[UI_MAX_DRAW_STR];
+ BLI_strncpy(
+ msg_str, TIP_("LMB: Stroke - Shift: Fill - Shift+Ctrl: Stroke + Fill"), UI_MAX_DRAW_STR);
+
+ ED_workspace_status_text(C, msg_str);
+}
+
+/* Initialize. */
+static bool eyedropper_gpencil_init(bContext *C, wmOperator *op)
+{
+ EyedropperGPencil *eye = MEM_callocN(sizeof(EyedropperGPencil), __func__);
+
+ op->customdata = eye;
+ Scene *scene = CTX_data_scene(C);
+
+ const char *display_device;
+ display_device = scene->display_settings.display_device;
+ eye->display = IMB_colormanagement_display_get_named(display_device);
+
+ eye->mode = RNA_enum_get(op->ptr, "mode");
+ return true;
+}
+
+/* Exit and free memory. */
+static void eyedropper_gpencil_exit(bContext *C, wmOperator *op)
+{
+ /* Clear status message area. */
+ ED_workspace_status_text(C, NULL);
+
+ MEM_SAFE_FREE(op->customdata);
+}
+
+static void eyedropper_add_material(bContext *C,
+ const float col_conv[4],
+ const bool only_stroke,
+ const bool only_fill,
+ const bool both)
+{
+ Main *bmain = CTX_data_main(C);
+ Object *ob = CTX_data_active_object(C);
+ Material *ma = NULL;
+
+ bool found = false;
+
+ /* Look for a similar material in grease pencil slots. */
+ short *totcol = BKE_object_material_len_p(ob);
+ for (short i = 0; i < *totcol; i++) {
+ ma = BKE_object_material_get(ob, i + 1);
+ if (ma == NULL) {
+ continue;
+ }
+
+ MaterialGPencilStyle *gp_style = ma->gp_style;
+ if (gp_style != NULL) {
+ /* Check stroke color. */
+ bool found_stroke = compare_v3v3(gp_style->stroke_rgba, col_conv, 0.01f) &&
+ (gp_style->flag & GP_MATERIAL_STROKE_SHOW);
+ /* Check fill color. */
+ bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) &&
+ (gp_style->flag & GP_MATERIAL_FILL_SHOW);
+
+ if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) {
+ found = true;
+ }
+ else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0)) {
+ found = true;
+ }
+ else if ((both) && (found_stroke) && (found_fill)) {
+ found = true;
+ }
+
+ /* Found existing material. */
+ if (found) {
+ ob->actcol = i + 1;
+ WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, NULL);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ return;
+ }
+ }
+ }
+
+ /* If material was not found add a new material with stroke and/or fill color
+ * depending of the secondary key (LMB: Stroke, Shift: Fill, Shift+Ctrl: Stroke/Fill)
+ */
+ int idx;
+ Material *ma_new = BKE_gpencil_object_material_new(bmain, ob, "Material", &idx);
+ WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, &ob->id);
+ WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, NULL);
+ DEG_relations_tag_update(bmain);
+
+ BLI_assert(ma_new != NULL);
+
+ MaterialGPencilStyle *gp_style_new = ma_new->gp_style;
+ BLI_assert(gp_style_new != NULL);
+
+ /* Only create Stroke (default option). */
+ if (only_stroke) {
+ /* Stroke color. */
+ gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW;
+ gp_style_new->flag &= ~GP_MATERIAL_FILL_SHOW;
+ copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
+ zero_v4(gp_style_new->fill_rgba);
+ }
+ /* Fill Only. */
+ else if (only_fill) {
+ /* Fill color. */
+ gp_style_new->flag &= ~GP_MATERIAL_STROKE_SHOW;
+ gp_style_new->flag |= GP_MATERIAL_FILL_SHOW;
+ zero_v4(gp_style_new->stroke_rgba);
+ copy_v3_v3(gp_style_new->fill_rgba, col_conv);
+ }
+ /* Stroke and Fill. */
+ else if (both) {
+ gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW | GP_MATERIAL_FILL_SHOW;
+ copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
+ copy_v3_v3(gp_style_new->fill_rgba, col_conv);
+ }
+ /* Push undo for new created material. */
+ ED_undo_push(C, "Add Grease Pencil Material");
+}
+
+/* Create a new palette color and palette if needed. */
+static void eyedropper_add_palette_color(bContext *C, const float col_conv[4])
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = scene->toolsettings;
+ GpPaint *gp_paint = ts->gp_paint;
+ GpVertexPaint *gp_vertexpaint = ts->gp_vertexpaint;
+ Paint *paint = &gp_paint->paint;
+ Paint *vertexpaint = &gp_vertexpaint->paint;
+
+ /* Check for Palette in Draw and Vertex Paint Mode. */
+ if (paint->palette == NULL) {
+ Palette *palette = BKE_palette_add(bmain, "Grease Pencil");
+ id_us_min(&palette->id);
+
+ BKE_paint_palette_set(paint, palette);
+
+ if (vertexpaint->palette == NULL) {
+ BKE_paint_palette_set(vertexpaint, palette);
+ }
+ }
+ /* Check if the color exist already. */
+ Palette *palette = paint->palette;
+ LISTBASE_FOREACH (PaletteColor *, palcolor, &palette->colors) {
+ if (compare_v3v3(palcolor->rgb, col_conv, 0.01f)) {
+ return;
+ }
+ }
+
+ /* Create Colors. */
+ PaletteColor *palcol = BKE_palette_color_add(palette);
+ if (palcol) {
+ copy_v3_v3(palcol->rgb, col_conv);
+ }
+}
+
+/* Set the material or the palette color. */
+static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye)
+{
+
+ const bool only_stroke = (event->modifier & (KM_CTRL | KM_SHIFT)) == 0;
+ const bool only_fill = ((event->modifier & KM_CTRL) == 0 && (event->modifier & KM_SHIFT));
+ const bool both = ((event->modifier & KM_CTRL) && (event->modifier & KM_SHIFT));
+
+ float col_conv[4];
+
+ /* Convert from linear rgb space to display space because grease pencil colors are in display
+ * space, and this conversion is needed to undo the conversion to linear performed by
+ * eyedropper_color_sample_fl. */
+ if (eye->display) {
+ copy_v3_v3(col_conv, eye->color);
+ IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display);
+ }
+ else {
+ copy_v3_v3(col_conv, eye->color);
+ }
+
+ /* Add material or Palette color. */
+ if (eye->mode == 0) {
+ eyedropper_add_material(C, col_conv, only_stroke, only_fill, both);
+ }
+ else {
+ eyedropper_add_palette_color(C, col_conv);
+ }
+}
+
+/* Sample the color below cursor. */
+static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, const int m_xy[2])
+{
+ eyedropper_color_sample_fl(C, m_xy, eye->color);
+}
+
+/* Cancel operator. */
+static void eyedropper_gpencil_cancel(bContext *C, wmOperator *op)
+{
+ eyedropper_gpencil_exit(C, op);
+}
+
+/* Main modal status check. */
+static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ EyedropperGPencil *eye = (EyedropperGPencil *)op->customdata;
+ /* Handle modal keymap */
+ switch (event->type) {
+ case EVT_MODAL_MAP: {
+ switch (event->val) {
+ case EYE_MODAL_SAMPLE_BEGIN: {
+ return OPERATOR_RUNNING_MODAL;
+ }
+ case EYE_MODAL_CANCEL: {
+ eyedropper_gpencil_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ case EYE_MODAL_SAMPLE_CONFIRM: {
+ eyedropper_gpencil_color_sample(C, eye, event->xy);
+
+ /* Create material. */
+ eyedropper_gpencil_color_set(C, event, eye);
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ eyedropper_gpencil_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ default: {
+ break;
+ }
+ }
+ break;
+ }
+ case MOUSEMOVE:
+ case INBETWEEN_MOUSEMOVE: {
+ eyedropper_gpencil_color_sample(C, eye, event->xy);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* Modal Operator init */
+static int eyedropper_gpencil_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ /* Init. */
+ if (eyedropper_gpencil_init(C, op)) {
+ /* Add modal temp handler. */
+ WM_event_add_modal_handler(C, op);
+ /* Status message. */
+ eyedropper_gpencil_status_indicators(C);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_PASS_THROUGH;
+}
+
+/* Repeat operator */
+static int eyedropper_gpencil_exec(bContext *C, wmOperator *op)
+{
+ /* init */
+ if (eyedropper_gpencil_init(C, op)) {
+
+ /* cleanup */
+ eyedropper_gpencil_exit(C, op);
+
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_PASS_THROUGH;
+}
+
+static bool eyedropper_gpencil_poll(bContext *C)
+{
+ /* Only valid if the current active object is grease pencil. */
+ Object *obact = CTX_data_active_object(C);
+ if ((obact == NULL) || (obact->type != OB_GPENCIL)) {
+ return false;
+ }
+
+ /* Test we have a window below. */
+ return (CTX_wm_window(C) != NULL);
+}
+
+void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot)
+{
+ static const EnumPropertyItem items_mode[] = {
+ {0, "MATERIAL", 0, "Material", ""},
+ {1, "PALETTE", 0, "Palette", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Grease Pencil Eyedropper";
+ ot->idname = "UI_OT_eyedropper_gpencil_color";
+ ot->description = "Sample a color from the Blender Window and create Grease Pencil material";
+
+ /* api callbacks */
+ ot->invoke = eyedropper_gpencil_invoke;
+ ot->modal = eyedropper_gpencil_modal;
+ ot->cancel = eyedropper_gpencil_cancel;
+ ot->exec = eyedropper_gpencil_exec;
+ ot->poll = eyedropper_gpencil_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "mode", items_mode, 0, "Mode", "");
+}
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_intern.h b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h
new file mode 100644
index 00000000000..946f2145d1d
--- /dev/null
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup edinterface
+ *
+ * Share between `interface/eyedropper/` files.
+ */
+
+#pragma once
+
+/* interface_eyedropper.c */
+
+void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name);
+void eyedropper_draw_cursor_text_region(const int xy[2], const char *name);
+/**
+ * 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
+ */
+uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event);
+void datadropper_win_area_find(const struct bContext *C,
+ const int mval[2],
+ int r_mval[2],
+ struct wmWindow **r_win,
+ struct ScrArea **r_area);
+
+/* interface_eyedropper_color.c (expose for color-band picker) */
+
+/**
+ * \brief get the color from the screen.
+ *
+ * Special check for image or nodes where we MAY have HDR pixels which don't display.
+ *
+ * \note Exposed by 'eyedropper_intern.h' for use with color band picking.
+ */
+void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]);
+
+/* Used for most eye-dropper operators. */
+enum {
+ EYE_MODAL_CANCEL = 1,
+ EYE_MODAL_SAMPLE_CONFIRM,
+ EYE_MODAL_SAMPLE_BEGIN,
+ EYE_MODAL_SAMPLE_RESET,
+};
+
+/* Color-band point sample. */
+enum {
+ EYE_MODAL_POINT_CANCEL = 1,
+ EYE_MODAL_POINT_SAMPLE,
+ EYE_MODAL_POINT_CONFIRM,
+ EYE_MODAL_POINT_RESET,
+ EYE_MODAL_POINT_REMOVE_LAST,
+};
diff --git a/source/blender/editors/interface/eyedroppers/interface_eyedropper.c b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c
new file mode 100644
index 00000000000..c6fb8f0ab68
--- /dev/null
+++ b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c
@@ -0,0 +1,161 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2009 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup edinterface
+ */
+
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "BLI_math_color.h"
+#include "BLI_math_vector.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "interface_intern.h"
+
+#include "eyedropper_intern.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/* Keymap
+ */
+/** \name Modal Keymap
+ * \{ */
+
+wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
+{
+ static const 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_find(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_ensure(keyconf, "Eyedropper Modal Map", modal_items);
+
+ /* assign to operators */
+ WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorramp");
+ 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");
+ WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_gpencil_color");
+
+ return keymap;
+}
+
+wmKeyMap *eyedropper_colorband_modal_keymap(wmKeyConfig *keyconf)
+{
+ static const EnumPropertyItem modal_items_point[] = {
+ {EYE_MODAL_POINT_CANCEL, "CANCEL", 0, "Cancel", ""},
+ {EYE_MODAL_POINT_SAMPLE, "SAMPLE_SAMPLE", 0, "Sample a Point", ""},
+ {EYE_MODAL_POINT_CONFIRM, "SAMPLE_CONFIRM", 0, "Confirm Sampling", ""},
+ {EYE_MODAL_POINT_RESET, "SAMPLE_RESET", 0, "Reset Sampling", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Eyedropper ColorRamp PointSampling Map");
+ if (keymap && keymap->modal_items) {
+ return keymap;
+ }
+
+ keymap = WM_modalkeymap_ensure(
+ keyconf, "Eyedropper ColorRamp PointSampling Map", modal_items_point);
+
+ /* assign to operators */
+ WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorramp_point");
+
+ return keymap;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/* Utility Functions
+ */
+
+/** \name Generic Shared Functions
+ * \{ */
+
+static void eyedropper_draw_cursor_text_ex(const int xy[2], const char *name)
+{
+ const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
+
+ /* Use the theme settings from tooltips. */
+ const bTheme *btheme = UI_GetTheme();
+ const uiWidgetColors *wcol = &btheme->tui.wcol_tooltip;
+
+ float col_fg[4], col_bg[4];
+ rgba_uchar_to_float(col_fg, wcol->text);
+ rgba_uchar_to_float(col_bg, wcol->inner);
+
+ UI_fontstyle_draw_simple_backdrop(fstyle, xy[0], xy[1] + U.widget_unit, name, col_fg, col_bg);
+}
+
+void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name)
+{
+ if (name[0] == '\0') {
+ return;
+ }
+
+ eyedropper_draw_cursor_text_ex(window->eventstate->xy, name);
+}
+
+void eyedropper_draw_cursor_text_region(const int xy[2], const char *name)
+{
+ if (name[0] == '\0') {
+ return;
+ }
+
+ eyedropper_draw_cursor_text_ex(xy, name);
+}
+
+uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event)
+{
+ bScreen *screen = CTX_wm_screen(C);
+ ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy);
+ const ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, event->xy);
+
+ uiBut *but = ui_but_find_mouse_over(region, event);
+
+ if (ELEM(NULL, but, but->rnapoin.data, but->rnaprop)) {
+ return NULL;
+ }
+ return but;
+}
+
+void datadropper_win_area_find(
+ const bContext *C, const int mval[2], int r_mval[2], wmWindow **r_win, ScrArea **r_area)
+{
+ bScreen *screen = CTX_wm_screen(C);
+
+ *r_win = CTX_wm_window(C);
+ *r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mval);
+ if (*r_area == NULL) {
+ *r_win = WM_window_find_under_cursor(*r_win, mval, r_mval);
+ if (*r_win) {
+ screen = WM_window_get_active_screen(*r_win);
+ *r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, r_mval);
+ }
+ }
+ else if (mval != r_mval) {
+ copy_v2_v2_int(r_mval, mval);
+ }
+}
+
+/** \} */