diff --git a/source/blender/editors/armature/armature_intern.hh b/source/blender/editors/armature/armature_intern.hh
index d19f1e8ea5f..f80d80d629a 100644
--- a/source/blender/editors/armature/armature_intern.hh
+++ b/source/blender/editors/armature/armature_intern.hh
@@ -266,10 +266,6 @@ void armature_tag_unselect(bArmature *arm);
/** \name Selection Picking
* \{ */
-EditBone *ED_armature_pick_ebone(bContext *C, const int xy[2], bool findunsel, Base **r_base);
-bPoseChannel *ED_armature_pick_pchan(bContext *C, const int xy[2], bool findunsel, Base **r_base);
-Bone *ED_armature_pick_bone(bContext *C, const int xy[2], bool findunsel, Base **r_base);
-
EditBone *ED_armature_pick_ebone_from_selectbuffer(blender::Span bases,
const GPUSelectResult *hit_results,
int hits,
diff --git a/source/blender/editors/include/ED_armature.hh b/source/blender/editors/include/ED_armature.hh
index d1790e686c5..dae012b9f71 100644
--- a/source/blender/editors/include/ED_armature.hh
+++ b/source/blender/editors/include/ED_armature.hh
@@ -340,3 +340,7 @@ void ED_mesh_deform_bind_callback(Object *object,
float *vertexcos,
int verts_num,
float cagemat[4][4]);
+
+EditBone *ED_armature_pick_ebone(bContext *C, const int xy[2], bool findunsel, Base **r_base);
+bPoseChannel *ED_armature_pick_pchan(bContext *C, const int xy[2], bool findunsel, Base **r_base);
+Bone *ED_armature_pick_bone(bContext *C, const int xy[2], bool findunsel, Base **r_base);
diff --git a/source/blender/editors/include/ED_outliner.hh b/source/blender/editors/include/ED_outliner.hh
index b2aee00f556..d02b48697d5 100644
--- a/source/blender/editors/include/ED_outliner.hh
+++ b/source/blender/editors/include/ED_outliner.hh
@@ -13,6 +13,8 @@ struct ListBase;
struct SpaceOutliner;
struct bContext;
struct ID;
+struct Bone;
+struct PointerRNA;
bool ED_outliner_collections_editor_poll(bContext *C);
@@ -28,6 +30,12 @@ void ED_outliner_selected_objects_get(const bContext *C, ListBase *objects);
*/
Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2]);
+/**
+ * Get the PointerRNA at the given coordinates. ONLY works for bone structs at the moment. Can be
+ * extended to work with other structs if the need arises.
+ */
+bool ED_outliner_give_rna_under_cursor(bContext *C, const int mval[2], PointerRNA *r_ptr);
+
/**
* Functions for tagging outliner selection syncing is dirty from operators.
*/
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index 6275d7ba620..7ce205e229c 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -35,6 +35,7 @@ set(SRC
eyedroppers/eyedropper_depth.cc
eyedroppers/eyedropper_driver.cc
eyedroppers/eyedropper_gpencil_color.cc
+ eyedroppers/eyedropper_bone.cc
eyedroppers/eyedropper_grease_pencil_color.cc
eyedroppers/interface_eyedropper.cc
interface.cc
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_bone.cc b/source/blender/editors/interface/eyedroppers/eyedropper_bone.cc
new file mode 100644
index 00000000000..d53e018773e
--- /dev/null
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_bone.cc
@@ -0,0 +1,551 @@
+/* SPDX-FileCopyrightText: 2024 Blender Authors
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup edinterface
+ *
+ * Eyedropper (bones)
+ */
+
+#include "BKE_armature.hh"
+#include "BKE_context.hh"
+#include "BKE_idtype.hh"
+#include "BKE_object.hh"
+#include "BKE_report.hh"
+#include "BKE_screen.hh"
+
+#include "BLT_translation.hh"
+
+#include "BLI_assert.h"
+#include "BLI_math_vector.h"
+#include "BLI_string.h"
+
+#include "RNA_access.hh"
+#include "RNA_prototypes.hh"
+
+#include "ED_armature.hh"
+#include "ED_outliner.hh"
+#include "ED_screen.hh"
+#include "ED_space_api.hh"
+#include "ED_view3d.hh"
+
+#include "WM_api.hh"
+#include "WM_types.hh"
+
+#include "UI_interface.hh"
+#include "UI_view2d.hh"
+
+#include "eyedropper_intern.hh"
+#include "interface_intern.hh"
+
+namespace blender::ui {
+
+enum class SampleResult {
+ UNKNOWN_FAILURE,
+ SUCCESS,
+ NO_BONE_3DVIEW,
+ NO_BONE_OUTLINER,
+ WRONG_AREA,
+ WRONG_ARMATURE,
+};
+
+struct BoneDropper {
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ PointerRNA search_ptr;
+ PropertyRNA *search_prop;
+
+ bool is_undo;
+
+ ScrArea *cursor_area; /* Area under the cursor. */
+ ARegionType *area_region_type;
+ void *draw_handle_pixel;
+ int name_pos[2];
+ char name[64];
+};
+
+struct BoneSampleData {
+ SampleResult sample_result = SampleResult::UNKNOWN_FAILURE;
+ /* Either EditBone, bPoseChannel or Bone. */
+ PointerRNA bone_rna;
+ char *name = nullptr;
+};
+
+static void datadropper_draw_cb(const bContext * /*C*/, ARegion * /*region*/, void *arg)
+{
+ BoneDropper *ddr = static_cast(arg);
+ eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name);
+}
+
+static bool is_bone_dropper_valid(BoneDropper *bone_dropper)
+{
+ if ((bone_dropper->ptr.data == nullptr) || (bone_dropper->prop == nullptr)) {
+ return false;
+ }
+ if (!RNA_property_editable(&bone_dropper->ptr, bone_dropper->prop)) {
+ return false;
+ }
+
+ PointerRNA owner_ptr = RNA_id_pointer_create(bone_dropper->search_ptr.owner_id);
+ if (RNA_type_to_ID_code(owner_ptr.type) != ID_AR) {
+ return false;
+ }
+
+ return true;
+}
+
+static int bonedropper_init(bContext *C, wmOperator *op)
+{
+ int index_dummy;
+ PointerRNA button_ptr;
+ PropertyRNA *button_prop;
+ uiBut *button = UI_context_active_but_prop_get(C, &button_ptr, &button_prop, &index_dummy);
+
+ if (!button || button->type != UI_BTYPE_SEARCH_MENU) {
+ return false;
+ }
+
+ BoneDropper *bone_dropper = MEM_cnew(__func__);
+ uiButSearch *search_button = (uiButSearch *)button;
+ bone_dropper->ptr = button_ptr;
+ bone_dropper->prop = button_prop;
+ bone_dropper->search_ptr = search_button->rnasearchpoin;
+ bone_dropper->search_prop = search_button->rnasearchprop;
+ if (!is_bone_dropper_valid(bone_dropper)) {
+ MEM_freeN(bone_dropper);
+ return false;
+ }
+
+ op->customdata = bone_dropper;
+
+ bone_dropper->is_undo = UI_but_flag_is_set(button, UI_BUT_UNDO);
+
+ SpaceType *space_type = BKE_spacetype_from_id(SPACE_VIEW3D);
+ ARegionType *area_region_type = BKE_regiontype_from_id(space_type, RGN_TYPE_WINDOW);
+ bone_dropper->cursor_area = CTX_wm_area(C);
+ bone_dropper->area_region_type = area_region_type;
+ bone_dropper->draw_handle_pixel = ED_region_draw_cb_activate(
+ area_region_type, datadropper_draw_cb, bone_dropper, REGION_DRAW_POST_PIXEL);
+
+ return true;
+}
+
+static void bonedropper_exit(bContext *C, wmOperator *op)
+{
+ wmWindow *win = CTX_wm_window(C);
+ WM_cursor_modal_restore(win);
+
+ if (op->customdata) {
+ BoneDropper *bdr = (BoneDropper *)op->customdata;
+
+ if (bdr->area_region_type) {
+ ED_region_draw_cb_exit(bdr->area_region_type, bdr->draw_handle_pixel);
+ }
+ ED_area_tag_redraw(bdr->cursor_area);
+
+ MEM_freeN(op->customdata);
+
+ op->customdata = nullptr;
+ }
+ WM_event_add_mousemove(win);
+}
+
+static void bonedropper_cancel(bContext *C, wmOperator *op)
+{
+ bonedropper_exit(C, op);
+}
+
+/* To switch the draw callback when region under mouse event changes */
+static void bonedropper_set_draw_callback_region(ScrArea &area, BoneDropper &bdr)
+{
+ if (area.spacetype == bdr.cursor_area->spacetype) {
+ return;
+ }
+
+ /* If the spacetype changed remove the old callback. */
+ ED_region_draw_cb_exit(bdr.area_region_type, bdr.draw_handle_pixel);
+
+ ARegion *region = BKE_area_find_region_type(bdr.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);
+
+ bdr.cursor_area = &area;
+ bdr.area_region_type = art;
+ bdr.draw_handle_pixel = ED_region_draw_cb_activate(
+ art, datadropper_draw_cb, &bdr, REGION_DRAW_POST_PIXEL);
+}
+
+static BoneSampleData sample_data_from_3d_view(bContext *C,
+ const int mval[2],
+ const BoneDropper &bdr)
+{
+ Base *base = nullptr;
+
+ switch (CTX_data_mode_enum(C)) {
+ case CTX_MODE_POSE: {
+ bPoseChannel *bone = ED_armature_pick_pchan(C, mval, true, &base);
+ if (!bone || !base) {
+ return {SampleResult::NO_BONE_3DVIEW};
+ }
+ Object *ob = base->object;
+ bArmature *armature = (bArmature *)ob->data;
+ if (!armature || &armature->id != bdr.search_ptr.owner_id) {
+ return {SampleResult::WRONG_ARMATURE};
+ }
+
+ BoneSampleData sample_data;
+ sample_data.name = bone->name;
+ /* Not using the search pointer owner ID because pose bones are part of the object. */
+ sample_data.bone_rna = RNA_pointer_create(&base->object->id, &RNA_PoseBone, bone);
+ sample_data.sample_result = SampleResult::SUCCESS;
+ return sample_data;
+ }
+
+ case CTX_MODE_EDIT_ARMATURE: {
+ EditBone *ebone = ED_armature_pick_ebone(C, mval, true, &base);
+ if (!ebone || !base) {
+ return {SampleResult::NO_BONE_3DVIEW};
+ }
+ Object *ob = base->object;
+ bArmature *armature = (bArmature *)ob->data;
+ if (!armature || &armature->id != bdr.search_ptr.owner_id) {
+ return {SampleResult::WRONG_ARMATURE};
+ }
+
+ BoneSampleData sample_data;
+ sample_data.name = ebone->name;
+ sample_data.bone_rna = RNA_pointer_create(&armature->id, &RNA_EditBone, ebone);
+ sample_data.sample_result = SampleResult::SUCCESS;
+ return sample_data;
+ }
+
+ default:
+ return {SampleResult::NO_BONE_3DVIEW};
+ }
+}
+
+static BoneSampleData sample_data_from_outliner(bContext *C,
+ const int mval[2],
+ const BoneDropper &bdr)
+{
+ BoneSampleData sample_data;
+
+ const bool success = ED_outliner_give_rna_under_cursor(C, mval, &sample_data.bone_rna);
+ if (!success) {
+ sample_data.sample_result = SampleResult::NO_BONE_OUTLINER;
+ return sample_data;
+ }
+ ID *bone_id = sample_data.bone_rna.owner_id;
+ ID *search_id = bdr.search_ptr.owner_id;
+
+ /* By comparing the ID of the RNA returned by the outliner with the ID we are searching in, we
+ * can determine if the Bone is for the correct armature. */
+ if (sample_data.bone_rna.type == &RNA_Bone) {
+ if (bone_id != search_id) {
+ sample_data.sample_result = SampleResult::WRONG_ARMATURE;
+ return sample_data;
+ }
+ Bone *bone = (Bone *)sample_data.bone_rna.data;
+ sample_data.name = bone->name;
+ sample_data.sample_result = SampleResult::SUCCESS;
+ return sample_data;
+ }
+
+ if (sample_data.bone_rna.type == &RNA_EditBone) {
+ if (bone_id != search_id) {
+ sample_data.sample_result = SampleResult::WRONG_ARMATURE;
+ return sample_data;
+ }
+ EditBone *bone = (EditBone *)sample_data.bone_rna.data;
+ sample_data.name = bone->name;
+ sample_data.sample_result = SampleResult::SUCCESS;
+ return sample_data;
+ }
+
+ if (sample_data.bone_rna.type == &RNA_PoseBone) {
+ bPoseChannel *pose_bone = (bPoseChannel *)sample_data.bone_rna.data;
+ /* Special case for pose bones. Because they are not stored in the Armature, the IDs of the
+ * search property and the picked result might not match since the comparison would be between
+ * armature and object. */
+ if (bdr.search_ptr.type == &RNA_Object) {
+ if (bone_id != search_id) {
+ sample_data.sample_result = SampleResult::WRONG_ARMATURE;
+ return sample_data;
+ }
+ }
+ /* If looking for an armature, get the Armature object and follow the data pointer. */
+ if (bdr.search_ptr.type == &RNA_Armature) {
+ /* Expecting Pose Bones to be stored on the object. */
+ BLI_assert(GS(sample_data.bone_rna.owner_id->name) == ID_OB);
+ Object *armature_object = (Object *)sample_data.bone_rna.owner_id;
+ if (armature_object->data != bdr.search_ptr.owner_id) {
+ sample_data.sample_result = SampleResult::WRONG_ARMATURE;
+ return sample_data;
+ }
+ }
+ sample_data.name = pose_bone->name;
+ sample_data.sample_result = SampleResult::SUCCESS;
+ return sample_data;
+ }
+
+ sample_data.sample_result = SampleResult::NO_BONE_OUTLINER;
+ return sample_data;
+}
+
+static BoneSampleData bonedropper_sample_pt(
+ bContext *C, wmWindow &win, ScrArea &area, BoneDropper &bdr, const int event_xy[2])
+{
+ if (!ELEM(area.spacetype, SPACE_VIEW3D, SPACE_OUTLINER)) {
+ return {};
+ }
+
+ ARegion *region = BKE_area_find_region_xy(&area, RGN_TYPE_WINDOW, event_xy);
+
+ if (!region) {
+ return {};
+ }
+
+ wmWindow *win_prev = CTX_wm_window(C);
+ ScrArea *area_prev = CTX_wm_area(C);
+ ARegion *region_prev = CTX_wm_region(C);
+
+ const int mval[2] = {event_xy[0] - region->winrct.xmin, event_xy[1] - region->winrct.ymin};
+
+ 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);
+
+ BoneSampleData sample_data;
+ switch (area.spacetype) {
+ case SPACE_VIEW3D: {
+ sample_data = sample_data_from_3d_view(C, mval, bdr);
+ break;
+ }
+ case SPACE_OUTLINER: {
+ sample_data = sample_data_from_outliner(C, mval, bdr);
+ break;
+ }
+
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ if (sample_data.name) {
+ SNPRINTF(bdr.name, "%s", sample_data.name);
+ copy_v2_v2_int(bdr.name_pos, mval);
+ }
+
+ CTX_wm_window_set(C, win_prev);
+ CTX_wm_area_set(C, area_prev);
+ CTX_wm_region_set(C, region_prev);
+
+ return sample_data;
+}
+
+static SampleResult bonedropper_sample(bContext *C, BoneDropper &bdr, const int event_xy[2])
+{
+ int event_xy_win[2];
+ wmWindow *win = nullptr;
+ ScrArea *area = nullptr;
+ eyedropper_win_area_find(C, event_xy, event_xy_win, &win, &area);
+
+ if (!win || !area) {
+ return SampleResult::WRONG_AREA;
+ }
+ if (!ELEM(area->spacetype, SPACE_VIEW3D, SPACE_OUTLINER)) {
+ return SampleResult::WRONG_AREA;
+ }
+
+ BoneSampleData sample_data = bonedropper_sample_pt(C, *win, *area, bdr, event_xy_win);
+ if (!sample_data.name) {
+ return sample_data.sample_result;
+ }
+
+ StructRNA *search_type = RNA_property_pointer_type(&bdr.search_ptr, bdr.search_prop);
+ /* In case we are searching for a bone, convert the pointer from bPoseChannel. */
+ if (search_type == &RNA_Bone && sample_data.bone_rna.type == &RNA_PoseBone &&
+ bdr.search_ptr.type == &RNA_Armature)
+ {
+ /* We are searching for something in the armature but got a pose bone on the object, so we
+ * need to do a conversion. We will just assume the ID under the cursor is the one we are
+ * searching for since there is no way to get the armature ID from the object ID that we
+ * have. */
+ bPoseChannel *pose_bone = (bPoseChannel *)sample_data.bone_rna.data;
+ sample_data.bone_rna = RNA_pointer_create(bdr.search_ptr.owner_id, &RNA_Bone, pose_bone->bone);
+ }
+
+ PropertyType type = RNA_property_type(bdr.prop);
+ switch (type) {
+ case PROP_STRING:
+ RNA_property_string_set(&bdr.ptr, bdr.prop, sample_data.name);
+ break;
+ case PROP_POINTER:
+ RNA_property_pointer_set(&bdr.ptr, bdr.prop, sample_data.bone_rna, CTX_wm_reports(C));
+ break;
+
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ RNA_property_update(C, &bdr.ptr, bdr.prop);
+
+ return SampleResult::SUCCESS;
+}
+
+static void generate_sample_warning(SampleResult result, wmOperator *op)
+{
+ switch (result) {
+ case SampleResult::UNKNOWN_FAILURE:
+ BKE_report(op->reports, RPT_WARNING, "Picking a bone failed");
+ break;
+ case SampleResult::WRONG_ARMATURE:
+ BKE_report(
+ op->reports, RPT_WARNING, "Picked bone does not belong to the already chosen armature");
+ break;
+
+ case SampleResult::NO_BONE_3DVIEW:
+ BKE_report(op->reports,
+ RPT_WARNING,
+ "Selection is not a bone. Armature needs to be in Pose Mode or Edit Mode "
+ "to pick in the 3D Viewport");
+ break;
+
+ case SampleResult::NO_BONE_OUTLINER:
+ BKE_report(op->reports, RPT_WARNING, "Selection is not a bone");
+ break;
+
+ case SampleResult::WRONG_AREA:
+ BKE_report(op->reports, RPT_WARNING, "Can only pick from the 3D viewport or the outliner");
+ break;
+
+ case SampleResult::SUCCESS:
+ break;
+ }
+}
+
+static int bonedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ BoneDropper *bdr = (BoneDropper *)op->customdata;
+ if (!bdr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case EYE_MODAL_CANCEL:
+ bonedropper_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ case EYE_MODAL_SAMPLE_CONFIRM: {
+ const bool is_undo = bdr->is_undo;
+ const SampleResult result = bonedropper_sample(C, *bdr, event->xy);
+ bonedropper_exit(C, op);
+ if (result == SampleResult::SUCCESS) {
+ /* Could support finished & undo-skip. */
+ return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ }
+ generate_sample_warning(result, op);
+ }
+ }
+ }
+ else if (event->type == MOUSEMOVE) {
+ bdr->name[0] = '\0';
+ int event_xy_win[2];
+ wmWindow *win = nullptr;
+ ScrArea *area = nullptr;
+ eyedropper_win_area_find(C, event->xy, event_xy_win, &win, &area);
+
+ if (win && area) {
+ /* Set the region for eyedropper cursor text drawing */
+ bonedropper_set_draw_callback_region(*area, *bdr);
+ bonedropper_sample_pt(C, *win, *area, *bdr, event->xy);
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+static int bonedropper_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
+{
+ /* This is needed to ensure viewport picking works. */
+ BKE_object_update_select_id(CTX_data_main(C));
+
+ if (bonedropper_init(C, op)) {
+ wmWindow *win = CTX_wm_window(C);
+ /* Workaround for de-activating the button clearing the cursor, see #76794 */
+ UI_context_active_but_clear(C, win, CTX_wm_region(C));
+ WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER);
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static int bonedropper_exec(bContext *C, wmOperator *op)
+{
+ if (bonedropper_init(C, op)) {
+ bonedropper_exit(C, op);
+
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static bool bonedropper_poll(bContext *C)
+{
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ int index_dummy;
+
+ if (CTX_wm_window(C) == nullptr) {
+ return false;
+ }
+
+ uiBut *but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy);
+
+ if (!but) {
+ return false;
+ }
+
+ if (but->type != UI_BTYPE_SEARCH_MENU || !(but->flag & UI_BUT_VALUE_CLEAR)) {
+ return false;
+ }
+
+ uiButSearch *search_but = (uiButSearch *)but;
+
+ if (!ELEM(RNA_property_type(prop), PROP_STRING, PROP_POINTER)) {
+ return false;
+ }
+
+ StructRNA *type = RNA_property_pointer_type(&search_but->rnasearchpoin,
+ search_but->rnasearchprop);
+
+ return type == &RNA_Bone || type == &RNA_EditBone;
+}
+
+void UI_OT_eyedropper_bone(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Eyedropper Bone";
+ ot->idname = "UI_OT_eyedropper_bone";
+ ot->description = "Sample a bone from the 3D View or the Outliner to store in a property";
+
+ /* API callbacks. */
+ ot->invoke = bonedropper_invoke;
+ ot->modal = bonedropper_modal;
+ ot->cancel = bonedropper_cancel;
+ ot->exec = bonedropper_exec;
+ ot->poll = bonedropper_poll;
+
+ /* Flags. */
+ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL;
+}
+
+} // namespace blender::ui
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_datablock.cc b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.cc
index 42eb8402849..b1926fb9106 100644
--- a/source/blender/editors/interface/eyedroppers/eyedropper_datablock.cc
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.cc
@@ -219,7 +219,7 @@ static bool datadropper_id_sample(bContext *C, DataDropper *ddr, const int event
int event_xy_win[2];
wmWindow *win;
ScrArea *area;
- datadropper_win_area_find(C, event_xy, event_xy_win, &win, &area);
+ eyedropper_win_area_find(C, event_xy, event_xy_win, &win, &area);
datadropper_id_sample_pt(C, win, area, ddr, event_xy_win, &id);
return datadropper_id_set(C, ddr, id);
@@ -286,7 +286,7 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
int event_xy_win[2];
wmWindow *win;
ScrArea *area;
- datadropper_win_area_find(C, event->xy, event_xy_win, &win, &area);
+ eyedropper_win_area_find(C, event->xy, event_xy_win, &win, &area);
/* Set the region for eyedropper cursor text drawing */
datadropper_set_draw_callback_region(area, ddr);
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_intern.hh b/source/blender/editors/interface/eyedroppers/eyedropper_intern.hh
index 691f513a741..3594f6a43a8 100644
--- a/source/blender/editors/interface/eyedroppers/eyedropper_intern.hh
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_intern.hh
@@ -24,11 +24,11 @@ void eyedropper_draw_cursor_text_region(const int xy[2], const char *name);
* \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 bContext *C,
- const int event_xy[2],
- int r_event_xy[2],
- wmWindow **r_win,
- ScrArea **r_area);
+void eyedropper_win_area_find(const bContext *C,
+ const int event_xy[2],
+ int r_event_xy[2],
+ wmWindow **r_win,
+ ScrArea **r_area);
/* interface_eyedropper_color.c (expose for color-band picker) */
diff --git a/source/blender/editors/interface/eyedroppers/interface_eyedropper.cc b/source/blender/editors/interface/eyedroppers/interface_eyedropper.cc
index 73c2b057485..30e17bb98d5 100644
--- a/source/blender/editors/interface/eyedroppers/interface_eyedropper.cc
+++ b/source/blender/editors/interface/eyedroppers/interface_eyedropper.cc
@@ -53,6 +53,7 @@ wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
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_bone");
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");
@@ -141,11 +142,11 @@ uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *ev
return but;
}
-void datadropper_win_area_find(const bContext *C,
- const int event_xy[2],
- int r_event_xy[2],
- wmWindow **r_win,
- ScrArea **r_area)
+void eyedropper_win_area_find(const bContext *C,
+ const int event_xy[2],
+ int r_event_xy[2],
+ wmWindow **r_win,
+ ScrArea **r_area)
{
bScreen *screen = CTX_wm_screen(C);
diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc
index 87ec2693056..3830bd0afa4 100644
--- a/source/blender/editors/interface/interface.cc
+++ b/source/blender/editors/interface/interface.cc
@@ -1619,6 +1619,7 @@ enum PredefinedExtraOpIconType {
PREDEFINED_EXTRA_OP_ICON_NONE = 1,
PREDEFINED_EXTRA_OP_ICON_CLEAR,
PREDEFINED_EXTRA_OP_ICON_EYEDROPPER,
+ PREDEFINED_EXTRA_OP_ICON_BONE_EYEDROPPER
};
static PointerRNA *ui_but_extra_operator_icon_add_ptr(uiBut *but,
@@ -1710,6 +1711,23 @@ static bool ui_but_icon_extra_is_visible_search_eyedropper(uiBut *but)
return ((but->editstr == nullptr) && (idcode == ID_OB || OB_DATA_SUPPORT_ID(idcode)));
}
+static bool ui_but_icon_extra_is_visible_bone_eyedropper(uiBut *but)
+{
+ if (but->type != UI_BTYPE_SEARCH_MENU || !(but->flag & UI_BUT_VALUE_CLEAR)) {
+ return false;
+ }
+ if (but->editstr != nullptr) {
+ return false;
+ }
+ if (!ELEM(RNA_property_type(but->rnaprop), PROP_STRING, PROP_POINTER)) {
+ return false;
+ }
+ uiButSearch *search_but = (uiButSearch *)but;
+ StructRNA *type = RNA_property_pointer_type(&search_but->rnasearchpoin,
+ search_but->rnasearchprop);
+ return type == &RNA_Bone || type == &RNA_EditBone;
+}
+
static PredefinedExtraOpIconType ui_but_icon_extra_get(uiBut *but)
{
switch (but->type) {
@@ -1728,6 +1746,9 @@ static PredefinedExtraOpIconType ui_but_icon_extra_get(uiBut *but)
else if (ui_but_icon_extra_is_visible_search_eyedropper(but)) {
return PREDEFINED_EXTRA_OP_ICON_EYEDROPPER;
}
+ else if (ui_but_icon_extra_is_visible_bone_eyedropper(but)) {
+ return PREDEFINED_EXTRA_OP_ICON_BONE_EYEDROPPER;
+ }
break;
default:
break;
@@ -1760,6 +1781,17 @@ static void ui_but_predefined_extra_operator_icons_add(uiBut *but)
break;
}
+ case PREDEFINED_EXTRA_OP_ICON_BONE_EYEDROPPER: {
+ static wmOperatorType *id_eyedropper_ot = nullptr;
+ if (!id_eyedropper_ot) {
+ id_eyedropper_ot = WM_operatortype_find("UI_OT_eyedropper_bone", false);
+ }
+ BLI_assert(id_eyedropper_ot);
+
+ optype = id_eyedropper_ot;
+ icon = ICON_EYEDROPPER;
+ break;
+ }
case PREDEFINED_EXTRA_OP_ICON_CLEAR: {
static wmOperatorType *clear_ot = nullptr;
if (!clear_ot) {
diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh
index 1fa77669b1c..2b704ca5a6b 100644
--- a/source/blender/editors/interface/interface_intern.hh
+++ b/source/blender/editors/interface/interface_intern.hh
@@ -1553,6 +1553,9 @@ void UI_OT_eyedropper_color(wmOperatorType *ot);
namespace blender::ui {
void UI_OT_eyedropper_colorramp(wmOperatorType *ot);
void UI_OT_eyedropper_colorramp_point(wmOperatorType *ot);
+
+void UI_OT_eyedropper_bone(wmOperatorType *ot);
+
} // namespace blender::ui
/* interface_eyedropper_datablock.c */
diff --git a/source/blender/editors/interface/interface_ops.cc b/source/blender/editors/interface/interface_ops.cc
index 0ffa9c56191..0d986033330 100644
--- a/source/blender/editors/interface/interface_ops.cc
+++ b/source/blender/editors/interface/interface_ops.cc
@@ -2774,6 +2774,7 @@ void ED_operatortypes_ui()
WM_operatortype_append(UI_OT_eyedropper_depth);
WM_operatortype_append(UI_OT_eyedropper_driver);
WM_operatortype_append(UI_OT_eyedropper_gpencil_color);
+ WM_operatortype_append(UI_OT_eyedropper_bone);
WM_operatortype_append(UI_OT_eyedropper_grease_pencil_color);
}
diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc
index 78e58b639df..57783da47ff 100644
--- a/source/blender/editors/space_outliner/outliner_utils.cc
+++ b/source/blender/editors/space_outliner/outliner_utils.cc
@@ -16,6 +16,9 @@
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
+#include "RNA_access.hh"
+#include "RNA_prototypes.hh"
+
#include "BKE_context.hh"
#include "BKE_layer.hh"
#include "BKE_object.hh"
@@ -478,3 +481,42 @@ Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2])
return base;
}
+
+bool ED_outliner_give_rna_under_cursor(bContext *C, const int mval[2], PointerRNA *r_ptr)
+{
+ ARegion *region = CTX_wm_region(C);
+ SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
+
+ float view_mval[2];
+ UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
+
+ TreeElement *te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]);
+ if (!te) {
+ return false;
+ }
+
+ bool success = true;
+ TreeStoreElem *tselem = TREESTORE(te);
+ switch (tselem->type) {
+ case TSE_BONE: {
+ Bone *bone = (Bone *)te->directdata;
+ *r_ptr = RNA_pointer_create(tselem->id, &RNA_Bone, bone);
+ break;
+ }
+ case TSE_POSE_CHANNEL: {
+ bPoseChannel *pchan = (bPoseChannel *)te->directdata;
+ *r_ptr = RNA_pointer_create(tselem->id, &RNA_PoseBone, pchan);
+ break;
+ }
+ case TSE_EBONE: {
+ EditBone *bone = (EditBone *)te->directdata;
+ *r_ptr = RNA_pointer_create(tselem->id, &RNA_EditBone, bone);
+ break;
+ }
+
+ default:
+ success = false;
+ break;
+ }
+ return success;
+}