User Interface: Add button color for indicating that the value differs from the interpolated one

One issue that especially newer users often run into is that they accidentally reset changes to the scene by switching frame without creating a keyframe first.

Therefore, this commit adds a new color that is used to draw properties if their current value differs from the one that would be set when switching to this frame.
This works both for existing keyframes as well as for currently interpolated frames.

Unfortunately the flags in but->flag are full, so I had to move the new flag to but->drawflag and pass that to all relevant functions.

I went with orange for the color since afaics it fits with the green and yellow that are currently used for keyframe states and since it's somewhat reddish to signify that there might be something to look out for here.

Reviewers: campbellbarton, #user_interface, brecht

Reviewed By: campbellbarton

Subscribers: brecht, predoe

Differential Revision: https://developer.blender.org/D3949
This commit is contained in:
Lukas Stockner 2018-12-07 03:26:20 +01:00
parent aff9ccbade
commit e79bb957fc
Notes: blender-bot 2023-02-14 09:02:40 +01:00
Referenced by issue #62597, Torus - Crash when changing parameters
Referenced by issue #59128, Eevee - Freestyle: screen becomes black at rendering time
Referenced by issue #59120, Compatibility between 2.79 and 2.8
Referenced by issue #59121, Weird Shadows on viewport (solid mode)
Referenced by issue #59122, Recovering from Auto Save changed some settings
Referenced by issue #59091, Bendy bone duplicate got wrong handles
Referenced by issue #59092, Setting Dynamic Paint brush as particle crashes when selecting a particle system
Referenced by issue #59079, Cycles Stereoscopic Render dies
Referenced by issue #59080, No autoname left/right
Referenced by issue #59081, Some material node groups in 2.80, from 2.7x files, crash in 2.80
Referenced by issue #59070, Viewport stop working with outline disabled
Referenced by issue #59056, Clicking the icon on the windows taskbar suddenly closes blender
Referenced by issue #59057, Crashing when viewing a 2d plane from side ortho views that would project the plane as a line
Referenced by issue #59046, LookDev HDRI changes are applied only to one quad in 3D Viewport Quad view
Referenced by issue #59053, Area light width and height is not updated while using area light size widget
Referenced by issue #58751, Blender 2.8 crash on startup
10 changed files with 84 additions and 28 deletions

View File

@ -228,6 +228,8 @@ const bTheme U_theme_default = {
.inner_driven_sel = RGBA(0x9900e6ff),
.inner_overridden = RGBA(0x19c3c300),
.inner_overridden_sel = RGBA(0x118f8f00),
.inner_changed = RGBA(0xcc7529ff),
.inner_changed_sel = RGBA(0xe6852dff),
.blend = 0.5f,
},
.widget_emboss = RGBA(0x00000005),

View File

@ -754,6 +754,8 @@ class USERPREF_PT_theme(Panel):
colsub.row().prop(ui_state, "inner_key_sel")
colsub.row().prop(ui_state, "inner_overridden")
colsub.row().prop(ui_state, "inner_overridden_sel")
colsub.row().prop(ui_state, "inner_changed")
colsub.row().prop(ui_state, "inner_changed_sel")
col.separator()
col.separator()

View File

@ -97,6 +97,11 @@ static void do_versions_theme(UserDef *userdef, bTheme *btheme)
copy_v4_v4_char(btheme->tuserpref.navigation_bar, U_theme_default.tuserpref.navigation_bar);
}
if (!USER_VERSION_ATLEAST(280, 36)) {
copy_v4_v4_char(btheme->tui.wcol_state.inner_changed, U_theme_default.tui.wcol_state.inner_changed);
copy_v4_v4_char(btheme->tui.wcol_state.inner_changed_sel, U_theme_default.tui.wcol_state.inner_changed_sel);
}
#undef USER_VERSION_ATLEAST
}

View File

@ -2242,6 +2242,20 @@ bool fcurve_frame_has_keyframe(FCurve *fcu, float frame, short filter)
return false;
}
/* Returns whether the current value of a given property differs from the interpolated value. */
bool fcurve_is_changed(PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, float frame)
{
PathResolvedRNA anim_rna;
anim_rna.ptr = ptr;
anim_rna.prop = prop;
anim_rna.prop_index = fcu->array_index;
float fcurve_val = calculate_fcurve(&anim_rna, fcu, frame);
float cur_val = setting_get_rna_value(NULL, &ptr, prop, fcu->array_index, false);
return !compare_ff_relative(fcurve_val, cur_val, FLT_EPSILON, 64);
}
/* Checks whether an Action has a keyframe for a given frame
* Since we're only concerned whether a keyframe exists, we can simply loop until a match is found...
*/

View File

@ -360,6 +360,12 @@ bool autokeyframe_cfra_can_key(struct Scene *scene, struct ID *id);
*/
bool fcurve_frame_has_keyframe(struct FCurve *fcu, float frame, short filter);
/* Lesser Keyframe Checking API call:
* - Returns whether the current value of a given property differs from the interpolated value.
* - Used for button drawing.
*/
bool fcurve_is_changed(struct PointerRNA ptr, struct PropertyRNA *prop, struct FCurve *fcu, float frame);
/* Main Keyframe Checking API call:
* Checks whether a keyframe exists for the given ID-block one the given frame.
* - It is recommended to call this method over the other keyframe-checkers directly,

View File

@ -240,6 +240,8 @@ enum {
UI_BUT_HAS_SHORTCUT = (1 << 23), /* Button has shortcut text. */
UI_BUT_ICON_REVERSE = (1 << 24), /* Reverse order of consecutive off/on icons */
UI_BUT_ANIMATED_CHANGED = (1 << 25), /* Value is animated, but the current value differs from the animated one. */
};
/* scale fixed button widths by this to account for DPI */

View File

@ -76,6 +76,7 @@ void ui_but_anim_flag(uiBut *but, float cfra)
bool special;
but->flag &= ~(UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN);
but->drawflag &= ~UI_BUT_ANIMATED_CHANGED;
/* NOTE: "special" is reserved for special F-Curves stored on the animation data
* itself (which are used to animate properties of the animation data).
@ -96,6 +97,9 @@ void ui_but_anim_flag(uiBut *but, float cfra)
if (fcurve_frame_has_keyframe(fcu, cfra, 0))
but->flag |= UI_BUT_ANIMATED_KEY;
if (fcurve_is_changed(but->rnapoin, but->rnaprop, fcu, cfra))
but->drawflag |= UI_BUT_ANIMATED_CHANGED;
}
else {
but->flag |= UI_BUT_DRIVEN;

View File

@ -146,7 +146,7 @@ typedef struct uiWidgetType {
/* converted colors for state */
uiWidgetColors wcol;
void (*state)(struct uiWidgetType *, int state);
void (*state)(struct uiWidgetType *, int state, int drawflag);
void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign);
void (*custom)(uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign);
void (*text)(uiFontStyle *, uiWidgetColors *, uiBut *, rcti *);
@ -2231,7 +2231,7 @@ static void widget_active_color(char cp[3])
}
/* copy colors from theme, and set changes in it based on state */
static void widget_state(uiWidgetType *wt, int state)
static void widget_state(uiWidgetType *wt, int state, int drawflag)
{
uiWidgetStateColors *wcol_state = wt->wcol_state;
@ -2249,8 +2249,9 @@ static void widget_state(uiWidgetType *wt, int state)
if (state & UI_SELECT) {
copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
if (state & UI_BUT_ANIMATED_KEY)
if (drawflag & UI_BUT_ANIMATED_CHANGED)
widget_state_blend(wt->wcol.inner, wcol_state->inner_changed_sel, wcol_state->blend);
else if (state & UI_BUT_ANIMATED_KEY)
widget_state_blend(wt->wcol.inner, wcol_state->inner_key_sel, wcol_state->blend);
else if (state & UI_BUT_ANIMATED)
widget_state_blend(wt->wcol.inner, wcol_state->inner_anim_sel, wcol_state->blend);
@ -2265,7 +2266,9 @@ static void widget_state(uiWidgetType *wt, int state)
SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
}
else {
if (state & UI_BUT_ANIMATED_KEY)
if (drawflag & UI_BUT_ANIMATED_CHANGED)
widget_state_blend(wt->wcol.inner, wcol_state->inner_changed, wcol_state->blend);
else if (state & UI_BUT_ANIMATED_KEY)
widget_state_blend(wt->wcol.inner, wcol_state->inner_key, wcol_state->blend);
else if (state & UI_BUT_ANIMATED)
widget_state_blend(wt->wcol.inner, wcol_state->inner_anim, wcol_state->blend);
@ -2303,19 +2306,21 @@ static void widget_state(uiWidgetType *wt, int state)
}
/* sliders use special hack which sets 'item' as inner when drawing filling */
static void widget_state_numslider(uiWidgetType *wt, int state)
static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag)
{
uiWidgetStateColors *wcol_state = wt->wcol_state;
float blend = wcol_state->blend - 0.2f; /* XXX special tweak to make sure that bar will still be visible */
/* call this for option button */
widget_state(wt, state);
widget_state(wt, state, drawflag);
/* now, set the inner-part so that it reflects state settings too */
/* TODO: maybe we should have separate settings for the blending colors used for this case? */
if (state & UI_SELECT) {
if (state & UI_BUT_ANIMATED_KEY)
if (drawflag & UI_BUT_ANIMATED_CHANGED)
widget_state_blend(wt->wcol.item, wcol_state->inner_changed_sel, blend);
else if (state & UI_BUT_ANIMATED_KEY)
widget_state_blend(wt->wcol.item, wcol_state->inner_key_sel, blend);
else if (state & UI_BUT_ANIMATED)
widget_state_blend(wt->wcol.item, wcol_state->inner_anim_sel, blend);
@ -2328,7 +2333,9 @@ static void widget_state_numslider(uiWidgetType *wt, int state)
SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
}
else {
if (state & UI_BUT_ANIMATED_KEY)
if (drawflag & UI_BUT_ANIMATED_CHANGED)
widget_state_blend(wt->wcol.item, wcol_state->inner_changed, blend);
else if (state & UI_BUT_ANIMATED_KEY)
widget_state_blend(wt->wcol.item, wcol_state->inner_key, blend);
else if (state & UI_BUT_ANIMATED)
widget_state_blend(wt->wcol.item, wcol_state->inner_anim, blend);
@ -2340,12 +2347,12 @@ static void widget_state_numslider(uiWidgetType *wt, int state)
}
/* labels use theme colors for text */
static void widget_state_option_menu(uiWidgetType *wt, int state)
static void widget_state_option_menu(uiWidgetType *wt, int state, int drawflag)
{
bTheme *btheme = UI_GetTheme(); /* XXX */
/* call this for option button */
widget_state(wt, state);
widget_state(wt, state, drawflag);
/* if not selected we get theme from menu back */
if (state & UI_SELECT)
@ -2355,19 +2362,19 @@ static void widget_state_option_menu(uiWidgetType *wt, int state)
}
static void widget_state_nothing(uiWidgetType *wt, int UNUSED(state))
static void widget_state_nothing(uiWidgetType *wt, int UNUSED(state), int UNUSED(drawflag))
{
wt->wcol = *(wt->wcol_theme);
}
/* special case, button that calls pulldown */
static void widget_state_pulldown(uiWidgetType *wt, int UNUSED(state))
static void widget_state_pulldown(uiWidgetType *wt, int UNUSED(state), int UNUSED(drawflag))
{
wt->wcol = *(wt->wcol_theme);
}
/* special case, pie menu items */
static void widget_state_pie_menu_item(uiWidgetType *wt, int state)
static void widget_state_pie_menu_item(uiWidgetType *wt, int state, int UNUSED(drawflag))
{
wt->wcol = *(wt->wcol_theme);
@ -2399,7 +2406,7 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, int state)
}
/* special case, menu items */
static void widget_state_menu_item(uiWidgetType *wt, int state)
static void widget_state_menu_item(uiWidgetType *wt, int state, int UNUSED(drawflag))
{
wt->wcol = *(wt->wcol_theme);
@ -3393,7 +3400,8 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat
ui_but_v3_get(but, col);
if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDEN | UI_BUT_REDALERT)) {
if ((state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDEN | UI_BUT_REDALERT)) ||
(but->drawflag & UI_BUT_ANIMATED_CHANGED)) {
/* draw based on state - color for keyed etc */
widgetbase_draw(&wtb, wcol);
@ -3642,18 +3650,18 @@ static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, int state, int UN
}
/* labels use Editor theme colors for text */
static void widget_state_label(uiWidgetType *wt, int state)
static void widget_state_label(uiWidgetType *wt, int state, int drawflag)
{
if (state & UI_BUT_LIST_ITEM) {
/* Override default label theme's colors. */
bTheme *btheme = UI_GetTheme();
wt->wcol_theme = &btheme->tui.wcol_list_item;
/* call this for option button */
widget_state(wt, state);
widget_state(wt, state, drawflag);
}
else {
/* call this for option button */
widget_state(wt, state);
widget_state(wt, state, drawflag);
if (state & UI_SELECT)
UI_GetThemeColor3ubv(TH_TEXT_HI, (unsigned char *)wt->wcol.text);
else
@ -4314,13 +4322,14 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
if (wt) {
//rcti disablerect = *rect; /* rect gets clipped smaller for text */
int roundboxalign, state;
int roundboxalign, state, drawflag;
bool disabled = false;
roundboxalign = widget_roundbox_set(but, rect);
/* Mask out flags re-used for local state. */
state = but->flag & ~UI_STATE_FLAGS_ALL;
drawflag = but->drawflag;
if (state & UI_SELECT_DRAW) {
state |= UI_SELECT;
@ -4352,7 +4361,7 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
if (disabled)
ui_widget_color_disabled(wt);
wt->state(wt, state);
wt->state(wt, state, drawflag);
if (wt->custom)
wt->custom(but, &wt->wcol, rect, state, roundboxalign);
else if (wt->draw)
@ -4366,7 +4375,7 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
if ((state & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) {
uiWidgetType wt_back = *wt;
uiWidgetType *wt_temp = widget_type(UI_WTYPE_MENU_ITEM);
wt_temp->state(wt_temp, state);
wt_temp->state(wt_temp, state, drawflag);
copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
wt->wcol.inner[3] = 128;
wt->wcol.roundness = 0.5f;
@ -4415,7 +4424,7 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect)
{
uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK);
wt->state(wt, 0);
wt->state(wt, 0, 0);
if (block)
wt->draw(&wt->wcol, rect, block->flag, block->direction);
else
@ -4489,7 +4498,7 @@ void ui_draw_popover_back(ARegion *ar, uiStyle *UNUSED(style), uiBlock *block, r
ui_draw_popover_back_impl(wt->wcol_theme, rect, block->direction, U.widget_unit / block->aspect, mval_origin);
}
else {
wt->state(wt, 0);
wt->state(wt, 0, 0);
wt->draw(&wt->wcol, rect, 0, 0);
}
@ -4641,7 +4650,7 @@ void ui_draw_widget_back_color(
}
rcti rect_copy = *rect;
wt->state(wt, 0);
wt->state(wt, 0, 0);
if (color) {
rgba_float_to_uchar((unsigned char *)wt->wcol.inner, color);
}
@ -4655,7 +4664,7 @@ void ui_draw_widget_back(uiWidgetTypeEnum type, bool use_shadow, const rcti *rec
void ui_draw_tooltip_background(uiStyle *UNUSED(style), uiBlock *UNUSED(block), rcti *rect)
{
uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP);
wt->state(wt, 0);
wt->state(wt, 0, 0);
/* wt->draw ends up using same function to draw the tooltip as menu_back */
wt->draw(&wt->wcol, rect, 0, 0);
}
@ -4668,7 +4677,7 @@ void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int ic
rcti _rect = *rect;
char *cpoin = NULL;
wt->state(wt, state);
wt->state(wt, state, 0);
wt->draw(&wt->wcol, rect, 0, 0);
UI_fontstyle_set(fstyle);
@ -4748,7 +4757,7 @@ void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int
uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
/* drawing button background */
wt->state(wt, state);
wt->state(wt, state, 0);
wt->draw(&wt->wcol, rect, 0, 0);
/* draw icon in rect above the space reserved for the label */

View File

@ -148,6 +148,8 @@ typedef struct uiWidgetStateColors {
char inner_driven_sel[4];
char inner_overridden[4];
char inner_overridden_sel[4];
char inner_changed[4];
char inner_changed_sel[4];
float blend, pad;
} uiWidgetStateColors;

View File

@ -997,6 +997,16 @@ static void rna_def_userdef_theme_ui_wcol_state(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Overridden Selected", "");
RNA_def_property_update(prop, 0, "rna_userdef_update");
prop = RNA_def_property(srna, "inner_changed", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Changed", "");
RNA_def_property_update(prop, 0, "rna_userdef_update");
prop = RNA_def_property(srna, "inner_changed_sel", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Changed Selected", "");
RNA_def_property_update(prop, 0, "rna_userdef_update");
prop = RNA_def_property(srna, "blend", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_ui_text(prop, "Blend", "");
RNA_def_property_update(prop, 0, "rna_userdef_update");