Changeset View
Standalone View
source/blender/editors/interface/interface_eyedropper_gpencil_color.c
- This file was added.
| /* | |||||
| * This program is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU General Public License | |||||
| * as published by the Free Software Foundation; either version 2 | |||||
| * of the License, or (at your option) any later version. | |||||
| * | |||||
| * This program is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU General Public License | |||||
| * along with this program; if not, write to the Free Software Foundation, | |||||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
| * | |||||
| * The Original Code is Copyright (C) 2009 Blender Foundation. | |||||
| * All rights reserved. | |||||
| */ | |||||
| /** \file | |||||
| * \ingroup edinterface | |||||
| * | |||||
| * Eyedropper (RGB Color) | |||||
| * | |||||
| * Defines: | |||||
| * - #UI_OT_eyedropper_gpencil_color | |||||
| */ | |||||
| #include "MEM_guardedalloc.h" | |||||
| #include "BLI_string.h" | |||||
| #include "BLT_translation.h" | |||||
| #include "DNA_gpencil_types.h" | |||||
| #include "DNA_space_types.h" | |||||
| #include "BKE_context.h" | |||||
| #include "BKE_gpencil.h" | |||||
| #include "BKE_main.h" | |||||
| #include "BKE_material.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 "interface_eyedropper_intern.h" | |||||
| typedef struct EyedropperGPencil { | |||||
| struct ColorManagedDisplay *display; | |||||
| /** color under cursor RGB */ | |||||
| float color[3]; | |||||
| } EyedropperGPencil; | |||||
| /* Helper: Draw status message while the user is running the operator */ | |||||
| static void eyedropper_gpencil_status_indicators(bContext *C, EyedropperGPencil *eye) | |||||
| { | |||||
| 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. */ | |||||
brecht: This text could be modified to indicate when raw mode is on/off? Now it's unclear to me. | |||||
| static bool eyedropper_gpencil_init(bContext *C, wmOperator *op) | |||||
| { | |||||
| EyedropperGPencil *eye = MEM_callocN(sizeof(EyedropperGPencil), __func__); | |||||
Not Done Inline ActionsThis message only appears after moving the mouse a little bit, it should appear immediately (call from eyedropper_gpencil_init instead?). brecht: This message only appears after moving the mouse a little bit, it should appear immediately… | |||||
| op->customdata = eye; | |||||
| Scene *scene = CTX_data_scene(C); | |||||
Not Done Inline ActionsCould you deduplicate this code with rna_GPencil_update and name it something like ED_gpencil_simplify_update? brecht: Could you deduplicate this code with `rna_GPencil_update` and name it something like… | |||||
| const char *display_device; | |||||
| display_device = scene->display_settings.display_device; | |||||
| eye->display = IMB_colormanagement_display_get_named(display_device); | |||||
| 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); | |||||
| } | |||||
| /* Set the material. */ | |||||
| static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye) | |||||
| { | |||||
| Main *bmain = CTX_data_main(C); | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| Material *ma = NULL; | |||||
| const bool only_stroke = ((!event->ctrl) && (!event->shift)); | |||||
| const bool only_fill = ((!event->ctrl) && (event->shift)); | |||||
| const bool both = ((event->ctrl) && (event->shift)); | |||||
| float col_conv[4]; | |||||
| bool found = false; | |||||
| /* 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) { | |||||
Not Done Inline ActionsFollow comment style: /* 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. */ brecht: Follow comment style:
```
/* Convert from linear rgb space to display space because grease… | |||||
| 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); | |||||
| } | |||||
| /* Look for a similar material in grease pencil slots. */ | |||||
| short *totcol = give_totcolp(ob); | |||||
| for (short i = 0; i < *totcol; i++) { | |||||
| ma = give_current_material(ob, i + 1); | |||||
Not Done Inline ActionsOnly call this if scene->r.simplify_gpencil != eye->saved_simplify. It's quite expensive to reevaluate all objects, and this should only be done when it's really needed. brecht: Only call this if `scene->r.simplify_gpencil != eye->saved_simplify`.
It's quite expensive to… | |||||
| 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_STYLE_STROKE_SHOW); | |||||
| /* Check fill color. */ | |||||
| bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) && | |||||
| (gp_style->flag & GP_STYLE_FILL_SHOW); | |||||
| if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_STYLE_FILL_SHOW) == 0)) { | |||||
| found = true; | |||||
Not Done Inline ActionsCould remove col variable and just use eye->color directly in code below. brecht: Could remove `col` variable and just use `eye->color` directly in code below. | |||||
| } | |||||
| else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_STYLE_STROKE_SHOW) == 0)) { | |||||
| found = true; | |||||
| } | |||||
| else if ((both) && (found_stroke) && (found_fill)) { | |||||
| found = true; | |||||
| } | |||||
| /* Found existing material. */ | |||||
| if (found) { | |||||
Not Done Inline ActionsThis could use a comment explaining that grease pencil colors are in display space, and that this conversion is needed to undo the conversion to linear performed by eyedropper_color_sample_fl. brecht: This could use a comment explaining that grease pencil colors are in display space, and that… | |||||
| 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); | |||||
Not Done Inline ActionsUse same notifiers as when modifying actol in RNA. Here that means: NC_MATERIAL | ND_SHADING_LINKS NC_SPACE | ND_SPACE_VIEW3D brecht: Use same notifiers as when modifying `actol` in RNA.
Here that means:
```
NC_MATERIAL |… | |||||
| 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_STYLE_STROKE_SHOW; | |||||
| gp_style_new->flag &= ~GP_STYLE_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_STYLE_STROKE_SHOW; | |||||
| gp_style_new->flag |= GP_STYLE_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) { | |||||
Not Done Inline ActionsUse same update as similar code elsewhere. In this case: 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); brecht: Use same update as similar code elsewhere. In this case:
```
WM_main_add_notifier(NC_OBJECT… | |||||
| gp_style_new->flag |= GP_STYLE_STROKE_SHOW | GP_STYLE_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"); | |||||
| } | |||||
| /* Sample the color below cursor. */ | |||||
| static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, int mx, int my) | |||||
| { | |||||
| eyedropper_color_sample_fl(C, mx, my, eye->color); | |||||
| } | |||||
| /* Cancel operator. */ | |||||
| static void eyedropper_gpencil_cancel(bContext *C, wmOperator *op) | |||||
| { | |||||
| eyedropper_gpencil_exit(C, op); | |||||
| } | |||||
Not Done Inline ActionsNew Grease Pencil Material created -> Add Grease Pencil Material brecht: `New Grease Pencil Material created` -> `Add Grease Pencil Material` | |||||
| /* Main modal status check. */ | |||||
| static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent *event) | |||||
| { | |||||
| EyedropperGPencil *eye = (EyedropperGPencil *)op->customdata; | |||||
| /* Handle modal keymap */ | |||||
Not Done Inline ActionsThis value is not used anywhere. brecht: This value is not used anywhere. | |||||
| 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; | |||||
Not Done Inline ActionsCan just pass eye->color directly without intermediate col variable. brecht: Can just pass `eye->color` directly without intermediate `col` variable. | |||||
| } | |||||
| case EYE_MODAL_SAMPLE_CONFIRM: { | |||||
| eyedropper_gpencil_color_sample(C, eye, event->x, event->y); | |||||
| /* 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; | |||||
| break; | |||||
Not Done Inline Actionseyedropper_gpencil_rawmode_set -> eyedropper_gpencil_rawmode_toggle brecht: `eyedropper_gpencil_rawmode_set` -> `eyedropper_gpencil_rawmode_toggle` | |||||
| } | |||||
| default: { | |||||
| break; | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| case MOUSEMOVE: | |||||
| case INBETWEEN_MOUSEMOVE: { | |||||
| eyedropper_gpencil_color_sample(C, eye, event->x, event->y); | |||||
| break; | |||||
| } | |||||
Not Done Inline ActionsDepsgraph tag only the scene here and send notifier for redraw, not all objects. brecht: Depsgraph tag only the scene here and send notifier for redraw, not all objects. | |||||
| default: { | |||||
| break; | |||||
| } | |||||
| } | |||||
| return OPERATOR_RUNNING_MODAL; | |||||
| } | |||||
| /* Modal Operator init */ | |||||
| static int eyedropper_gpencil_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) | |||||
| { | |||||
| /* Init. */ | |||||
Not Done Inline ActionsComment looks out of place. brecht: Comment looks out of place. | |||||
| if (eyedropper_gpencil_init(C, op)) { | |||||
| /* Add modal temp handler. */ | |||||
| WM_event_add_modal_handler(C, op); | |||||
| /* Status message. */ | |||||
| EyedropperGPencil *eye = (EyedropperGPencil *)op->customdata; | |||||
Not Done Inline ActionsNo need to add break; after return. brecht: No need to add `break;` after `return`. | |||||
| eyedropper_gpencil_status_indicators(C, eye); | |||||
| return OPERATOR_RUNNING_MODAL; | |||||
| } | |||||
| else { | |||||
| return OPERATOR_PASS_THROUGH; | |||||
| } | |||||
| } | |||||
| /* Repeat operator */ | |||||
| static int eyedropper_gpencil_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| /* init */ | |||||
| if (eyedropper_gpencil_init(C, op)) { | |||||
| /* cleanup */ | |||||
Not Done Inline Actionsis_undo is never set, is this code needed or unfinished? brecht: `is_undo` is never set, is this code needed or unfinished? | |||||
| eyedropper_gpencil_exit(C, op); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| else { | |||||
| 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) | |||||
| { | |||||
| /* 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; | |||||
Not Done Inline ActionsThis seems wrong. You press R to enter the eye dropper in raw mode, then release R key and it disables raw mode, while the modal operator keeps running? brecht: This seems wrong. You press R to enter the eye dropper in raw mode, then release R key and it… | |||||
| ot->poll = eyedropper_gpencil_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; | |||||
| } | |||||
Not Done Inline ActionsAdd description explaining what Raw Mode is. brecht: Add description explaining what Raw Mode is. | |||||
This text could be modified to indicate when raw mode is on/off? Now it's unclear to me.