diff options
Diffstat (limited to 'source/blender/editors/interface')
34 files changed, 8816 insertions, 5094 deletions
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 2c984d64266..cf172441be1 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -24,6 +24,8 @@ set(INC ../../blenkernel ../../blenlib ../../blentranslation + ../../depsgraph + ../../draw ../../gpu ../../imbuf ../../makesdna @@ -52,13 +54,16 @@ set(SRC interface_eyedropper_driver.c interface_handlers.c interface_icons.c + interface_icons_event.c interface_layout.c interface_ops.c interface_panel.c interface_query.c interface_region_color_picker.c + interface_region_hud.c interface_region_menu_pie.c interface_region_menu_popup.c + interface_region_popover.c interface_region_popup.c interface_region_search.c interface_region_tooltip.c diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 82ae4de07af..37c56d454bb 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -41,6 +41,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_userdef_types.h" +#include "DNA_workspace_types.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -50,6 +51,7 @@ #include "BLI_utildefines.h" +#include "BKE_animsys.h" #include "BKE_context.h" #include "BKE_idprop.h" #include "BKE_main.h" @@ -57,27 +59,37 @@ #include "BKE_screen.h" #include "BKE_unit.h" -#include "BIF_gl.h" +#include "GPU_glew.h" +#include "GPU_matrix.h" +#include "GPU_state.h" #include "BLF_api.h" #include "BLT_translation.h" #include "UI_interface.h" +#include "UI_interface_icons.h" #include "IMB_imbuf.h" #include "WM_api.h" #include "WM_types.h" -#include "wm_subwindow.h" +#include "WM_message.h" #include "RNA_access.h" #include "BPY_extern.h" +#include "ED_screen.h" + #include "IMB_colormanagement.h" +#include "DEG_depsgraph_query.h" + #include "interface_intern.h" +/* prototypes. */ +static void ui_but_to_pixelrect(struct rcti *rect, const struct ARegion *ar, struct uiBlock *block, struct uiBut *but); + /* avoid unneeded calls to ui_but_value_get */ #define UI_BUT_VALUE_UNSET DBL_MAX #define UI_GET_BUT_VALUE_INIT(_but, _value) if (_value == DBL_MAX) { (_value) = ui_but_value_get(_but); } (void)0 @@ -205,6 +217,84 @@ void ui_region_to_window(const ARegion *ar, int *x, int *y) *y += ar->winrct.ymin; } +static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block) +{ + int sepr_flex_len = 0; + for (uiBut *but = block->buttons.first; but; but = but->next) { + if (but->type == UI_BTYPE_SEPR_SPACER) { + sepr_flex_len++; + } + } + + if (sepr_flex_len == 0) { + return; + } + + rcti rect; + ui_but_to_pixelrect(&rect, region, block, block->buttons.last); + const float buttons_width = (float)rect.xmax + UI_HEADER_OFFSET; + const float region_width = (float)region->sizex * U.dpi_fac; + + if (region_width <= buttons_width) { + return; + } + + /* We could get rid of this loop if we agree on a max number of spacer */ + int *spacers_pos = alloca(sizeof(*spacers_pos) * (size_t)sepr_flex_len); + int i = 0; + for (uiBut *but = block->buttons.first; but; but = but->next) { + if (but->type == UI_BTYPE_SEPR_SPACER) { + ui_but_to_pixelrect(&rect, region, block, but); + spacers_pos[i] = rect.xmax + UI_HEADER_OFFSET; + i++; + } + } + + const float segment_width = region_width / (float)sepr_flex_len; + float offset = 0, remaining_space = region_width - buttons_width; + i = 0; + for (uiBut *but = block->buttons.first; but; but = but->next) { + BLI_rctf_translate(&but->rect, offset, 0); + if (but->type == UI_BTYPE_SEPR_SPACER) { + /* How much the next block overlap with the current segment */ + int overlap = ( + (i == sepr_flex_len - 1) ? + buttons_width - spacers_pos[i] : + (spacers_pos[i + 1] - spacers_pos[i]) / 2); + int segment_end = segment_width * (i + 1); + int spacer_end = segment_end - overlap; + int spacer_sta = spacers_pos[i] + offset; + if (spacer_end > spacer_sta) { + float step = min_ff(remaining_space, spacer_end - spacer_sta); + remaining_space -= step; + offset += step; + } + i++; + } + } + ui_block_bounds_calc(block); +} + +static void ui_update_window_matrix(const wmWindow *window, const ARegion *region, uiBlock *block) +{ + /* window matrix and aspect */ + if (region && region->visible) { + /* Get projection matrix which includes View2D translation and zoom. */ + GPU_matrix_projection_get(block->winmat); + block->aspect = 2.0f / fabsf(region->winx * block->winmat[0][0]); + } + else { + /* No subwindow created yet, for menus for example, so we use the main + * window instead, since buttons are created there anyway. */ + int width = WM_window_pixels_x(window); + int height = WM_window_pixels_y(window); + rcti winrct = {0, width - 1, 0, height - 1}; + + wmGetProjectionMatrix(block->winmat, &winrct); + block->aspect = 2.0f / fabsf(width * block->winmat[0][0]); + } +} + /** * Popups will add a margin to #ARegion.winrct for shadow, * for interactivity (point-inside tests for eg), we want the winrct without the margin added. @@ -223,7 +313,7 @@ void ui_region_winrct_get_no_margin(const struct ARegion *ar, struct rcti *r_rec /* ******************* block calc ************************* */ -void ui_block_translate(uiBlock *block, int x, int y) +void UI_block_translate(uiBlock *block, int x, int y) { uiBut *but; @@ -243,7 +333,7 @@ static void ui_block_bounds_calc_text(uiBlock *block, float offset) UI_fontstyle_set(&style->widget); for (init_col_bt = bt = block->buttons.first; bt; bt = bt->next) { - if (!ELEM(bt->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) { + if (!ELEM(bt->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER)) { j = BLF_width(style->widget.uifont_id, bt->drawstr, sizeof(bt->drawstr)); if (j > i) @@ -333,7 +423,7 @@ static void ui_block_bounds_calc_centered(wmWindow *window, uiBlock *block) startx = (xmax * 0.5f) - (width * 0.5f); starty = (ymax * 0.5f) - (height * 0.5f); - ui_block_translate(block, startx - block->rect.xmin, starty - block->rect.ymin); + UI_block_translate(block, startx - block->rect.xmin, starty - block->rect.ymin); /* now recompute bounds and safety */ ui_block_bounds_calc(block); @@ -347,7 +437,7 @@ static void ui_block_bounds_calc_centered_pie(uiBlock *block) block->pie_data.pie_center_spawned[1] }; - ui_block_translate(block, xy[0], xy[1]); + UI_block_translate(block, xy[0], xy[1]); /* now recompute bounds and safety */ ui_block_bounds_calc(block); @@ -407,7 +497,7 @@ static void ui_block_bounds_calc_popup( rect_bounds.ymax = ymax - UI_POPUP_MENU_TOP; BLI_rcti_clamp(&rect, &rect_bounds, ofs_dummy); - ui_block_translate(block, rect.xmin - block->rect.xmin, rect.ymin - block->rect.ymin); + UI_block_translate(block, rect.xmin - block->rect.xmin, rect.ymin - block->rect.ymin); /* now recompute bounds and safety */ ui_block_bounds_calc(block); @@ -491,86 +581,6 @@ static int ui_but_calc_float_precision(uiBut *but, double value) return UI_calc_float_precision(prec, value); } -/* ************** LINK LINE DRAWING ************* */ - -/* link line drawing is not part of buttons or theme.. so we stick with it here */ - -static void ui_draw_linkline(uiLinkLine *line, int highlightActiveLines, int dashInactiveLines) -{ - rcti rect; - - if (line->from == NULL || line->to == NULL) return; - - rect.xmin = BLI_rctf_cent_x(&line->from->rect); - rect.ymin = BLI_rctf_cent_y(&line->from->rect); - rect.xmax = BLI_rctf_cent_x(&line->to->rect); - rect.ymax = BLI_rctf_cent_y(&line->to->rect); - - if (dashInactiveLines) - UI_ThemeColor(TH_GRID); - else if (line->flag & UI_SELECT) - glColor3ub(100, 100, 100); - else if (highlightActiveLines && ((line->from->flag & UI_ACTIVE) || (line->to->flag & UI_ACTIVE))) - UI_ThemeColor(TH_TEXT_HI); - else - glColor3ub(0, 0, 0); - - ui_draw_link_bezier(&rect); -} - -static void ui_draw_links(uiBlock *block) -{ - uiBut *but; - uiLinkLine *line; - - /* Draw the gray out lines. Do this first so they appear at the - * bottom of inactive or active lines. - * As we go, remember if we see any active or selected lines. */ - bool found_selectline = false; - bool found_activeline = false; - - for (but = block->buttons.first; but; but = but->next) { - if (but->type == UI_BTYPE_LINK && but->link) { - for (line = but->link->lines.first; line; line = line->next) { - if (!(line->from->flag & UI_ACTIVE) && !(line->to->flag & UI_ACTIVE)) { - if (line->deactive) - ui_draw_linkline(line, 0, true); - } - else - found_activeline = true; - - if ((line->from->flag & UI_SELECT) || (line->to->flag & UI_SELECT)) - found_selectline = true; - } - } - } - - /* Draw the inactive lines (lines with neither button being hovered over) */ - for (but = block->buttons.first; but; but = but->next) { - if (but->type == UI_BTYPE_LINK && but->link) { - for (line = but->link->lines.first; line; line = line->next) { - if (!(line->from->flag & UI_ACTIVE) && !(line->to->flag & UI_ACTIVE)) { - if (!line->deactive) - ui_draw_linkline(line, 0, false); - } - } - } - } - - /* Draw any active lines (lines with either button being hovered over). - * Do this last so they appear on top of inactive and gray out lines. */ - if (found_activeline) { - for (but = block->buttons.first; but; but = but->next) { - if (but->type == UI_BTYPE_LINK && but->link) { - for (line = but->link->lines.first; line; line = line->next) { - if ((line->from->flag & UI_ACTIVE) || (line->to->flag & UI_ACTIVE)) - ui_draw_linkline(line, !found_selectline, false); - } - } - } - } -} - /* ************** BLOCK ENDING FUNCTION ************* */ /* NOTE: if but->poin is allocated memory for every defbut, things fail... */ @@ -613,38 +623,6 @@ uiBut *ui_but_find_new(uiBlock *block_new, const uiBut *but_old) return but_new; } -/* oldbut is being inserted in new block, so we use the lines from new button, and replace button pointers */ -static void ui_but_update_linklines(uiBlock *block, uiBut *oldbut, uiBut *newbut) -{ - uiLinkLine *line; - uiBut *but; - - /* if active button is UI_BTYPE_LINK */ - if (newbut->type == UI_BTYPE_LINK && newbut->link) { - - SWAP(uiLink *, oldbut->link, newbut->link); - - for (line = oldbut->link->lines.first; line; line = line->next) { - if (line->to == newbut) - line->to = oldbut; - if (line->from == newbut) - line->from = oldbut; - } - } - - /* check all other button links */ - for (but = block->buttons.first; but; but = but->next) { - if (but != newbut && but->type == UI_BTYPE_LINK && but->link) { - for (line = but->link->lines.first; line; line = line->next) { - if (line->to == newbut) - line->to = oldbut; - if (line->from == newbut) - line->from = oldbut; - } - } - } -} - /** * \return true when \a but_p is set (only done for active buttons). */ @@ -701,8 +679,6 @@ static bool ui_but_update_from_old_block(const bContext *C, uiBlock *block, uiBu but->selend = oldbut->selend; but->softmin = oldbut->softmin; but->softmax = oldbut->softmax; - but->linkto[0] = oldbut->linkto[0]; - but->linkto[1] = oldbut->linkto[1]; oldbut->active = NULL; #endif @@ -744,8 +720,6 @@ static bool ui_but_update_from_old_block(const bContext *C, uiBlock *block, uiBu oldbut->a1 = but->a1; } - ui_but_update_linklines(block, oldbut, but); - if (!BLI_listbase_is_empty(&block->butstore)) { UI_butstore_register_update(block, oldbut, but); } @@ -968,6 +942,7 @@ void ui_but_add_shortcut(uiBut *but, const char *shortcut_str, const bool do_str MEM_freeN(butstr_orig); but->str = but->strdata; but->flag |= UI_BUT_HAS_SEP_CHAR; + but->drawflag |= UI_BUT_HAS_SHORTCUT; ui_but_update(but); } } @@ -1023,6 +998,41 @@ static bool ui_but_event_operator_string_from_menu( return found; } +static bool ui_but_event_operator_string_from_panel( + const bContext *C, uiBut *but, + char *buf, const size_t buf_len) +{ + /** Nearly exact copy of #ui_but_event_operator_string_from_menu */ + PanelType *pt = UI_but_paneltype_get(but); + BLI_assert(pt != NULL); + + bool found = false; + IDProperty *prop_panel; + + /* annoying, create a property */ + IDPropertyTemplate val = {0}; + prop_panel = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant */ + IDP_AddToGroup(prop_panel, IDP_NewString(pt->idname, "name", sizeof(pt->idname))); + IDP_AddToGroup(prop_panel, IDP_New(IDP_INT, &(IDPropertyTemplate){ .i = pt->space_type, }, "space_type")); + IDP_AddToGroup(prop_panel, IDP_New(IDP_INT, &(IDPropertyTemplate){ .i = pt->region_type, }, "region_type")); + + for (int i = 0; i < 2; i++) { + /* FIXME(campbell): We can't reasonably search all configurations - long term. */ + IDP_ReplaceInGroup(prop_panel, IDP_New(IDP_INT, &(IDPropertyTemplate){ .i = i, }, "keep_open")); + if (WM_key_event_operator_string( + C, "WM_OT_call_panel", WM_OP_INVOKE_REGION_WIN, prop_panel, true, + buf, buf_len)) + { + found = true; + break; + } + } + + IDP_FreeProperty(prop_panel); + MEM_freeN(prop_panel); + return found; +} + static bool ui_but_event_operator_string( const bContext *C, uiBut *but, char *buf, const size_t buf_len) @@ -1035,6 +1045,9 @@ static bool ui_but_event_operator_string( else if (UI_but_menutype_get(but) != NULL) { found = ui_but_event_operator_string_from_menu(C, but, buf, buf_len); } + else if (UI_but_paneltype_get(but) != NULL) { + found = ui_but_event_operator_string_from_panel(C, but, buf, buf_len); + } return found; } @@ -1200,7 +1213,7 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) uiBut *but; char buf[128]; - BLI_assert(block->flag & UI_BLOCK_LOOP); + BLI_assert(block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS)); /* only do it before bounding */ if (block->rect.xmin != block->rect.xmax) @@ -1216,7 +1229,13 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) } else { for (but = block->buttons.first; but; but = but->next) { - if (but->dt != UI_EMBOSS_PULLDOWN) { + if (block->flag & UI_BLOCK_SHOW_SHORTCUT_ALWAYS) { + /* Skip icon-only buttons (as used in the toolbar). */ + if (but->drawstr[0] == '\0') { + continue; + } + } + else if (but->dt != UI_EMBOSS_PULLDOWN) { continue; } @@ -1230,6 +1249,18 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) } } +void ui_but_override_flag(uiBut *but) +{ + const int override_status = RNA_property_static_override_status(&but->rnapoin, but->rnaprop, but->rnaindex); + + if (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN) { + but->flag |= UI_BUT_OVERRIDEN; + } + else { + but->flag &= ~UI_BUT_OVERRIDEN; + } +} + void UI_block_update_from_old(const bContext *C, uiBlock *block) { uiBut *but_old; @@ -1267,6 +1298,7 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x { wmWindow *window = CTX_wm_window(C); Scene *scene = CTX_data_scene(C); + ARegion *region = CTX_wm_region(C); uiBut *but; BLI_assert(block->active); @@ -1294,6 +1326,10 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x } ui_but_anim_flag(but, (scene) ? scene->r.cfra : 0.0f); + ui_but_override_flag(but); + if (UI_but_is_decorator(but)) { + ui_but_anim_decorate_update_from_flag(but); + } } @@ -1302,12 +1338,12 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x if (block->layouts.first) { UI_block_layout_resolve(block, NULL, NULL); } - ui_block_align_calc(block); + ui_block_align_calc(block, CTX_wm_region(C)); if ((block->flag & UI_BLOCK_LOOP) && (block->flag & UI_BLOCK_NUMSELECT)) { ui_menu_block_set_keyaccels(block); /* could use a different flag to check */ } - if (block->flag & UI_BLOCK_LOOP) { + if (block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS)) { ui_menu_block_set_keymaps(C, block); } @@ -1342,6 +1378,8 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x UI_block_align_end(block); } + ui_update_flexible_spacing(region, block); + block->endblock = 1; } @@ -1388,7 +1426,6 @@ void UI_block_draw(const bContext *C, uiBlock *block) ARegion *ar; uiBut *but; rcti rect; - int multisample_enabled; /* get menu region or area region */ ar = CTX_wm_menu(C); @@ -1398,13 +1435,8 @@ void UI_block_draw(const bContext *C, uiBlock *block) if (!block->endblock) UI_block_end(C, block); - /* disable AA, makes widgets too blurry */ - multisample_enabled = glIsEnabled(GL_MULTISAMPLE); - if (multisample_enabled) - glDisable(GL_MULTISAMPLE); - /* we set this only once */ - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); /* scale fonts */ ui_fontscale(&style.paneltitle.points, block->aspect); @@ -1416,22 +1448,26 @@ void UI_block_draw(const bContext *C, uiBlock *block) ui_but_to_pixelrect(&rect, ar, block, NULL); /* pixel space for AA widgets */ - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); + GPU_matrix_push_projection(); + GPU_matrix_push(); + GPU_matrix_identity_set(); wmOrtho2_region_pixelspace(ar); /* back */ if (block->flag & UI_BLOCK_RADIAL) ui_draw_pie_center(block); + else if (block->flag & UI_BLOCK_POPOVER) + ui_draw_popover_back(ar, &style, block, &rect); else if (block->flag & UI_BLOCK_LOOP) ui_draw_menu_back(&style, block, &rect); else if (block->panel) ui_draw_aligned_panel(&style, block, &rect, UI_panel_category_is_visible(ar)); + BLF_batch_draw_begin(); + UI_icon_draw_cache_begin(); + UI_widgetbase_draw_cache_begin(); + /* widgets */ for (but = block->buttons.first; but; but = but->next) { if (!(but->flag & (UI_HIDDEN | UI_SCROLLED))) { @@ -1444,16 +1480,47 @@ void UI_block_draw(const bContext *C, uiBlock *block) } } - /* restore matrix */ - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); + UI_widgetbase_draw_cache_end(); + UI_icon_draw_cache_end(); + BLF_batch_draw_end(); - if (multisample_enabled) - glEnable(GL_MULTISAMPLE); + /* restore matrix */ + GPU_matrix_pop_projection(); + GPU_matrix_pop(); +} + +static void ui_block_message_subscribe(ARegion *ar, struct wmMsgBus *mbus, uiBlock *block) +{ + uiBut *but_prev = NULL; + /* possibly we should keep the region this block is contained in? */ + for (uiBut *but = block->buttons.first; but; but = but->next) { + if (but->rnapoin.type && but->rnaprop) { + /* quick check to avoid adding buttons representing a vector, multiple times. */ + if ((but_prev && + (but_prev->rnaprop == but->rnaprop) && + (but_prev->rnapoin.type == but->rnapoin.type) && + (but_prev->rnapoin.data == but->rnapoin.data) && + (but_prev->rnapoin.id.data == but->rnapoin.id.data)) == false) + { + /* TODO: could make this into utility function. */ + WM_msg_subscribe_rna( + mbus, &but->rnapoin, but->rnaprop, + &(const wmMsgSubscribeValue){ + .owner = ar, + .user_data = ar, + .notify = ED_region_do_msg_notify_tag_redraw, + }, __func__); + but_prev = but; + } + } + } +} - ui_draw_links(block); +void UI_region_message_subscribe(ARegion *ar, struct wmMsgBus *mbus) +{ + for (uiBlock *block = ar->uiblocks.first; block; block = block->next) { + ui_block_message_subscribe(ar, mbus, block); + } } /* ************* EVENTS ************* */ @@ -1511,6 +1578,18 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) if (*value == (double)but->hardmax) is_push = true; } break; + case UI_BTYPE_TAB: + if (but->rnaprop && but->custom_data) { + /* uiBut.custom_data points to data this tab represents (e.g. workspace). + * uiBut.rnapoin/prop store an active value (e.g. active workspace). */ + if (RNA_property_type(but->rnaprop) == PROP_POINTER) { + PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop); + if (active_ptr.data == but->custom_data) { + is_push = true; + } + } + } + break; default: is_push = -1; break; @@ -1537,83 +1616,6 @@ static void ui_but_update_select_flag(uiBut *but, double *value) } } -static uiBut *ui_linkline_find_inlink(uiBlock *block, void *poin) -{ - uiBut *but; - - but = block->buttons.first; - while (but) { - if (but->type == UI_BTYPE_INLINK) { - if (but->poin == poin) return but; - } - but = but->next; - } - return NULL; -} - -static void ui_linkline_add(ListBase *listb, uiBut *but, uiBut *bt, short deactive) -{ - uiLinkLine *line; - - line = MEM_callocN(sizeof(uiLinkLine), "linkline"); - BLI_addtail(listb, line); - line->from = but; - line->to = bt; - line->deactive = deactive; -} - -uiBut *UI_block_links_find_inlink(uiBlock *block, void *poin) -{ - return ui_linkline_find_inlink(block, poin); -} - -void UI_block_links_compose(uiBlock *block) -{ - uiBut *but, *bt; - uiLink *link; - void ***ppoin; - int a; - - but = block->buttons.first; - while (but) { - if (but->type == UI_BTYPE_LINK) { - link = but->link; - - /* for all pointers in the array */ - if (link) { - if (link->ppoin) { - ppoin = link->ppoin; - for (a = 0; a < *(link->totlink); a++) { - bt = ui_linkline_find_inlink(block, (*ppoin)[a]); - if (bt) { - if ((but->flag & UI_BUT_SCA_LINK_GREY) || (bt->flag & UI_BUT_SCA_LINK_GREY)) { - ui_linkline_add(&link->lines, but, bt, true); - } - else { - ui_linkline_add(&link->lines, but, bt, false); - } - - } - } - } - else if (link->poin) { - bt = ui_linkline_find_inlink(block, *link->poin); - if (bt) { - if ((but->flag & UI_BUT_SCA_LINK_GREY) || (bt->flag & UI_BUT_SCA_LINK_GREY)) { - ui_linkline_add(&link->lines, but, bt, true); - } - else { - ui_linkline_add(&link->lines, but, bt, false); - } - } - } - } - } - but = but->next; - } -} - - /* ************************************************ */ void UI_block_lock_set(uiBlock *block, bool val, const char *lockstr) @@ -1630,48 +1632,78 @@ void UI_block_lock_clear(uiBlock *block) block->lockstr = NULL; } -/* *************************************************************** */ - -void ui_linkline_remove(uiLinkLine *line, uiBut *but) -{ - uiLink *link; - int a, b; - - BLI_remlink(&but->link->lines, line); - - link = line->from->link; - - /* are there more pointers allowed? */ - if (link->ppoin) { +/* *********************** data get/set *********************** + * this either works with the pointed to data, or can work with + * an edit override pointer while dragging for example */ - if (*(link->totlink) == 1) { - *(link->totlink) = 0; - MEM_freeN(*(link->ppoin)); - *(link->ppoin) = NULL; - } - else { - b = 0; - for (a = 0; a < (*(link->totlink)); a++) { - if ((*(link->ppoin))[a] != line->to->poin) { - (*(link->ppoin))[b] = (*(link->ppoin))[a]; - b++; - } - } - (*(link->totlink))--; +/* Get PointerRNA which will point to a data inside of an evaluated + * ID datablock. + */ +static PointerRNA ui_but_evaluated_rnapoin_get(uiBut *but) +{ + BLI_assert(but->rnaprop != NULL); + /* TODO(sergey): evil_C sounds.. EVIL! Any clear way to avoid this? */ + PointerRNA rnapoin_eval = but->rnapoin; + /* If there is no animation or drivers, it doesn't matter if we read value + * from evaluated datablock or from original one. + * + * Reading from original one is much faster, since we don't need to do any + * PointerRNA remapping or hash lookup. + */ + if (BKE_animdata_from_id(but->rnapoin.id.data) == NULL) { + return rnapoin_eval; + } + /* Same goes for the properties which can not be animated. */ + if (!RNA_property_animateable(&but->rnapoin, but->rnaprop)) { + return rnapoin_eval; + } + Depsgraph *depsgraph = CTX_data_depsgraph(but->block->evil_C); + /* ID pointer we can always remap, they are inside of depsgraph. */ + rnapoin_eval.id.data = + DEG_get_evaluated_id(depsgraph, rnapoin_eval.id.data); + /* Some of ID datablocks do not have their evaluated copy inside + * of dependency graph. If it's such datablock, no need to worry about + * data pointer. + */ + if (rnapoin_eval.id.data == but->rnapoin.id.data) { + return rnapoin_eval; + } + /* For the data pointer it's getting a bit more involved, since it can + * whether be and ID, or can be a property deep inside of ID. + * + * We start from checking if it's an ID, since that is the less involved + * code path, and probably is executed in most of the cases. + */ + if (but->rnapoin.data == but->rnapoin.id.data) { + rnapoin_eval.data = DEG_get_evaluated_id(depsgraph, rnapoin_eval.data); + return rnapoin_eval; + } + /* We aren't as lucky as we thought we are :( + * + * Since we don't know what the property is, we get it's RNA path + * relative to the original ID, and then we decent down from evaluated + * ID to the same property. + * + * This seems to be most straightforward way to get sub-data pointers + * which can be buried deep inside of ID block. + */ + const char *rna_path = + RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop); + if (rna_path != NULL) { + PointerRNA id_ptr; + RNA_id_pointer_create(rnapoin_eval.id.data, &id_ptr); + if (!RNA_path_resolve_full(&id_ptr, + rna_path, + &rnapoin_eval, + NULL, NULL)) + { + /* TODO(sergey): Anything to do here to recover? */ } + MEM_freeN((void *)rna_path); } - else { - *(link->poin) = NULL; - } - - MEM_freeN(line); - //REDRAW + return rnapoin_eval; } -/* *********************** data get/set *********************** - * this either works with the pointed to data, or can work with - * an edit override pointer while dragging for example */ - /* for buttons pointing to color for example */ void ui_but_v3_get(uiBut *but, float vec[3]) { @@ -1687,16 +1719,18 @@ void ui_but_v3_get(uiBut *but, float vec[3]) zero_v3(vec); + PointerRNA rnapoin_eval = ui_but_evaluated_rnapoin_get(but); + if (RNA_property_type(prop) == PROP_FLOAT) { - int tot = RNA_property_array_length(&but->rnapoin, prop); + int tot = RNA_property_array_length(&rnapoin_eval, prop); BLI_assert(tot > 0); if (tot == 3) { - RNA_property_float_get_array(&but->rnapoin, prop, vec); + RNA_property_float_get_array(&rnapoin_eval, prop, vec); } else { tot = min_ii(tot, 3); for (a = 0; a < tot; a++) { - vec[a] = RNA_property_float_get_index(&but->rnapoin, prop, a); + vec[a] = RNA_property_float_get_index(&rnapoin_eval, prop, a); } } } @@ -1876,27 +1910,29 @@ double ui_but_value_get(uiBut *but) BLI_assert(but->rnaindex != -1); + PointerRNA rnapoin_eval = ui_but_evaluated_rnapoin_get(but); + switch (RNA_property_type(prop)) { case PROP_BOOLEAN: if (RNA_property_array_check(prop)) - value = RNA_property_boolean_get_index(&but->rnapoin, prop, but->rnaindex); + value = RNA_property_boolean_get_index(&rnapoin_eval, prop, but->rnaindex); else - value = RNA_property_boolean_get(&but->rnapoin, prop); + value = RNA_property_boolean_get(&rnapoin_eval, prop); break; case PROP_INT: if (RNA_property_array_check(prop)) - value = RNA_property_int_get_index(&but->rnapoin, prop, but->rnaindex); + value = RNA_property_int_get_index(&rnapoin_eval, prop, but->rnaindex); else - value = RNA_property_int_get(&but->rnapoin, prop); + value = RNA_property_int_get(&rnapoin_eval, prop); break; case PROP_FLOAT: if (RNA_property_array_check(prop)) - value = RNA_property_float_get_index(&but->rnapoin, prop, but->rnaindex); + value = RNA_property_float_get_index(&rnapoin_eval, prop, but->rnaindex); else - value = RNA_property_float_get(&but->rnapoin, prop); + value = RNA_property_float_get(&rnapoin_eval, prop); break; case PROP_ENUM: - value = RNA_property_enum_get(&but->rnapoin, prop); + value = RNA_property_enum_get(&rnapoin_eval, prop); break; default: value = 0.0; @@ -2039,7 +2075,7 @@ static bool ui_but_icon_extra_is_visible_text_clear(const uiBut *but) static bool ui_but_icon_extra_is_visible_search_unlink(const uiBut *but) { - BLI_assert(but->type == UI_BTYPE_SEARCH_MENU); + BLI_assert(ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_TAB)); return ((but->editstr == NULL) && (but->drawstr[0] != '\0') && (but->flag & UI_BUT_VALUE_CLEAR)); @@ -2082,6 +2118,11 @@ uiButExtraIconType ui_but_icon_extra_get(uiBut *but) return UI_BUT_ICONEXTRA_EYEDROPPER; } break; + case UI_BTYPE_TAB: + if (ui_but_icon_extra_is_visible_search_unlink(but)) { + return UI_BUT_ICONEXTRA_CLEAR; + } + break; default: break; } @@ -2196,14 +2237,23 @@ void ui_but_string_get_ex(uiBut *but, char *str, const size_t maxlen, const int *r_use_exp_float = false; } - if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { + if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_TAB)) { PropertyType type; const char *buf = NULL; int buf_len; type = RNA_property_type(but->rnaprop); - if (type == PROP_STRING) { + if ((but->type == UI_BTYPE_TAB) && (but->custom_data)) { + StructRNA *ptr_type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop); + PointerRNA ptr; + + /* uiBut.custom_data points to data this tab represents (e.g. workspace). + * uiBut.rnapoin/prop store an active value (e.g. active workspace). */ + RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr); + buf = RNA_struct_name_get_alloc(&ptr, str, maxlen, &buf_len); + } + else if (type == PROP_STRING) { /* RNA string */ buf = RNA_property_string_get_alloc(&but->rnapoin, but->rnaprop, str, maxlen, &buf_len); } @@ -2239,12 +2289,7 @@ void ui_but_string_get_ex(uiBut *but, char *str, const size_t maxlen, const int MEM_freeN((void *)buf); } } - else if (but->type == UI_BTYPE_TEXT) { - /* string */ - BLI_strncpy(str, but->poin, maxlen); - return; - } - else if (but->type == UI_BTYPE_SEARCH_MENU) { + else if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { /* string */ BLI_strncpy(str, but->poin, maxlen); return; @@ -2443,7 +2488,7 @@ static void ui_but_string_free_internal(uiBut *but) bool ui_but_string_set(bContext *C, uiBut *but, const char *str) { - if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { + if (but->rnaprop && but->rnapoin.data && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { if (RNA_property_editable(&but->rnapoin, but->rnaprop)) { PropertyType type; @@ -2455,17 +2500,15 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str) return true; } else if (type == PROP_POINTER) { - /* RNA pointer */ - PointerRNA ptr, rptr; - PropertyRNA *prop; - if (str[0] == '\0') { RNA_property_pointer_set(&but->rnapoin, but->rnaprop, PointerRNA_NULL); return true; } else { - ptr = but->rnasearchpoin; - prop = but->rnasearchprop; + /* RNA pointer */ + PointerRNA rptr; + PointerRNA ptr = but->rnasearchpoin; + PropertyRNA *prop = but->rnasearchprop; if (prop && RNA_property_collection_lookup_string(&ptr, prop, str, &rptr)) RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr); @@ -2488,10 +2531,32 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str) } } } + else if (but->type == UI_BTYPE_TAB) { + if (but->rnaprop && but->custom_data) { + StructRNA *ptr_type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop); + PointerRNA ptr; + PropertyRNA *prop; + + /* uiBut.custom_data points to data this tab represents (e.g. workspace). + * uiBut.rnapoin/prop store an active value (e.g. active workspace). */ + RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr); + prop = RNA_struct_name_property(ptr_type); + if (RNA_property_editable(&ptr, prop)) { + RNA_property_string_set(&ptr, prop, str); + } + } + } else if (but->type == UI_BTYPE_TEXT) { /* string */ - if (ui_but_is_utf8(but)) BLI_strncpy_utf8(but->poin, str, but->hardmax); - else BLI_strncpy(but->poin, str, but->hardmax); + if (!but->poin || (str[0] == '\0')) { + str = ""; + } + else if (ui_but_is_utf8(but)) { + BLI_strncpy_utf8(but->poin, str, but->hardmax); + } + else { + BLI_strncpy(but->poin, str, but->hardmax); + } return true; } @@ -2675,14 +2740,6 @@ static void ui_set_but_soft_range(uiBut *but) /* ******************* Free ********************/ -static void ui_free_link(uiLink *link) -{ - if (link) { - BLI_freelistN(&link->lines); - MEM_freeN(link); - } -} - /* can be called with C==NULL */ static void ui_but_free(const bContext *C, uiBut *but) { @@ -2703,6 +2760,10 @@ static void ui_but_free(const bContext *C, uiBut *but) MEM_freeN(but->hold_argN); } + if (!but->editstr && but->free_search_arg) { + MEM_SAFE_FREE(but->search_arg); + } + if (but->active) { /* XXX solve later, buttons should be free-able without context ideally, * however they may have open tooltips or popup windows, which need to @@ -2719,7 +2780,6 @@ static void ui_but_free(const bContext *C, uiBut *but) if (but->str && but->str != but->strdata) { MEM_freeN(but->str); } - ui_free_link(but->link); if ((but->type == UI_BTYPE_IMAGE) && but->poin) { IMB_freeImBuf((struct ImBuf *)but->poin); @@ -2761,6 +2821,27 @@ void UI_block_free(const bContext *C, uiBlock *block) MEM_freeN(block); } +void UI_blocklist_update_window_matrix(const bContext *C, const ListBase *lb) +{ + ARegion *region = CTX_wm_region(C); + wmWindow *window = CTX_wm_window(C); + + for (uiBlock *block = lb->first; block; block = block->next) { + if (block->active) { + ui_update_window_matrix(window, region, block); + } + } +} + +void UI_blocklist_draw(const bContext *C, const ListBase *lb) +{ + for (uiBlock *block = lb->first; block; block = block->next) { + if (block->active) { + UI_block_draw(C, block); + } + } +} + /* can be called with C==NULL */ void UI_blocklist_free(const bContext *C, ListBase *lb) { @@ -2817,7 +2898,6 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh uiBlock *block; wmWindow *window; Scene *scn; - int getsizex, getsizey; window = CTX_wm_window(C); scn = CTX_data_scene(C); @@ -2847,33 +2927,18 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh if (region) UI_block_region_set(block, region); - /* window matrix and aspect */ - if (region && region->swinid) { - wm_subwindow_matrix_get(window, region->swinid, block->winmat); - wm_subwindow_size_get(window, region->swinid, &getsizex, &getsizey); - - block->aspect = 2.0f / fabsf(getsizex * block->winmat[0][0]); - } - else { - /* no subwindow created yet, for menus for example, so we - * use the main window instead, since buttons are created - * there anyway */ - wm_subwindow_matrix_get(window, window->screen->mainwin, block->winmat); - wm_subwindow_size_get(window, window->screen->mainwin, &getsizex, &getsizey); + /* Set window matrix and aspect for region and OpenGL state. */ + ui_update_window_matrix(window, region, block); - block->aspect = 2.0f / fabsf(getsizex * block->winmat[0][0]); + /* Tag as popup menu if not created within a region. */ + if (!(region && region->visible)) { block->auto_open = true; - block->flag |= UI_BLOCK_LOOP; /* tag as menu */ + block->flag |= UI_BLOCK_LOOP; } return block; } -uiBlock *UI_block_find_in_region(const char *name, ARegion *ar) -{ - return BLI_findstring(&ar->uiblocks, name, offsetof(uiBlock, name)); -} - void UI_block_emboss_set(uiBlock *block, char dt) { block->dt = dt; @@ -3030,6 +3095,7 @@ void ui_but_update_ex(uiBut *but, const bool validate) case UI_BTYPE_TEXT: case UI_BTYPE_SEARCH_MENU: + case UI_BTYPE_TAB: if (!but->editstr) { char str[UI_MAX_DRAW_STR]; @@ -3153,6 +3219,16 @@ void ui_block_cm_to_display_space_range(uiBlock *block, float *min, float *max) *max = max_fff(UNPACK3(pixel)); } +static uiBut *ui_but_alloc(const eButType type) +{ + switch (type) { + case UI_BTYPE_TAB: + return MEM_callocN(sizeof(uiButTab), "uiButTab"); + default: + return MEM_callocN(sizeof(uiBut), "uiBut"); + } +} + /** * \brief ui_def_but is the function that draws many button types * @@ -3186,7 +3262,7 @@ static uiBut *ui_def_but( } } - but = MEM_callocN(sizeof(uiBut), "uiBut"); + but = ui_but_alloc(type & BUTTYPE); but->type = type & BUTTYPE; but->pointype = type & UI_BUT_POIN_TYPES; @@ -3250,7 +3326,7 @@ static uiBut *ui_def_but( ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_TEXT, UI_BTYPE_LABEL, UI_BTYPE_BLOCK, UI_BTYPE_BUT_MENU, UI_BTYPE_SEARCH_MENU, - UI_BTYPE_PROGRESS_BAR)) + UI_BTYPE_PROGRESS_BAR, UI_BTYPE_POPOVER)) { but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT); } @@ -3273,7 +3349,8 @@ static uiBut *ui_def_but( UI_BTYPE_BLOCK, UI_BTYPE_BUT, UI_BTYPE_LABEL, UI_BTYPE_PULLDOWN, UI_BTYPE_ROUNDBOX, UI_BTYPE_LISTBOX, UI_BTYPE_BUT_MENU, UI_BTYPE_SCROLL, UI_BTYPE_GRIP, - UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE) || + UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, + UI_BTYPE_SEPR_SPACER) || (but->type >= UI_BTYPE_SEARCH_MENU)) { /* pass */ @@ -3441,6 +3518,12 @@ static void ui_def_but_rna__menu(bContext *UNUSED(C), uiLayout *layout, void *bu block->flag |= UI_BLOCK_IS_FLIP; } +static void ui_but_submenu_enable(uiBlock *block, uiBut *but) +{ + but->flag |= UI_BUT_ICON_SUBMENU; + block->content_hints |= BLOCK_CONTAINS_SUBMENU_BUT; +} + /** * ui_def_but_rna_propname and ui_def_but_rna * both take the same args except for propname vs prop, this is done so we can @@ -3576,11 +3659,11 @@ static uiBut *ui_def_but_rna( } if ((type == UI_BTYPE_MENU) && (but->dt == UI_EMBOSS_PULLDOWN)) { - but->flag |= UI_BUT_ICON_SUBMENU; + ui_but_submenu_enable(block, but); } const char *info; - if (!RNA_property_editable_info(&but->rnapoin, prop, &info)) { + if (but->rnapoin.data && !RNA_property_editable_info(&but->rnapoin, prop, &info)) { ui_def_but_rna__disable(but, info); } @@ -3999,19 +4082,6 @@ uiBut *uiDefIconTextButO(uiBlock *block, int type, const char *opname, int opcon /* END Button containing both string label and icon */ -void UI_but_link_set(uiBut *but, void **poin, void ***ppoin, short *tot, int from, int to) -{ - uiLink *link; - - link = but->link = MEM_callocN(sizeof(uiLink), "new uilink"); - - link->poin = poin; - link->ppoin = ppoin; - link->totlink = tot; - link->fromcode = from; - link->tocode = to; -} - /* cruft to make uiBlock and uiBut private */ int UI_blocklist_min_y_get(ListBase *lb) @@ -4328,7 +4398,7 @@ uiBut *uiDefIconTextMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, in ui_def_but_icon(but, icon, UI_HAS_ICON); but->drawflag |= UI_BUT_ICON_LEFT; - but->flag |= UI_BUT_ICON_SUBMENU; + ui_but_submenu_enable(block, but); but->menu_create_func = func; ui_but_update(but); @@ -4360,7 +4430,7 @@ uiBut *uiDefIconTextBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, but->drawflag |= UI_BUT_ICON_LEFT; } but->flag |= UI_HAS_ICON; - but->flag |= UI_BUT_ICON_SUBMENU; + ui_but_submenu_enable(block, but); but->block_create_func = func; ui_but_update(but); @@ -4479,7 +4549,7 @@ static void operator_enum_search_cb(const struct bContext *C, void *but, const c for (item = item_array; item->identifier; item++) { /* note: need to give the index rather than the identifier because the enum can be freed */ if (BLI_strcasestr(item->name, str)) { - if (false == UI_search_item_add(items, item->name, SET_INT_IN_POINTER(item->value), 0)) + if (false == UI_search_item_add(items, item->name, SET_INT_IN_POINTER(item->value), item->icon)) break; } } @@ -4610,7 +4680,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) tmp = BLI_strdup(RNA_property_identifier(but->rnaprop)); } else if (type == BUT_GET_RNASTRUCT_IDENTIFIER) { - if (but->rnaprop) + if (but->rnaprop && but->rnapoin.data) tmp = BLI_strdup(RNA_struct_identifier(but->rnapoin.type)); else if (but->optype) tmp = BLI_strdup(but->optype->idname); @@ -4687,6 +4757,9 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) PointerRNA *opptr = UI_but_operator_ptr_get(but); wmOperatorType *ot = but->optype; + /* so the context is passed to itemf functions */ + WM_operator_properties_sanitize(opptr, false); + /* if the default property of the operator is enum and it is set, * fetch the tooltip of the selected value so that "Snap" and "Mirror" * operator menus in the Anim Editors will show tooltips for the different diff --git a/source/blender/editors/interface/interface_align.c b/source/blender/editors/interface/interface_align.c index 36b0b938b5a..c945746c2df 100644 --- a/source/blender/editors/interface/interface_align.c +++ b/source/blender/editors/interface/interface_align.c @@ -110,9 +110,9 @@ enum { bool ui_but_can_align(const uiBut *but) { - return ( - !ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_CHECKBOX, UI_BTYPE_CHECKBOX_N, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE) && - (BLI_rctf_size_x(&but->rect) > 0.0f) && (BLI_rctf_size_y(&but->rect) > 0.0f)); + const bool btype_can_align = !ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_CHECKBOX, UI_BTYPE_CHECKBOX_N, + UI_BTYPE_TAB, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER); + return (btype_can_align && (BLI_rctf_size_x(&but->rect) > 0.0f) && (BLI_rctf_size_y(&but->rect) > 0.0f)); } /** @@ -320,13 +320,43 @@ static int ui_block_align_butal_cmp(const void *a, const void *b) return 0; } +static void ui_block_align_but_to_region(uiBut *but, const ARegion *region) +{ + rctf *rect = &but->rect; + const float but_width = BLI_rctf_size_x(rect); + const float but_height = BLI_rctf_size_y(rect); + const float px = U.pixelsize; + + switch (but->drawflag & UI_BUT_ALIGN) { + case UI_BUT_ALIGN_TOP: + rect->ymax = region->winy + px; + rect->ymin = but->rect.ymax - but_height; + break; + case UI_BUT_ALIGN_DOWN: + rect->ymin = -px; + rect->ymax = rect->ymin + but_height; + break; + case UI_BUT_ALIGN_LEFT: + rect->xmin = -px; + rect->xmax = rect->xmin + but_width; + break; + case UI_BUT_ALIGN_RIGHT: + rect->xmax = region->winx + px; + rect->xmin = rect->xmax - but_width; + break; + default: + BLI_assert(0); + break; + } +} + /** * Compute the alignment of all 'align groups' of buttons in given block. * * This is using an order-independent algorithm, i.e. alignment of buttons should be OK regardless of order in which * they are added to the block. */ -void ui_block_align_calc(uiBlock *block) +void ui_block_align_calc(uiBlock *block, const ARegion *region) { uiBut *but; int num_buttons = 0; @@ -338,10 +368,17 @@ void ui_block_align_calc(uiBlock *block) int side; int i, j; - /* First loop: we count number of buttons belonging to an align group, and clear their align flag. */ + /* First loop: we count number of buttons belonging to an align group, and clear their align flag. + * Tabs get some special treatment here, they get aligned to region border. */ for (but = block->buttons.first; but; but = but->next) { - /* Clear old align flags. */ - but->drawflag &= ~UI_BUT_ALIGN_ALL; + /* special case: tabs need to be aligned to a region border, drawflag tells which one */ + if (but->type == UI_BTYPE_TAB) { + ui_block_align_but_to_region(but, region); + } + else { + /* Clear old align flags. */ + but->drawflag &= ~UI_BUT_ALIGN_ALL; + } if (but->alignnr != 0) { num_buttons++; @@ -462,7 +499,8 @@ void ui_block_align_calc(uiBlock *block) bool ui_but_can_align(uiBut *but) { - return !ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_CHECKBOX, UI_BTYPE_CHECKBOX_N, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE); + return !ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_CHECKBOX, UI_BTYPE_CHECKBOX_N, + UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER); } static bool buts_are_horiz(uiBut *but1, uiBut *but2) diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 10a23792e8f..cda70d405ad 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -39,12 +39,14 @@ #include "BLI_utildefines.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_nla.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + #include "ED_keyframing.h" #include "UI_interface.h" @@ -101,6 +103,30 @@ void ui_but_anim_flag(uiBut *but, float cfra) } } +void ui_but_anim_decorate_update_from_flag(uiBut *but) +{ + BLI_assert(UI_but_is_decorator(but) && but->prev); + int flag = but->prev->flag; + if (flag & UI_BUT_DRIVEN) { + but->icon = ICON_AUTO; + } + else if (flag & UI_BUT_ANIMATED_KEY) { + but->icon = ICON_SPACE2; + } + else if (flag & UI_BUT_ANIMATED) { + but->icon = ICON_SPACE3; + } + else if (flag & UI_BUT_OVERRIDEN) { + but->icon = ICON_LIBRARY_DATA_OVERRIDE; + } + else { + but->icon = ICON_DOT; + } + + const int flag_copy = (UI_BUT_DISABLED | UI_BUT_INACTIVE); + but->flag = (but->flag & ~flag_copy) | (flag & flag_copy); +} + /** * \a str can be NULL to only perform check if \a but has an expression at all. * \return if button has an expression. @@ -212,7 +238,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str) /* updates */ driver->flag |= DRIVER_FLAG_RECOMPILE; - DAG_relations_tag_update(CTX_data_main(C)); + DEG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, NULL); ok = true; } @@ -240,10 +266,11 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) if (special) { /* NLA Strip property */ if (IS_AUTOKEY_ON(scene)) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); ReportList *reports = CTX_wm_reports(C); ToolSettings *ts = scene->toolsettings; - insert_keyframe_direct(reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, 0); + insert_keyframe_direct(depsgraph, reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, 0); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } } @@ -252,10 +279,11 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) * making it easier to set up corrective drivers */ if (IS_AUTOKEY_ON(scene)) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); ReportList *reports = CTX_wm_reports(C); ToolSettings *ts = scene->toolsettings; - insert_keyframe_direct(reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, INSERTKEY_DRIVER); + insert_keyframe_direct(depsgraph, reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, INSERTKEY_DRIVER); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } } @@ -264,6 +292,7 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) /* TODO: this should probably respect the keyingset only option for anim */ if (autokeyframe_cfra_can_key(scene, id)) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); ReportList *reports = CTX_wm_reports(C); ToolSettings *ts = scene->toolsettings; short flag = ANIM_get_keyframing_flags(scene, 1); @@ -274,7 +303,8 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) * because a button may control all items of an array at once. * E.g., color wheels (see T42567). */ BLI_assert((fcu->array_index == but->rnaindex) || (but->rnaindex == -1)); - insert_keyframe(bmain, reports, id, action, ((fcu->grp) ? (fcu->grp->name) : (NULL)), + insert_keyframe(bmain, depsgraph, reports, id, action, + ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, but->rnaindex, cfra, ts->keyframe_type, flag); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -293,3 +323,38 @@ void ui_but_anim_paste_driver(bContext *C) /* this operator calls UI_context_active_but_prop_get */ WM_operator_name_call(C, "ANIM_OT_paste_driver_button", WM_OP_INVOKE_DEFAULT, NULL); } + +void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy)) +{ + wmWindowManager *wm = CTX_wm_manager(C); + uiBut *but = arg_but; + but = but->prev; + + /* FIXME(campbell), swapping active pointer is weak. */ + SWAP(struct uiHandleButtonData *, but->active, but->next->active); + wm->op_undo_depth++; + + if (but->flag & UI_BUT_DRIVEN) { + /* pass */ + /* TODO: report? */ + } + else if (but->flag & UI_BUT_ANIMATED_KEY) { + PointerRNA props_ptr; + wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_delete_button", false); + WM_operator_properties_create_ptr(&props_ptr, ot); + RNA_boolean_set(&props_ptr, "all", but->rnaindex == -1); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_properties_free(&props_ptr); + } + else { + PointerRNA props_ptr; + wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_insert_button", false); + WM_operator_properties_create_ptr(&props_ptr, ot); + RNA_boolean_set(&props_ptr, "all", but->rnaindex == -1); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_properties_free(&props_ptr); + } + + SWAP(struct uiHandleButtonData *, but->active, but->next->active); + wm->op_undo_depth--; +} diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 0306139bac0..240649c8ab0 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -24,6 +24,8 @@ * Generic context popup menus. */ +#include <string.h> + #include "MEM_guardedalloc.h" #include "DNA_scene_types.h" @@ -214,6 +216,98 @@ static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) UI_popup_block_ex(C, menu_add_shortcut, NULL, menu_add_shortcut_cancel, but, NULL); } +static bool ui_but_is_user_menu_compatible(bContext *C, uiBut *but) +{ + return (but->optype || + (but->rnaprop && + (RNA_property_type(but->rnaprop) == PROP_BOOLEAN) && + (WM_context_member_from_ptr(C, &but->rnapoin) != NULL)) || + UI_but_menutype_get(but)); +} + +static bUserMenuItem *ui_but_user_menu_find(bContext *C, uiBut *but, bUserMenu *um) +{ + MenuType *mt = NULL; + if (but->optype) { + IDProperty *prop = (but->opptr) ? but->opptr->data : NULL; + return (bUserMenuItem *)ED_screen_user_menu_item_find_operator( + &um->items, but->optype, prop, but->opcontext); + } + else if (but->rnaprop) { + const char *member_id = WM_context_member_from_ptr(C, &but->rnapoin); + const char *prop_id = RNA_property_identifier(but->rnaprop); + return (bUserMenuItem *)ED_screen_user_menu_item_find_prop( + &um->items, member_id, prop_id, but->rnaindex); + } + else if ((mt = UI_but_menutype_get(but))) { + return (bUserMenuItem *)ED_screen_user_menu_item_find_menu( + &um->items, mt); + } + else { + return NULL; + } +} + +static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um) +{ + BLI_assert(ui_but_is_user_menu_compatible(C, but)); + + char drawstr[sizeof(but->drawstr)]; + STRNCPY(drawstr, but->drawstr); + if (but->flag & UI_BUT_HAS_SEP_CHAR) { + char *sep = strrchr(drawstr, UI_SEP_CHAR); + if (sep) { + *sep = '\0'; + } + } + + MenuType *mt = NULL; + if (but->optype) { + ED_screen_user_menu_item_add_operator( + &um->items, drawstr, + but->optype, but->opptr ? but->opptr->data : NULL, but->opcontext); + } + else if (but->rnaprop) { + /* Note: 'member_id' may be a path. */ + const char *member_id = WM_context_member_from_ptr(C, &but->rnapoin); + const char *data_path = RNA_path_from_ID_to_struct(&but->rnapoin); + const char *member_id_data_path = member_id; + if (data_path) { + member_id_data_path = BLI_sprintfN("%s.%s", member_id, data_path); + } + const char *prop_id = RNA_property_identifier(but->rnaprop); + /* Note, ignore 'drawstr', use property idname always. */ + ED_screen_user_menu_item_add_prop( + &um->items, "", + member_id_data_path, prop_id, but->rnaindex); + if (data_path) { + MEM_freeN((void *)data_path); + } + if (member_id != member_id_data_path) { + MEM_freeN((void *)member_id_data_path); + } + } + else if ((mt = UI_but_menutype_get(but))) { + ED_screen_user_menu_item_add_menu( + &um->items, drawstr, + mt); + } +} + +static void popup_user_menu_add_or_replace_func(bContext *C, void *arg1, void *UNUSED(arg2)) +{ + uiBut *but = arg1; + bUserMenu *um = ED_screen_user_menu_ensure(C); + ui_but_user_menu_add(C, but, um); +} + +static void popup_user_menu_remove_func(bContext *UNUSED(C), void *arg1, void *arg2) +{ + bUserMenu *um = arg1; + bUserMenuItem *umi = arg2; + ED_screen_user_menu_item_remove(&um->items, umi); +} + static void ui_but_menu_add_path_operators(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop) { const PropertySubType subtype = RNA_property_subtype(prop); @@ -285,6 +379,9 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) const bool is_array = RNA_property_array_length(&but->rnapoin, but->rnaprop) != 0; const bool is_array_component = (is_array && but->rnaindex != -1); + const int override_status = RNA_property_static_override_status(ptr, prop, -1); + const bool is_overridable = (override_status & RNA_OVERRIDE_STATUS_OVERRIDABLE) != 0; + /* Keyframes */ if (but->flag & UI_BUT_ANIMATED_KEY) { /* Set the (button_pointer, button_prop) and pointer data for Python access to the hovered ui element. */ @@ -378,6 +475,12 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Paste Driver"), ICON_NONE, "ANIM_OT_paste_driver_button"); } + + uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Edit Driver"), + ICON_DRIVER, "ANIM_OT_driver_button_edit"); + + uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Open Drivers Editor"), + ICON_NONE, "SCREEN_OT_drivers_editor_show"); } else if (but->flag & (UI_BUT_ANIMATED_KEY | UI_BUT_ANIMATED)) { /* pass */ @@ -385,23 +488,16 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) else if (is_anim) { uiItemS(layout); - if (is_array_component) { - uiItemMenuEnumO( - layout, C, "ANIM_OT_driver_button_add", "mapping_type", - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add Drivers"), - ICON_DRIVER); - } - else { - uiItemMenuEnumO( - layout, C, "ANIM_OT_driver_button_add", "mapping_type", - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add Driver"), - ICON_DRIVER); - } + uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add Driver"), + ICON_DRIVER, "ANIM_OT_driver_button_add"); if (ANIM_driver_can_paste()) { uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Paste Driver"), ICON_NONE, "ANIM_OT_paste_driver_button"); } + + uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Open Drivers Editor"), + ICON_NONE, "SCREEN_OT_drivers_editor_show"); } /* Keying Sets */ @@ -428,6 +524,65 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) } } + if (is_overridable) { + wmOperatorType *ot; + PointerRNA op_ptr; + /* Override Operators */ + uiItemS(layout); + + if (but->flag & UI_BUT_OVERRIDEN) { + if (is_array_component) { +#if 0 /* Disabled for now. */ + ot = WM_operatortype_find("UI_OT_override_type_set_button", false); + uiItemFullO_ptr( + layout, ot, "Overrides Type", ICON_NONE, + NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_boolean_set(&op_ptr, "all", true); + uiItemFullO_ptr( + layout, ot, "Single Override Type", ICON_NONE, + NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_boolean_set(&op_ptr, "all", false); +#endif + uiItemBooleanO( + layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove Overrides"), + ICON_X, "UI_OT_override_remove_button", "all", true); + uiItemBooleanO( + layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove Single Override"), + ICON_X, "UI_OT_override_remove_button", "all", false); + } + else { +#if 0 /* Disabled for now. */ + uiItemFullO( + layout, "UI_OT_override_type_set_button", "Override Type", ICON_NONE, + NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_boolean_set(&op_ptr, "all", false); +#endif + uiItemBooleanO( + layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove Override"), + ICON_X, "UI_OT_override_remove_button", "all", true); + } + } + else { + if (is_array_component) { + ot = WM_operatortype_find("UI_OT_override_type_set_button", false); + uiItemFullO_ptr( + layout, ot, "Define Overrides", ICON_NONE, + NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_boolean_set(&op_ptr, "all", true); + uiItemFullO_ptr( + layout, ot, "Define Single Override", ICON_NONE, + NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_boolean_set(&op_ptr, "all", false); + } + else { + uiItemFullO( + layout, "UI_OT_override_type_set_button", "Define Override", ICON_NONE, + NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_boolean_set(&op_ptr, "all", false); + } + } + } + uiItemS(layout); /* Property Operators */ @@ -532,6 +687,33 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) uiItemS(layout); } + /* Favorites Menu */ + if (ui_but_is_user_menu_compatible(C, but)) { + uiBlock *block = uiLayoutGetBlock(layout); + const int w = uiLayoutGetWidth(layout); + uiBut *but2; + + but2 = uiDefIconTextBut( + block, UI_BTYPE_BUT, 0, ICON_MENU_PANEL, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add to Favorites Menu"), + 0, 0, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, + "Add to a user defined context menu (stored in the user preferences)"); + UI_but_func_set(but2, popup_user_menu_add_or_replace_func, but, NULL); + + bUserMenu *um = ED_screen_user_menu_find(C); + if (um) { + bUserMenuItem *umi = ui_but_user_menu_find(C, but, um); + if (umi != NULL) { + but2 = uiDefIconTextBut( + block, UI_BTYPE_BUT, 0, ICON_CANCEL, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove from Favorites Menu"), + 0, 0, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, ""); + UI_but_func_set(but2, popup_user_menu_remove_func, um, umi); + } + } + uiItemS(layout); + } + /* Show header tools for header buttons. */ if (ui_block_is_menu(but->block) == false) { ARegion *ar = CTX_wm_region(C); diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 50cd1c544c4..6a304a8150e 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -50,19 +50,23 @@ #include "IMB_imbuf_types.h" #include "IMB_colormanagement.h" -#include "BIF_gl.h" #include "BIF_glutil.h" #include "BLF_api.h" -#include "GPU_draw.h" -#include "GPU_basic_shader.h" +#include "GPU_batch.h" +#include "GPU_batch_presets.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" #include "UI_interface.h" /* own include */ #include "interface_intern.h" + static int roundboxtype = UI_CNR_ALL; void UI_draw_roundbox_corner_set(int type) @@ -71,340 +75,564 @@ void UI_draw_roundbox_corner_set(int type) * if this is undone, it's not that big a deal, only makes curves edges * square for the */ roundboxtype = type; - } +#if 0 /* unused */ int UI_draw_roundbox_corner_get(void) { return roundboxtype; } +#endif + +void UI_draw_roundbox_3ubAlpha(bool filled, float minx, float miny, float maxx, float maxy, float rad, const unsigned char col[3], unsigned char alpha) +{ + float colv[4]; + colv[0] = ((float)col[0]) / 255; + colv[1] = ((float)col[1]) / 255; + colv[2] = ((float)col[2]) / 255; + colv[3] = ((float)alpha) / 255; + UI_draw_roundbox_4fv(filled, minx, miny, maxx, maxy, rad, colv); +} + +void UI_draw_roundbox_3fvAlpha(bool filled, float minx, float miny, float maxx, float maxy, float rad, const float col[3], float alpha) +{ + float colv[4]; + colv[0] = col[0]; + colv[1] = col[1]; + colv[2] = col[2]; + colv[3] = alpha; + UI_draw_roundbox_4fv(filled, minx, miny, maxx, maxy, rad, colv); +} -void UI_draw_roundbox_gl_mode(int mode, float minx, float miny, float maxx, float maxy, float rad) +void UI_draw_roundbox_aa(bool filled, float minx, float miny, float maxx, float maxy, float rad, const float color[4]) { + uiWidgetBaseParameters widget_params = { + .recti.xmin = minx, .recti.ymin = miny, + .recti.xmax = maxx, .recti.ymax = maxy, + .radi = rad, + .round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f, + .round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f, + .round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f, + .round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f, + .color_inner1[0] = color[0], .color_inner2[0] = color[0], + .color_inner1[1] = color[1], .color_inner2[1] = color[1], + .color_inner1[2] = color[2], .color_inner2[2] = color[2], + .color_inner1[3] = color[3], .color_inner2[3] = color[3], + .alpha_discard = 1.0f, + }; + + GPU_blend(true); + + if (filled) { + /* plain antialiased filled box */ + widget_params.color_inner1[3] *= 0.125f; + widget_params.color_inner2[3] *= 0.125f; + + /* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space. + * If it has been scaled, then it's no longer valid. */ + GPUBatch *batch = ui_batch_roundbox_get(filled, true); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); + GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); + GPU_batch_draw(batch); + } + else { + /* plain antialiased unfilled box */ + GPU_line_smooth(true); + + GPUBatch *batch = ui_batch_roundbox_get(filled, false); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); + GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); + GPU_batch_draw(batch); + + GPU_line_smooth(false); + } + + GPU_blend(false); +} + +void UI_draw_roundbox_4fv(bool filled, float minx, float miny, float maxx, float maxy, float rad, const float col[4]) +{ +#if 0 float vec[7][2] = { {0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293}, {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}, }; int a; + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + /* mult */ for (a = 0; a < 7; a++) { mul_v2_fl(vec[a], rad); } - glBegin(mode); + uint vert_len = 0; + vert_len += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 9 : 1; + vert_len += (roundboxtype & UI_CNR_TOP_RIGHT) ? 9 : 1; + vert_len += (roundboxtype & UI_CNR_TOP_LEFT) ? 9 : 1; + vert_len += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 9 : 1; + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4fv(col); + + immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_LOOP, vert_len); /* start with corner right-bottom */ if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { - glVertex2f(maxx - rad, miny); + immVertex2f(pos, maxx - rad, miny); for (a = 0; a < 7; a++) { - glVertex2f(maxx - rad + vec[a][0], miny + vec[a][1]); + immVertex2f(pos, maxx - rad + vec[a][0], miny + vec[a][1]); } - glVertex2f(maxx, miny + rad); + immVertex2f(pos, maxx, miny + rad); } else { - glVertex2f(maxx, miny); + immVertex2f(pos, maxx, miny); } /* corner right-top */ if (roundboxtype & UI_CNR_TOP_RIGHT) { - glVertex2f(maxx, maxy - rad); + immVertex2f(pos, maxx, maxy - rad); for (a = 0; a < 7; a++) { - glVertex2f(maxx - vec[a][1], maxy - rad + vec[a][0]); + immVertex2f(pos, maxx - vec[a][1], maxy - rad + vec[a][0]); } - glVertex2f(maxx - rad, maxy); + immVertex2f(pos, maxx - rad, maxy); } else { - glVertex2f(maxx, maxy); + immVertex2f(pos, maxx, maxy); } /* corner left-top */ if (roundboxtype & UI_CNR_TOP_LEFT) { - glVertex2f(minx + rad, maxy); + immVertex2f(pos, minx + rad, maxy); for (a = 0; a < 7; a++) { - glVertex2f(minx + rad - vec[a][0], maxy - vec[a][1]); + immVertex2f(pos, minx + rad - vec[a][0], maxy - vec[a][1]); } - glVertex2f(minx, maxy - rad); + immVertex2f(pos, minx, maxy - rad); } else { - glVertex2f(minx, maxy); + immVertex2f(pos, minx, maxy); } /* corner left-bottom */ if (roundboxtype & UI_CNR_BOTTOM_LEFT) { - glVertex2f(minx, miny + rad); + immVertex2f(pos, minx, miny + rad); for (a = 0; a < 7; a++) { - glVertex2f(minx + vec[a][1], miny + rad - vec[a][0]); + immVertex2f(pos, minx + vec[a][1], miny + rad - vec[a][0]); } - glVertex2f(minx + rad, miny); + immVertex2f(pos, minx + rad, miny); } else { - glVertex2f(minx, miny); + immVertex2f(pos, minx, miny); } - glEnd(); + immEnd(); + immUnbindProgram(); +#endif + + uiWidgetBaseParameters widget_params = { + .recti.xmin = minx, .recti.ymin = miny, + .recti.xmax = maxx, .recti.ymax = maxy, + .radi = rad, + .round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f, + .round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f, + .round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f, + .round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f, + .color_inner1[0] = col[0], .color_inner2[0] = col[0], + .color_inner1[1] = col[1], .color_inner2[1] = col[1], + .color_inner1[2] = col[2], .color_inner2[2] = col[2], + .color_inner1[3] = col[3], .color_inner2[3] = col[3], + .alpha_discard = 1.0f, + }; + + GPUBatch *batch = ui_batch_roundbox_get(filled, false); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); + GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); + GPU_batch_draw(batch); } -static void round_box_shade_col(const float col1[3], float const col2[3], const float fac) +#if 0 +static void round_box_shade_col(unsigned attrib, const float col1[3], float const col2[3], const float fac) { - float col[3] = { + float col[4] = { fac * col1[0] + (1.0f - fac) * col2[0], fac * col1[1] + (1.0f - fac) * col2[1], - fac * col1[2] + (1.0f - fac) * col2[2] + fac * col1[2] + (1.0f - fac) * col2[2], + 1.0f }; - glColor3fv(col); + immAttrib4fv(attrib, col); } +#endif /* linear horizontal shade within button or in outline */ /* view2d scrollers use it */ void UI_draw_roundbox_shade_x( - int mode, float minx, float miny, float maxx, float maxy, - float rad, float shadetop, float shadedown) + bool filled, float minx, float miny, float maxx, float maxy, + float rad, float shadetop, float shadedown, const float col[4]) { +#if 0 float vec[7][2] = { {0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293}, - {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}}; + {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}, + }; const float div = maxy - miny; const float idiv = 1.0f / div; - float coltop[3], coldown[3], color[4]; + float coltop[3], coldown[3]; + int vert_count = 0; int a; + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); + /* mult */ for (a = 0; a < 7; a++) { mul_v2_fl(vec[a], rad); } - /* get current color, needs to be outside of glBegin/End */ - glGetFloatv(GL_CURRENT_COLOR, color); /* 'shade' defines strength of shading */ - coltop[0] = min_ff(1.0f, color[0] + shadetop); - coltop[1] = min_ff(1.0f, color[1] + shadetop); - coltop[2] = min_ff(1.0f, color[2] + shadetop); - coldown[0] = max_ff(0.0f, color[0] + shadedown); - coldown[1] = max_ff(0.0f, color[1] + shadedown); - coldown[2] = max_ff(0.0f, color[2] + shadedown); + coltop[0] = min_ff(1.0f, col[0] + shadetop); + coltop[1] = min_ff(1.0f, col[1] + shadetop); + coltop[2] = min_ff(1.0f, col[2] + shadetop); + coldown[0] = max_ff(0.0f, col[0] + shadedown); + coldown[1] = max_ff(0.0f, col[1] + shadedown); + coldown[2] = max_ff(0.0f, col[2] + shadedown); + + vert_count += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 9 : 1; + vert_count += (roundboxtype & UI_CNR_TOP_RIGHT) ? 9 : 1; + vert_count += (roundboxtype & UI_CNR_TOP_LEFT) ? 9 : 1; + vert_count += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 9 : 1; - glBegin(mode); + immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_LOOP, vert_count); /* start with corner right-bottom */ if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { - round_box_shade_col(coltop, coldown, 0.0); - glVertex2f(maxx - rad, miny); + round_box_shade_col(color, coltop, coldown, 0.0); + immVertex2f(pos, maxx - rad, miny); for (a = 0; a < 7; a++) { - round_box_shade_col(coltop, coldown, vec[a][1] * idiv); - glVertex2f(maxx - rad + vec[a][0], miny + vec[a][1]); + round_box_shade_col(color, coltop, coldown, vec[a][1] * idiv); + immVertex2f(pos, maxx - rad + vec[a][0], miny + vec[a][1]); } - round_box_shade_col(coltop, coldown, rad * idiv); - glVertex2f(maxx, miny + rad); + round_box_shade_col(color, coltop, coldown, rad * idiv); + immVertex2f(pos, maxx, miny + rad); } else { - round_box_shade_col(coltop, coldown, 0.0); - glVertex2f(maxx, miny); + round_box_shade_col(color, coltop, coldown, 0.0); + immVertex2f(pos, maxx, miny); } /* corner right-top */ if (roundboxtype & UI_CNR_TOP_RIGHT) { - round_box_shade_col(coltop, coldown, (div - rad) * idiv); - glVertex2f(maxx, maxy - rad); + round_box_shade_col(color, coltop, coldown, (div - rad) * idiv); + immVertex2f(pos, maxx, maxy - rad); for (a = 0; a < 7; a++) { - round_box_shade_col(coltop, coldown, (div - rad + vec[a][1]) * idiv); - glVertex2f(maxx - vec[a][1], maxy - rad + vec[a][0]); + round_box_shade_col(color, coltop, coldown, (div - rad + vec[a][1]) * idiv); + immVertex2f(pos, maxx - vec[a][1], maxy - rad + vec[a][0]); } - round_box_shade_col(coltop, coldown, 1.0); - glVertex2f(maxx - rad, maxy); + round_box_shade_col(color, coltop, coldown, 1.0); + immVertex2f(pos, maxx - rad, maxy); } else { - round_box_shade_col(coltop, coldown, 1.0); - glVertex2f(maxx, maxy); + round_box_shade_col(color, coltop, coldown, 1.0); + immVertex2f(pos, maxx, maxy); } /* corner left-top */ if (roundboxtype & UI_CNR_TOP_LEFT) { - round_box_shade_col(coltop, coldown, 1.0); - glVertex2f(minx + rad, maxy); + round_box_shade_col(color, coltop, coldown, 1.0); + immVertex2f(pos, minx + rad, maxy); for (a = 0; a < 7; a++) { - round_box_shade_col(coltop, coldown, (div - vec[a][1]) * idiv); - glVertex2f(minx + rad - vec[a][0], maxy - vec[a][1]); + round_box_shade_col(color, coltop, coldown, (div - vec[a][1]) * idiv); + immVertex2f(pos, minx + rad - vec[a][0], maxy - vec[a][1]); } - round_box_shade_col(coltop, coldown, (div - rad) * idiv); - glVertex2f(minx, maxy - rad); + round_box_shade_col(color, coltop, coldown, (div - rad) * idiv); + immVertex2f(pos, minx, maxy - rad); } else { - round_box_shade_col(coltop, coldown, 1.0); - glVertex2f(minx, maxy); + round_box_shade_col(color, coltop, coldown, 1.0); + immVertex2f(pos, minx, maxy); } /* corner left-bottom */ if (roundboxtype & UI_CNR_BOTTOM_LEFT) { - round_box_shade_col(coltop, coldown, rad * idiv); - glVertex2f(minx, miny + rad); + round_box_shade_col(color, coltop, coldown, rad * idiv); + immVertex2f(pos, minx, miny + rad); for (a = 0; a < 7; a++) { - round_box_shade_col(coltop, coldown, (rad - vec[a][1]) * idiv); - glVertex2f(minx + vec[a][1], miny + rad - vec[a][0]); + round_box_shade_col(color, coltop, coldown, (rad - vec[a][1]) * idiv); + immVertex2f(pos, minx + vec[a][1], miny + rad - vec[a][0]); } - round_box_shade_col(coltop, coldown, 0.0); - glVertex2f(minx + rad, miny); + round_box_shade_col(color, coltop, coldown, 0.0); + immVertex2f(pos, minx + rad, miny); } else { - round_box_shade_col(coltop, coldown, 0.0); - glVertex2f(minx, miny); + round_box_shade_col(color, coltop, coldown, 0.0); + immVertex2f(pos, minx, miny); } - glEnd(); + immEnd(); + immUnbindProgram(); +#endif + + uiWidgetBaseParameters widget_params = { + .recti.xmin = minx, .recti.ymin = miny, + .recti.xmax = maxx, .recti.ymax = maxy, + .radi = rad, + .round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f, + .round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f, + .round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f, + .round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f, + .color_inner1[0] = min_ff(1.0f, col[0] + shadetop), + .color_inner2[0] = max_ff(0.0f, col[0] + shadedown), + .color_inner1[1] = min_ff(1.0f, col[1] + shadetop), + .color_inner2[1] = max_ff(0.0f, col[1] + shadedown), + .color_inner1[2] = min_ff(1.0f, col[2] + shadetop), + .color_inner2[2] = max_ff(0.0f, col[2] + shadedown), + .color_inner1[3] = 1.0f, + .color_inner2[3] = 1.0f, + .alpha_discard = 1.0f, + }; + + GPUBatch *batch = ui_batch_roundbox_get(filled, false); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); + GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); + GPU_batch_draw(batch); } +#if 0 /* unused */ /* linear vertical shade within button or in outline */ /* view2d scrollers use it */ void UI_draw_roundbox_shade_y( - int mode, float minx, float miny, float maxx, float maxy, - float rad, float shadeLeft, float shadeRight) + bool filled, float minx, float miny, float maxx, float maxy, + float rad, float shadeleft, float shaderight, const float col[4]) { float vec[7][2] = { {0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293}, - {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}}; + {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}, + }; const float div = maxx - minx; const float idiv = 1.0f / div; - float colLeft[3], colRight[3], color[4]; + float colLeft[3], colRight[3]; + int vert_count = 0; int a; /* mult */ for (a = 0; a < 7; a++) { mul_v2_fl(vec[a], rad); } - /* get current color, needs to be outside of glBegin/End */ - glGetFloatv(GL_CURRENT_COLOR, color); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); /* 'shade' defines strength of shading */ - colLeft[0] = min_ff(1.0f, color[0] + shadeLeft); - colLeft[1] = min_ff(1.0f, color[1] + shadeLeft); - colLeft[2] = min_ff(1.0f, color[2] + shadeLeft); - colRight[0] = max_ff(0.0f, color[0] + shadeRight); - colRight[1] = max_ff(0.0f, color[1] + shadeRight); - colRight[2] = max_ff(0.0f, color[2] + shadeRight); + colLeft[0] = min_ff(1.0f, col[0] + shadeleft); + colLeft[1] = min_ff(1.0f, col[1] + shadeleft); + colLeft[2] = min_ff(1.0f, col[2] + shadeleft); + colRight[0] = max_ff(0.0f, col[0] + shaderight); + colRight[1] = max_ff(0.0f, col[1] + shaderight); + colRight[2] = max_ff(0.0f, col[2] + shaderight); + - glBegin(mode); + vert_count += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 9 : 1; + vert_count += (roundboxtype & UI_CNR_TOP_RIGHT) ? 9 : 1; + vert_count += (roundboxtype & UI_CNR_TOP_LEFT) ? 9 : 1; + vert_count += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 9 : 1; + + immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_LOOP, vert_count); /* start with corner right-bottom */ if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { - round_box_shade_col(colLeft, colRight, 0.0); - glVertex2f(maxx - rad, miny); + round_box_shade_col(color, colLeft, colRight, 0.0); + immVertex2f(pos, maxx - rad, miny); for (a = 0; a < 7; a++) { - round_box_shade_col(colLeft, colRight, vec[a][0] * idiv); - glVertex2f(maxx - rad + vec[a][0], miny + vec[a][1]); + round_box_shade_col(color, colLeft, colRight, vec[a][0] * idiv); + immVertex2f(pos, maxx - rad + vec[a][0], miny + vec[a][1]); } - round_box_shade_col(colLeft, colRight, rad * idiv); - glVertex2f(maxx, miny + rad); + round_box_shade_col(color, colLeft, colRight, rad * idiv); + immVertex2f(pos, maxx, miny + rad); } else { - round_box_shade_col(colLeft, colRight, 0.0); - glVertex2f(maxx, miny); + round_box_shade_col(color, colLeft, colRight, 0.0); + immVertex2f(pos, maxx, miny); } /* corner right-top */ if (roundboxtype & UI_CNR_TOP_RIGHT) { - round_box_shade_col(colLeft, colRight, 0.0); - glVertex2f(maxx, maxy - rad); + round_box_shade_col(color, colLeft, colRight, 0.0); + immVertex2f(pos, maxx, maxy - rad); for (a = 0; a < 7; a++) { - round_box_shade_col(colLeft, colRight, (div - rad - vec[a][0]) * idiv); - glVertex2f(maxx - vec[a][1], maxy - rad + vec[a][0]); + round_box_shade_col(color, colLeft, colRight, (div - rad - vec[a][0]) * idiv); + immVertex2f(pos, maxx - vec[a][1], maxy - rad + vec[a][0]); } - round_box_shade_col(colLeft, colRight, (div - rad) * idiv); - glVertex2f(maxx - rad, maxy); + round_box_shade_col(color, colLeft, colRight, (div - rad) * idiv); + immVertex2f(pos, maxx - rad, maxy); } else { - round_box_shade_col(colLeft, colRight, 0.0); - glVertex2f(maxx, maxy); + round_box_shade_col(color, colLeft, colRight, 0.0); + immVertex2f(pos, maxx, maxy); } /* corner left-top */ if (roundboxtype & UI_CNR_TOP_LEFT) { - round_box_shade_col(colLeft, colRight, (div - rad) * idiv); - glVertex2f(minx + rad, maxy); + round_box_shade_col(color, colLeft, colRight, (div - rad) * idiv); + immVertex2f(pos, minx + rad, maxy); for (a = 0; a < 7; a++) { - round_box_shade_col(colLeft, colRight, (div - rad + vec[a][0]) * idiv); - glVertex2f(minx + rad - vec[a][0], maxy - vec[a][1]); + round_box_shade_col(color, colLeft, colRight, (div - rad + vec[a][0]) * idiv); + immVertex2f(pos, minx + rad - vec[a][0], maxy - vec[a][1]); } - round_box_shade_col(colLeft, colRight, 1.0); - glVertex2f(minx, maxy - rad); + round_box_shade_col(color, colLeft, colRight, 1.0); + immVertex2f(pos, minx, maxy - rad); } else { - round_box_shade_col(colLeft, colRight, 1.0); - glVertex2f(minx, maxy); + round_box_shade_col(color, colLeft, colRight, 1.0); + immVertex2f(pos, minx, maxy); } /* corner left-bottom */ if (roundboxtype & UI_CNR_BOTTOM_LEFT) { - round_box_shade_col(colLeft, colRight, 1.0); - glVertex2f(minx, miny + rad); + round_box_shade_col(color, colLeft, colRight, 1.0); + immVertex2f(pos, minx, miny + rad); for (a = 0; a < 7; a++) { - round_box_shade_col(colLeft, colRight, (vec[a][0]) * idiv); - glVertex2f(minx + vec[a][1], miny + rad - vec[a][0]); + round_box_shade_col(color, colLeft, colRight, (vec[a][0]) * idiv); + immVertex2f(pos, minx + vec[a][1], miny + rad - vec[a][0]); } - round_box_shade_col(colLeft, colRight, 1.0); - glVertex2f(minx + rad, miny); + round_box_shade_col(color, colLeft, colRight, 1.0); + immVertex2f(pos, minx + rad, miny); } else { - round_box_shade_col(colLeft, colRight, 1.0); - glVertex2f(minx, miny); + round_box_shade_col(color, colLeft, colRight, 1.0); + immVertex2f(pos, minx, miny); } - glEnd(); + immEnd(); + immUnbindProgram(); } +#endif /* unused */ -/* plain antialiased unfilled rectangle */ -void UI_draw_roundbox_unfilled(float minx, float miny, float maxx, float maxy, float rad) +void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]) { - float color[4]; - - if (roundboxtype & UI_RB_ALPHA) { - glGetFloatv(GL_CURRENT_COLOR, color); - color[3] = 0.5; - glColor4fv(color); - glEnable(GL_BLEND); - } + int ofs_y = 4 * U.pixelsize; - /* set antialias line */ - glEnable(GL_LINE_SMOOTH); - glEnable(GL_BLEND); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - UI_draw_roundbox_gl_mode(GL_LINE_LOOP, minx, miny, maxx, maxy, rad); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4fv(color); - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); + immRecti(pos, pos_x, pos_y - ofs_y, pos_x + len, pos_y - ofs_y + (height * U.pixelsize)); + immUnbindProgram(); } -/* (old, used in outliner) plain antialiased filled box */ -void UI_draw_roundbox(float minx, float miny, float maxx, float maxy, float rad) -{ - ui_draw_anti_roundbox(GL_POLYGON, minx, miny, maxx, maxy, rad, roundboxtype & UI_RB_ALPHA); -} +/* ************** SPECIAL BUTTON DRAWING FUNCTIONS ************* */ -void UI_draw_text_underline(int pos_x, int pos_y, int len, int height) +/* based on UI_draw_roundbox_gl_mode, check on making a version which allows us to skip some sides */ +void ui_draw_but_TAB_outline(const rcti *rect, float rad, unsigned char highlight[3], unsigned char highlight_fade[3]) { - int ofs_y = 4 * U.pixelsize; - glRecti(pos_x, pos_y - ofs_y, pos_x + len, pos_y - ofs_y + (height * U.pixelsize)); -} + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); + /* add a 1px offset, looks nicer */ + const int minx = rect->xmin + U.pixelsize, maxx = rect->xmax - U.pixelsize; + const int miny = rect->ymin + U.pixelsize, maxy = rect->ymax - U.pixelsize; + int a; + float vec[4][2] = { + {0.195, 0.02}, + {0.55, 0.169}, + {0.831, 0.45}, + {0.98, 0.805}, + }; -/* ************** SPECIAL BUTTON DRAWING FUNCTIONS ************* */ + + /* mult */ + for (a = 0; a < 4; a++) { + mul_v2_fl(vec[a], rad); + } + + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); + immBeginAtMost(GPU_PRIM_LINE_STRIP, 25); + + immAttrib3ubv(col, highlight); + + /* start with corner left-top */ + if (roundboxtype & UI_CNR_TOP_LEFT) { + immVertex2f(pos, minx, maxy - rad); + for (a = 0; a < 4; a++) { + immVertex2f(pos, minx + vec[a][1], maxy - rad + vec[a][0]); + } + immVertex2f(pos, minx + rad, maxy); + } + else { + immVertex2f(pos, minx, maxy); + } + + /* corner right-top */ + if (roundboxtype & UI_CNR_TOP_RIGHT) { + immVertex2f(pos, maxx - rad, maxy); + for (a = 0; a < 4; a++) { + immVertex2f(pos, maxx - rad + vec[a][0], maxy - vec[a][1]); + } + immVertex2f(pos, maxx, maxy - rad); + } + else { + immVertex2f(pos, maxx, maxy); + } + + immAttrib3ubv(col, highlight_fade); + + /* corner right-bottom */ + if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { + immVertex2f(pos, maxx, miny + rad); + for (a = 0; a < 4; a++) { + immVertex2f(pos, maxx - vec[a][1], miny + rad - vec[a][0]); + } + immVertex2f(pos, maxx - rad, miny); + } + else { + immVertex2f(pos, maxx, miny); + } + + /* corner left-bottom */ + if (roundboxtype & UI_CNR_BOTTOM_LEFT) { + immVertex2f(pos, minx + rad, miny); + for (a = 0; a < 4; a++) { + immVertex2f(pos, minx + rad - vec[a][0], miny + vec[a][1]); + } + immVertex2f(pos, minx, miny + rad); + } + else { + immVertex2f(pos, minx, miny); + } + + immAttrib3ubv(col, highlight); + + /* back to corner left-top */ + immVertex2f(pos, minx, roundboxtype & UI_CNR_TOP_LEFT ? maxy - rad : maxy); + + immEnd(); + immUnbindProgram(); +} void ui_draw_but_IMAGE(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *rect) { @@ -416,37 +644,37 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(w if (!ibuf) return; + float facx = 1.0f; + float facy = 1.0f; + int w = BLI_rcti_size_x(rect); int h = BLI_rcti_size_y(rect); /* scissor doesn't seem to be doing the right thing...? */ #if 0 - //glColor4f(1.0, 0.f, 0.f, 1.f); - //fdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax) - /* prevent drawing outside widget area */ - GLint scissor[4]; - glGetIntegerv(GL_SCISSOR_BOX, scissor); - glScissor(ar->winrct.xmin + rect->xmin, ar->winrct.ymin + rect->ymin, w, h); + int scissor[4]; + GPU_scissor_get_i(scissor); + GPU_scissor(rect->xmin, rect->ymin, w, h); #endif - glEnable(GL_BLEND); - glColor4f(0.0, 0.0, 0.0, 0.0); + GPU_blend(true); if (w != ibuf->x || h != ibuf->y) { - float facx = (float)w / (float)ibuf->x; - float facy = (float)h / (float)ibuf->y; - glPixelZoom(facx, facy); + facx = (float)w / (float)ibuf->x; + facy = (float)h / (float)ibuf->y; } - glaDrawPixelsAuto((float)rect->xmin, (float)rect->ymin, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, ibuf->rect); - glPixelZoom(1.0f, 1.0f); + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); + immDrawPixelsTex( + &state, (float)rect->xmin, (float)rect->ymin, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, ibuf->rect, + facx, facy, NULL); - glDisable(GL_BLEND); + GPU_blend(false); #if 0 // restore scissortest - glScissor(scissor[0], scissor[1], scissor[2], scissor[3]); + GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]); #endif #endif @@ -455,43 +683,34 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(w /** * Draw title and text safe areas. * - * The first 4 parameters are the offsets for the view, not the zones. + * \Note This functionn is to be used with the 2D dashed shader enabled. + * + * \param pos is a PRIM_FLOAT, 2, GPU_FETCH_FLOAT vertex attrib + * \param line_origin is a PRIM_FLOAT, 2, GPU_FETCH_FLOAT vertex attrib + * + * The next 4 parameters are the offsets for the view, not the zones. */ void UI_draw_safe_areas( - float x1, float x2, float y1, float y2, + uint pos, float x1, float x2, float y1, float y2, const float title_aspect[2], const float action_aspect[2]) { const float size_x_half = (x2 - x1) * 0.5f; const float size_y_half = (y2 - y1) * 0.5f; const float *safe_areas[] = {title_aspect, action_aspect}; - int safe_len = ARRAY_SIZE(safe_areas); - bool is_first = true; + const int safe_len = ARRAY_SIZE(safe_areas); for (int i = 0; i < safe_len; i++) { if (safe_areas[i][0] || safe_areas[i][1]) { - float margin_x, margin_y; - float minx, miny, maxx, maxy; - - if (is_first) { - UI_ThemeColorBlendShade(TH_VIEW_OVERLAY, TH_BACK, 0.25f, 0); - is_first = false; - } - - margin_x = safe_areas[i][0] * size_x_half; - margin_y = safe_areas[i][1] * size_y_half; + float margin_x = safe_areas[i][0] * size_x_half; + float margin_y = safe_areas[i][1] * size_y_half; - minx = x1 + margin_x; - miny = y1 + margin_y; - maxx = x2 - margin_x; - maxy = y2 - margin_y; + float minx = x1 + margin_x; + float miny = y1 + margin_y; + float maxx = x2 - margin_x; + float maxy = y2 - margin_y; - glBegin(GL_LINE_LOOP); - glVertex2f(maxx, miny); - glVertex2f(maxx, maxy); - glVertex2f(minx, maxy); - glVertex2f(minx, miny); - glEnd(); + imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy); } } } @@ -500,65 +719,73 @@ void UI_draw_safe_areas( static void draw_scope_end(const rctf *rect, GLint *scissor) { /* restore scissortest */ - glScissor(scissor[0], scissor[1], scissor[2], scissor[3]); + GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); /* outline */ - glColor4f(0.f, 0.f, 0.f, 0.5f); UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_gl_mode(GL_LINE_LOOP, rect->xmin - 1, rect->ymin, rect->xmax + 1, rect->ymax + 1, 3.0f); + float color[4] = {0.0f, 0.0f, 0.0f, 0.5f}; + UI_draw_roundbox_4fv(false, rect->xmin - 1, rect->ymin, rect->xmax + 1, rect->ymax + 1, 3.0f, color); } static void histogram_draw_one( float r, float g, float b, float alpha, - float x, float y, float w, float h, const float *data, int res, const bool is_line) + float x, float y, float w, float h, const float *data, int res, const bool is_line, + unsigned int pos_attrib) { - glEnable(GL_LINE_SMOOTH); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - glColor4f(r, g, b, alpha); + float color[4] = {r, g, b, alpha}; + + /* that can happen */ + if (res == 0) + return; + + GPU_line_smooth(true); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE); + + immUniformColor4fv(color); if (is_line) { /* curve outline */ - glLineWidth(1.5); + GPU_line_width(1.5); - glBegin(GL_LINE_STRIP); + immBegin(GPU_PRIM_LINE_STRIP, res); for (int i = 0; i < res; i++) { float x2 = x + i * (w / (float)res); - glVertex2f(x2, y + (data[i] * h)); + immVertex2f(pos_attrib, x2, y + (data[i] * h)); } - glEnd(); + immEnd(); } else { /* under the curve */ - glBegin(GL_TRIANGLE_STRIP); - glVertex2f(x, y); - glVertex2f(x, y + (data[0] * h)); + immBegin(GPU_PRIM_TRI_STRIP, res * 2); + immVertex2f(pos_attrib, x, y); + immVertex2f(pos_attrib, x, y + (data[0] * h)); for (int i = 1; i < res; i++) { float x2 = x + i * (w / (float)res); - glVertex2f(x2, y + (data[i] * h)); - glVertex2f(x2, y); + immVertex2f(pos_attrib, x2, y + (data[i] * h)); + immVertex2f(pos_attrib, x2, y); } - glEnd(); + immEnd(); /* curve outline */ - glColor4f(0.f, 0.f, 0.f, 0.25f); + immUniformColor4f(0.0f, 0.0f, 0.0f, 0.25f); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBegin(GL_LINE_STRIP); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + immBegin(GPU_PRIM_LINE_STRIP, res); for (int i = 0; i < res; i++) { float x2 = x + i * (w / (float)res); - glVertex2f(x2, y + (data[i] * h)); + immVertex2f(pos_attrib, x2, y + (data[i] * h)); } - glEnd(); + immEnd(); } - glDisable(GL_LINE_SMOOTH); + GPU_line_smooth(false); } #define HISTOGRAM_TOT_GRID_LINES 4 -void ui_draw_but_HISTOGRAM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *recti) +void ui_draw_but_HISTOGRAM(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *recti) { Histogram *hist = (Histogram *)but->poin; int res = hist->x_resolution; @@ -574,60 +801,95 @@ void ui_draw_but_HISTOGRAM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol) float w = BLI_rctf_size_x(&rect); float h = BLI_rctf_size_y(&rect) * hist->ymax; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GPU_blend(true); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - UI_ThemeColor4(TH_PREVIEW_BACK); + float color[4]; + UI_GetThemeColor4fv(TH_PREVIEW_BACK, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_gl_mode(GL_POLYGON, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f); + UI_draw_roundbox_4fv(true, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f, color); /* need scissor test, histogram can draw outside of boundary */ - GLint scissor[4]; - glGetIntegerv(GL_VIEWPORT, scissor); - glScissor(ar->winrct.xmin + (rect.xmin - 1), - ar->winrct.ymin + (rect.ymin - 1), - (rect.xmax + 1) - (rect.xmin - 1), - (rect.ymax + 1) - (rect.ymin - 1)); - - glColor4f(1.f, 1.f, 1.f, 0.08f); + int scissor[4]; + GPU_scissor_get_i(scissor); + GPU_scissor( + (rect.xmin - 1), + (rect.ymin - 1), + (rect.xmax + 1) - (rect.xmin - 1), + (rect.ymax + 1) - (rect.ymin - 1)); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.08f); /* draw grid lines here */ for (int i = 1; i <= HISTOGRAM_TOT_GRID_LINES; i++) { const float fac = (float)i / (float)HISTOGRAM_TOT_GRID_LINES; /* so we can tell the 1.0 color point */ if (i == HISTOGRAM_TOT_GRID_LINES) { - glColor4f(1.0f, 1.0f, 1.0f, 0.5f); + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); } - fdrawline(rect.xmin, rect.ymin + fac * h, rect.xmax, rect.ymin + fac * h); - fdrawline(rect.xmin + fac * w, rect.ymin, rect.xmin + fac * w, rect.ymax); + immBegin(GPU_PRIM_LINES, 4); + + immVertex2f(pos, rect.xmin, rect.ymin + fac * h); + immVertex2f(pos, rect.xmax, rect.ymin + fac * h); + + immVertex2f(pos, rect.xmin + fac * w, rect.ymin); + immVertex2f(pos, rect.xmin + fac * w, rect.ymax); + + immEnd(); } if (hist->mode == HISTO_MODE_LUMA) { - histogram_draw_one(1.0, 1.0, 1.0, 0.75, rect.xmin, rect.ymin, w, h, hist->data_luma, res, is_line); + histogram_draw_one(1.0, 1.0, 1.0, 0.75, rect.xmin, rect.ymin, w, h, hist->data_luma, res, is_line, pos); } else if (hist->mode == HISTO_MODE_ALPHA) { - histogram_draw_one(1.0, 1.0, 1.0, 0.75, rect.xmin, rect.ymin, w, h, hist->data_a, res, is_line); + histogram_draw_one(1.0, 1.0, 1.0, 0.75, rect.xmin, rect.ymin, w, h, hist->data_a, res, is_line, pos); } else { if (hist->mode == HISTO_MODE_RGB || hist->mode == HISTO_MODE_R) - histogram_draw_one(1.0, 0.0, 0.0, 0.75, rect.xmin, rect.ymin, w, h, hist->data_r, res, is_line); + histogram_draw_one(1.0, 0.0, 0.0, 0.75, rect.xmin, rect.ymin, w, h, hist->data_r, res, is_line, pos); if (hist->mode == HISTO_MODE_RGB || hist->mode == HISTO_MODE_G) - histogram_draw_one(0.0, 1.0, 0.0, 0.75, rect.xmin, rect.ymin, w, h, hist->data_g, res, is_line); + histogram_draw_one(0.0, 1.0, 0.0, 0.75, rect.xmin, rect.ymin, w, h, hist->data_g, res, is_line, pos); if (hist->mode == HISTO_MODE_RGB || hist->mode == HISTO_MODE_B) - histogram_draw_one(0.0, 0.0, 1.0, 0.75, rect.xmin, rect.ymin, w, h, hist->data_b, res, is_line); + histogram_draw_one(0.0, 0.0, 1.0, 0.75, rect.xmin, rect.ymin, w, h, hist->data_b, res, is_line, pos); } + immUnbindProgram(); + /* outline */ draw_scope_end(&rect, scissor); } #undef HISTOGRAM_TOT_GRID_LINES -void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *recti) +static void waveform_draw_one(float *waveform, int nbr, const float col[3]) +{ + GPUVertFormat format = {0}; + uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo, nbr); + + GPU_vertbuf_attr_fill(vbo, pos_id, waveform); + + /* TODO store the GPUBatch inside the scope */ + GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_UNIFORM_COLOR); + GPU_batch_uniform_4f(batch, "color", col[0], col[1], col[2], 1.0f); + GPU_batch_draw(batch); + + GPU_batch_discard(batch); +} + +void ui_draw_but_WAVEFORM(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *recti) { Scopes *scopes = (Scopes *)but->poin; - GLint scissor[4]; + int scissor[4]; float colors[3][3]; float colorsycc[3][3] = {{1, 0, 1}, {1, 1, 0}, {0, 1, 1}}; float colors_alpha[3][3], colorsycc_alpha[3][3]; /* colors pre multiplied by alpha for speed up */ @@ -646,7 +908,7 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), scopes->wavefrm_yfac = 0.98f; float w = BLI_rctf_size_x(&rect) - 7; float h = BLI_rctf_size_y(&rect) * scopes->wavefrm_yfac; - float yofs = rect.ymin + (BLI_rctf_size_y(&rect) - h) / 2.0f; + float yofs = rect.ymin + (BLI_rctf_size_y(&rect) - h) * 0.5f; float w3 = w / 3.0f; /* log scale for alpha */ @@ -661,108 +923,141 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), } } - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + /* Flush text cache before changing scissors. */ + BLF_batch_draw_flush(); - UI_ThemeColor4(TH_PREVIEW_BACK); + GPU_blend(true); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + + float color[4]; + UI_GetThemeColor4fv(TH_PREVIEW_BACK, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_gl_mode(GL_POLYGON, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f); + UI_draw_roundbox_4fv(true, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f, color); /* need scissor test, waveform can draw outside of boundary */ - glGetIntegerv(GL_VIEWPORT, scissor); - glScissor(ar->winrct.xmin + (rect.xmin - 1), - ar->winrct.ymin + (rect.ymin - 1), - (rect.xmax + 1) - (rect.xmin - 1), - (rect.ymax + 1) - (rect.ymin - 1)); - - glColor4f(1.f, 1.f, 1.f, 0.08f); - /* draw grid lines here */ + GPU_scissor_get_i(scissor); + GPU_scissor( + (rect.xmin - 1), + (rect.ymin - 1), + (rect.xmax + 1) - (rect.xmin - 1), + (rect.ymax + 1) - (rect.ymin - 1)); + + /* draw scale numbers first before binding any shader */ for (int i = 0; i < 6; i++) { char str[4]; BLI_snprintf(str, sizeof(str), "%-3d", i * 20); str[3] = '\0'; - fdrawline(rect.xmin + 22, yofs + (i / 5.f) * h, rect.xmax + 1, yofs + (i / 5.f) * h); - BLF_draw_default(rect.xmin + 1, yofs - 5 + (i / 5.f) * h, 0, str, sizeof(str) - 1); - /* in the loop because blf_draw reset it */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + BLF_color4f(BLF_default(), 1.0f, 1.0f, 1.0f, 0.08f); + BLF_draw_default(rect.xmin + 1, yofs - 5 + (i * 0.2f) * h, 0, str, sizeof(str) - 1); } + + /* Flush text cache before drawing things on top. */ + BLF_batch_draw_flush(); + + GPU_blend(true); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.08f); + + /* draw grid lines here */ + immBegin(GPU_PRIM_LINES, 12); + + for (int i = 0; i < 6; i++) { + immVertex2f(pos, rect.xmin + 22, yofs + (i * 0.2f) * h); + immVertex2f(pos, rect.xmax + 1, yofs + (i * 0.2f) * h); + } + + immEnd(); + /* 3 vertical separation */ if (scopes->wavefrm_mode != SCOPES_WAVEFRM_LUMA) { + immBegin(GPU_PRIM_LINES, 4); + for (int i = 1; i < 3; i++) { - fdrawline(rect.xmin + i * w3, rect.ymin, rect.xmin + i * w3, rect.ymax); + immVertex2f(pos, rect.xmin + i * w3, rect.ymin); + immVertex2f(pos, rect.xmin + i * w3, rect.ymax); } + + immEnd(); } /* separate min max zone on the right */ - fdrawline(rect.xmin + w, rect.ymin, rect.xmin + w, rect.ymax); + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, rect.xmin + w, rect.ymin); + immVertex2f(pos, rect.xmin + w, rect.ymax); + immEnd(); + /* 16-235-240 level in case of ITU-R BT601/709 */ - glColor4f(1.f, 0.4f, 0.f, 0.2f); + immUniformColor4f(1.0f, 0.4f, 0.0f, 0.2f); if (ELEM(scopes->wavefrm_mode, SCOPES_WAVEFRM_YCC_601, SCOPES_WAVEFRM_YCC_709)) { - fdrawline(rect.xmin + 22, yofs + h * 16.0f / 255.0f, rect.xmax + 1, yofs + h * 16.0f / 255.0f); - fdrawline(rect.xmin + 22, yofs + h * 235.0f / 255.0f, rect.xmin + w3, yofs + h * 235.0f / 255.0f); - fdrawline(rect.xmin + 3 * w3, yofs + h * 235.0f / 255.0f, rect.xmax + 1, yofs + h * 235.0f / 255.0f); - fdrawline(rect.xmin + w3, yofs + h * 240.0f / 255.0f, rect.xmax + 1, yofs + h * 240.0f / 255.0f); + immBegin(GPU_PRIM_LINES, 8); + + immVertex2f(pos, rect.xmin + 22, yofs + h * 16.0f / 255.0f); + immVertex2f(pos, rect.xmax + 1, yofs + h * 16.0f / 255.0f); + + immVertex2f(pos, rect.xmin + 22, yofs + h * 235.0f / 255.0f); + immVertex2f(pos, rect.xmin + w3, yofs + h * 235.0f / 255.0f); + + immVertex2f(pos, rect.xmin + 3 * w3, yofs + h * 235.0f / 255.0f); + immVertex2f(pos, rect.xmax + 1, yofs + h * 235.0f / 255.0f); + + immVertex2f(pos, rect.xmin + w3, yofs + h * 240.0f / 255.0f); + immVertex2f(pos, rect.xmax + 1, yofs + h * 240.0f / 255.0f); + + immEnd(); } /* 7.5 IRE black point level for NTSC */ - if (scopes->wavefrm_mode == SCOPES_WAVEFRM_LUMA) - fdrawline(rect.xmin, yofs + h * 0.075f, rect.xmax + 1, yofs + h * 0.075f); + if (scopes->wavefrm_mode == SCOPES_WAVEFRM_LUMA) { + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, rect.xmin, yofs + h * 0.075f); + immVertex2f(pos, rect.xmax + 1, yofs + h * 0.075f); + immEnd(); + } if (scopes->ok && scopes->waveform_1 != NULL) { - - /* LUMA (1 channel) */ glBlendFunc(GL_ONE, GL_ONE); - glColor3f(alpha, alpha, alpha); - glPointSize(1.0); + GPU_point_size(1.0); + /* LUMA (1 channel) */ if (scopes->wavefrm_mode == SCOPES_WAVEFRM_LUMA) { + float col[3] = {alpha, alpha, alpha}; - glBlendFunc(GL_ONE, GL_ONE); - - glPushMatrix(); - glEnableClientState(GL_VERTEX_ARRAY); + GPU_matrix_push(); + GPU_matrix_translate_2f(rect.xmin, yofs); + GPU_matrix_scale_2f(w, h); - glTranslatef(rect.xmin, yofs, 0.f); - glScalef(w, h, 0.f); - glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_1); - glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); + waveform_draw_one(scopes->waveform_1, scopes->waveform_tot, col); - glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); + GPU_matrix_pop(); /* min max */ - glColor3f(0.5f, 0.5f, 0.5f); + immUniformColor3f(0.5f, 0.5f, 0.5f); min = yofs + scopes->minmax[0][0] * h; max = yofs + scopes->minmax[0][1] * h; CLAMP(min, rect.ymin, rect.ymax); CLAMP(max, rect.ymin, rect.ymax); - fdrawline(rect.xmax - 3, min, rect.xmax - 3, max); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, rect.xmax - 3, min); + immVertex2f(pos, rect.xmax - 3, max); + immEnd(); } /* RGB (3 channel) */ else if (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB) { - glBlendFunc(GL_ONE, GL_ONE); + GPU_matrix_push(); + GPU_matrix_translate_2f(rect.xmin, yofs); + GPU_matrix_scale_2f(w, h); - glEnableClientState(GL_VERTEX_ARRAY); + waveform_draw_one(scopes->waveform_1, scopes->waveform_tot, colors_alpha[0]); + waveform_draw_one(scopes->waveform_2, scopes->waveform_tot, colors_alpha[1]); + waveform_draw_one(scopes->waveform_3, scopes->waveform_tot, colors_alpha[2]); - glPushMatrix(); - - glTranslatef(rect.xmin, yofs, 0.f); - glScalef(w, h, 0.f); - - glColor3fv( colors_alpha[0] ); - glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_1); - glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); - - glColor3fv( colors_alpha[1] ); - glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_2); - glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); - - glColor3fv( colors_alpha[2] ); - glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_3); - glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); - - glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); + GPU_matrix_pop(); } /* PARADE / YCC (3 channels) */ else if (ELEM(scopes->wavefrm_mode, @@ -774,49 +1069,47 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), { int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB_PARADE); - glBlendFunc(GL_ONE, GL_ONE); - - glPushMatrix(); - glEnableClientState(GL_VERTEX_ARRAY); + GPU_matrix_push(); + GPU_matrix_translate_2f(rect.xmin, yofs); + GPU_matrix_scale_2f(w3, h); - glTranslatef(rect.xmin, yofs, 0.f); - glScalef(w3, h, 0.f); + waveform_draw_one(scopes->waveform_1, scopes->waveform_tot, (rgb) ? colors_alpha[0] : colorsycc_alpha[0]); - glColor3fv((rgb) ? colors_alpha[0] : colorsycc_alpha[0]); - glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_1); - glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); + GPU_matrix_translate_2f(1.0f, 0.0f); + waveform_draw_one(scopes->waveform_2, scopes->waveform_tot, (rgb) ? colors_alpha[1] : colorsycc_alpha[1]); - glTranslatef(1.f, 0.f, 0.f); - glColor3fv((rgb) ? colors_alpha[1] : colorsycc_alpha[1]); - glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_2); - glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); + GPU_matrix_translate_2f(1.0f, 0.0f); + waveform_draw_one(scopes->waveform_3, scopes->waveform_tot, (rgb) ? colors_alpha[2] : colorsycc_alpha[2]); - glTranslatef(1.f, 0.f, 0.f); - glColor3fv((rgb) ? colors_alpha[2] : colorsycc_alpha[2]); - glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_3); - glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); - - glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); + GPU_matrix_pop(); } + /* min max */ if (scopes->wavefrm_mode != SCOPES_WAVEFRM_LUMA ) { for (int c = 0; c < 3; c++) { if (ELEM(scopes->wavefrm_mode, SCOPES_WAVEFRM_RGB_PARADE, SCOPES_WAVEFRM_RGB)) - glColor3f(colors[c][0] * 0.75f, colors[c][1] * 0.75f, colors[c][2] * 0.75f); + immUniformColor3f(colors[c][0] * 0.75f, colors[c][1] * 0.75f, colors[c][2] * 0.75f); else - glColor3f(colorsycc[c][0] * 0.75f, colorsycc[c][1] * 0.75f, colorsycc[c][2] * 0.75f); + immUniformColor3f(colorsycc[c][0] * 0.75f, colorsycc[c][1] * 0.75f, colorsycc[c][2] * 0.75f); min = yofs + scopes->minmax[c][0] * h; max = yofs + scopes->minmax[c][1] * h; CLAMP(min, rect.ymin, rect.ymax); CLAMP(max, rect.ymin, rect.ymax); - fdrawline(rect.xmin + w + 2 + c * 2, min, rect.xmin + w + 2 + c * 2, max); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, rect.xmin + w + 2 + c * 2, min); + immVertex2f(pos, rect.xmin + w + 2 + c * 2, max); + immEnd(); } } } + immUnbindProgram(); + /* outline */ draw_scope_end(&rect, scissor); + + GPU_blend(false); } static float polar_to_x(float center, float diam, float ampli, float angle) @@ -829,10 +1122,10 @@ static float polar_to_y(float center, float diam, float ampli, float angle) return center + diam * ampli * sinf(angle); } -static void vectorscope_draw_target(float centerx, float centery, float diam, const float colf[3]) +static void vectorscope_draw_target(unsigned int pos, float centerx, float centery, float diam, const float colf[3]) { float y, u, v; - float tangle = 0.f, tampli; + float tangle = 0.0f, tampli; float dangle, dampli, dangle2, dampli2; rgb_to_yuv(colf[0], colf[1], colf[2], &y, &u, &v, BLI_YUV_ITU_BT709); @@ -844,44 +1137,44 @@ static void vectorscope_draw_target(float centerx, float centery, float diam, co tampli = sqrtf(u * u + v * v); /* small target vary by 2.5 degree and 2.5 IRE unit */ - glColor4f(1.0f, 1.0f, 1.0, 0.12f); + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.12f); dangle = DEG2RADF(2.5f); dampli = 2.5f / 200.0f; - glBegin(GL_LINE_LOOP); - glVertex2f(polar_to_x(centerx, diam, tampli + dampli, tangle + dangle), polar_to_y(centery, diam, tampli + dampli, tangle + dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli - dampli, tangle + dangle), polar_to_y(centery, diam, tampli - dampli, tangle + dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli - dampli, tangle - dangle), polar_to_y(centery, diam, tampli - dampli, tangle - dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli + dampli, tangle - dangle), polar_to_y(centery, diam, tampli + dampli, tangle - dangle)); - glEnd(); + immBegin(GPU_PRIM_LINE_LOOP, 4); + immVertex2f(pos, polar_to_x(centerx, diam, tampli + dampli, tangle + dangle), polar_to_y(centery, diam, tampli + dampli, tangle + dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli - dampli, tangle + dangle), polar_to_y(centery, diam, tampli - dampli, tangle + dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli - dampli, tangle - dangle), polar_to_y(centery, diam, tampli - dampli, tangle - dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli + dampli, tangle - dangle), polar_to_y(centery, diam, tampli + dampli, tangle - dangle)); + immEnd(); /* big target vary by 10 degree and 20% amplitude */ - glColor4f(1.0f, 1.0f, 1.0, 0.12f); + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.12f); dangle = DEG2RADF(10.0f); dampli = 0.2f * tampli; dangle2 = DEG2RADF(5.0f); dampli2 = 0.5f * dampli; - glBegin(GL_LINE_STRIP); - glVertex2f(polar_to_x(centerx, diam, tampli + dampli - dampli2, tangle + dangle), polar_to_y(centery, diam, tampli + dampli - dampli2, tangle + dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli + dampli, tangle + dangle), polar_to_y(centery, diam, tampli + dampli, tangle + dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli + dampli, tangle + dangle - dangle2), polar_to_y(centery, diam, tampli + dampli, tangle + dangle - dangle2)); - glEnd(); - glBegin(GL_LINE_STRIP); - glVertex2f(polar_to_x(centerx, diam, tampli - dampli + dampli2, tangle + dangle), polar_to_y(centery, diam, tampli - dampli + dampli2, tangle + dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli - dampli, tangle + dangle), polar_to_y(centery, diam, tampli - dampli, tangle + dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli - dampli, tangle + dangle - dangle2), polar_to_y(centery, diam, tampli - dampli, tangle + dangle - dangle2)); - glEnd(); - glBegin(GL_LINE_STRIP); - glVertex2f(polar_to_x(centerx, diam, tampli - dampli + dampli2, tangle - dangle), polar_to_y(centery, diam, tampli - dampli + dampli2, tangle - dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli - dampli, tangle - dangle), polar_to_y(centery, diam, tampli - dampli, tangle - dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli - dampli, tangle - dangle + dangle2), polar_to_y(centery, diam, tampli - dampli, tangle - dangle + dangle2)); - glEnd(); - glBegin(GL_LINE_STRIP); - glVertex2f(polar_to_x(centerx, diam, tampli + dampli - dampli2, tangle - dangle), polar_to_y(centery, diam, tampli + dampli - dampli2, tangle - dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli + dampli, tangle - dangle), polar_to_y(centery, diam, tampli + dampli, tangle - dangle)); - glVertex2f(polar_to_x(centerx, diam, tampli + dampli, tangle - dangle + dangle2), polar_to_y(centery, diam, tampli + dampli, tangle - dangle + dangle2)); - glEnd(); + immBegin(GPU_PRIM_LINE_STRIP, 3); + immVertex2f(pos, polar_to_x(centerx, diam, tampli + dampli - dampli2, tangle + dangle), polar_to_y(centery, diam, tampli + dampli - dampli2, tangle + dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli + dampli, tangle + dangle), polar_to_y(centery, diam, tampli + dampli, tangle + dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli + dampli, tangle + dangle - dangle2), polar_to_y(centery, diam, tampli + dampli, tangle + dangle - dangle2)); + immEnd(); + immBegin(GPU_PRIM_LINE_STRIP, 3); + immVertex2f(pos, polar_to_x(centerx, diam, tampli - dampli + dampli2, tangle + dangle), polar_to_y(centery, diam, tampli - dampli + dampli2, tangle + dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli - dampli, tangle + dangle), polar_to_y(centery, diam, tampli - dampli, tangle + dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli - dampli, tangle + dangle - dangle2), polar_to_y(centery, diam, tampli - dampli, tangle + dangle - dangle2)); + immEnd(); + immBegin(GPU_PRIM_LINE_STRIP, 3); + immVertex2f(pos, polar_to_x(centerx, diam, tampli - dampli + dampli2, tangle - dangle), polar_to_y(centery, diam, tampli - dampli + dampli2, tangle - dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli - dampli, tangle - dangle), polar_to_y(centery, diam, tampli - dampli, tangle - dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli - dampli, tangle - dangle + dangle2), polar_to_y(centery, diam, tampli - dampli, tangle - dangle + dangle2)); + immEnd(); + immBegin(GPU_PRIM_LINE_STRIP, 3); + immVertex2f(pos, polar_to_x(centerx, diam, tampli + dampli - dampli2, tangle - dangle), polar_to_y(centery, diam, tampli + dampli - dampli2, tangle - dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli + dampli, tangle - dangle), polar_to_y(centery, diam, tampli + dampli, tangle - dangle)); + immVertex2f(pos, polar_to_x(centerx, diam, tampli + dampli, tangle - dangle + dangle2), polar_to_y(centery, diam, tampli + dampli, tangle - dangle + dangle2)); + immEnd(); } -void ui_draw_but_VECTORSCOPE(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *recti) +void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *recti) { const float skin_rad = DEG2RADF(123.0f); /* angle in radians of the skin tone line */ Scopes *scopes = (Scopes *)but->poin; @@ -899,114 +1192,132 @@ void ui_draw_but_VECTORSCOPE(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wco float w = BLI_rctf_size_x(&rect); float h = BLI_rctf_size_y(&rect); - float centerx = rect.xmin + w / 2; - float centery = rect.ymin + h / 2; + float centerx = rect.xmin + w * 0.5f; + float centery = rect.ymin + h * 0.5f; float diam = (w < h) ? w : h; float alpha = scopes->vecscope_alpha * scopes->vecscope_alpha * scopes->vecscope_alpha; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GPU_blend(true); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - UI_ThemeColor4(TH_PREVIEW_BACK); + float color[4]; + UI_GetThemeColor4fv(TH_PREVIEW_BACK, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_gl_mode(GL_POLYGON, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f); + UI_draw_roundbox_4fv(true, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f, color); /* need scissor test, hvectorscope can draw outside of boundary */ - GLint scissor[4]; - glGetIntegerv(GL_VIEWPORT, scissor); - glScissor(ar->winrct.xmin + (rect.xmin - 1), - ar->winrct.ymin + (rect.ymin - 1), - (rect.xmax + 1) - (rect.xmin - 1), - (rect.ymax + 1) - (rect.ymin - 1)); - - glColor4f(1.f, 1.f, 1.f, 0.08f); + int scissor[4]; + GPU_scissor_get_i(scissor); + GPU_scissor( + (rect.xmin - 1), + (rect.ymin - 1), + (rect.xmax + 1) - (rect.xmin - 1), + (rect.ymax + 1) - (rect.ymin - 1)); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.08f); /* draw grid elements */ /* cross */ - fdrawline(centerx - (diam / 2) - 5, centery, centerx + (diam / 2) + 5, centery); - fdrawline(centerx, centery - (diam / 2) - 5, centerx, centery + (diam / 2) + 5); + immBegin(GPU_PRIM_LINES, 4); + + immVertex2f(pos, centerx - (diam * 0.5f) - 5, centery); + immVertex2f(pos, centerx + (diam * 0.5f) + 5, centery); + + immVertex2f(pos, centerx, centery - (diam * 0.5f) - 5); + immVertex2f(pos, centerx, centery + (diam * 0.5f) + 5); + + immEnd(); + /* circles */ for (int j = 0; j < 5; j++) { - glBegin(GL_LINE_LOOP); const int increment = 15; + immBegin(GPU_PRIM_LINE_LOOP, (int)(360 / increment)); for (int i = 0; i <= 360 - increment; i += increment) { const float a = DEG2RADF((float)i); - const float r = (j + 1) / 10.0f; - glVertex2f(polar_to_x(centerx, diam, r, a), polar_to_y(centery, diam, r, a)); + const float r = (j + 1) * 0.1f; + immVertex2f(pos, polar_to_x(centerx, diam, r, a), polar_to_y(centery, diam, r, a)); } - glEnd(); + immEnd(); } /* skin tone line */ - glColor4f(1.f, 0.4f, 0.f, 0.2f); - fdrawline(polar_to_x(centerx, diam, 0.5f, skin_rad), polar_to_y(centery, diam, 0.5, skin_rad), - polar_to_x(centerx, diam, 0.1f, skin_rad), polar_to_y(centery, diam, 0.1, skin_rad)); + immUniformColor4f(1.0f, 0.4f, 0.0f, 0.2f); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, polar_to_x(centerx, diam, 0.5f, skin_rad), polar_to_y(centery, diam, 0.5f, skin_rad)); + immVertex2f(pos, polar_to_x(centerx, diam, 0.1f, skin_rad), polar_to_y(centery, diam, 0.1f, skin_rad)); + immEnd(); + /* saturation points */ for (int i = 0; i < 6; i++) - vectorscope_draw_target(centerx, centery, diam, colors[i]); + vectorscope_draw_target(pos, centerx, centery, diam, colors[i]); if (scopes->ok && scopes->vecscope != NULL) { /* pixel point cloud */ - glBlendFunc(GL_ONE, GL_ONE); - glColor3f(alpha, alpha, alpha); + float col[3] = {alpha, alpha, alpha}; - glPushMatrix(); - glEnableClientState(GL_VERTEX_ARRAY); + glBlendFunc(GL_ONE, GL_ONE); + GPU_point_size(1.0); - glTranslatef(centerx, centery, 0.f); - glScalef(diam, diam, 0.f); + GPU_matrix_push(); + GPU_matrix_translate_2f(centerx, centery); + GPU_matrix_scale_1f(diam); - glVertexPointer(2, GL_FLOAT, 0, scopes->vecscope); - glPointSize(1.0); - glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); + waveform_draw_one(scopes->vecscope, scopes->waveform_tot, col); - glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); + GPU_matrix_pop(); } + immUnbindProgram(); + /* outline */ draw_scope_end(&rect, scissor); - glDisable(GL_BLEND); + GPU_blend(false); } -static void ui_draw_colorband_handle_tri_hlight(float x1, float y1, float halfwidth, float height) +static void ui_draw_colorband_handle_tri_hlight(unsigned int pos, float x1, float y1, float halfwidth, float height) { - glEnable(GL_LINE_SMOOTH); + GPU_line_smooth(true); - glBegin(GL_LINE_STRIP); - glVertex2f(x1 + halfwidth, y1); - glVertex2f(x1, y1 + height); - glVertex2f(x1 - halfwidth, y1); - glEnd(); + immBegin(GPU_PRIM_LINE_STRIP, 3); + immVertex2f(pos, x1 + halfwidth, y1); + immVertex2f(pos, x1, y1 + height); + immVertex2f(pos, x1 - halfwidth, y1); + immEnd(); - glDisable(GL_LINE_SMOOTH); + GPU_line_smooth(false); } -static void ui_draw_colorband_handle_tri(float x1, float y1, float halfwidth, float height, bool fill) +static void ui_draw_colorband_handle_tri(unsigned int pos, float x1, float y1, float halfwidth, float height, bool fill) { glEnable(fill ? GL_POLYGON_SMOOTH : GL_LINE_SMOOTH); - glBegin(fill ? GL_TRIANGLES : GL_LINE_LOOP); - glVertex2f(x1 + halfwidth, y1); - glVertex2f(x1, y1 + height); - glVertex2f(x1 - halfwidth, y1); - glEnd(); + immBegin(fill ? GPU_PRIM_TRIS : GPU_PRIM_LINE_LOOP, 3); + immVertex2f(pos, x1 + halfwidth, y1); + immVertex2f(pos, x1, y1 + height); + immVertex2f(pos, x1 - halfwidth, y1); + immEnd(); glDisable(fill ? GL_POLYGON_SMOOTH : GL_LINE_SMOOTH); } -static void ui_draw_colorband_handle_box(float x1, float y1, float x2, float y2, bool fill) +static void ui_draw_colorband_handle_box(unsigned int pos, float x1, float y1, float x2, float y2, bool fill) { - glBegin(fill ? GL_QUADS : GL_LINE_LOOP); - glVertex2f(x1, y1); - glVertex2f(x1, y2); - glVertex2f(x2, y2); - glVertex2f(x2, y1); - glEnd(); + immBegin(fill ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_LOOP, 4); + immVertex2f(pos, x1, y1); + immVertex2f(pos, x1, y2); + immVertex2f(pos, x2, y2); + immVertex2f(pos, x2, y1); + immEnd(); } static void ui_draw_colorband_handle( - const rcti *rect, float x, + uint shdr_pos, const rcti *rect, float x, const float rgb[3], struct ColorManagedDisplay *display, bool active) { @@ -1025,18 +1336,26 @@ static void ui_draw_colorband_handle( y1 = floorf(y1 + 0.5f); if (active || half_width < min_width) { - glBegin(GL_LINES); - glColor3ub(0, 0, 0); - glVertex2f(x, y1); - glVertex2f(x, y2); - glEnd(); - setlinestyle(active ? 2 : 1); - glBegin(GL_LINES); - glColor3ub(200, 200, 200); - glVertex2f(x, y1); - glVertex2f(x, y2); - glEnd(); - setlinestyle(0); + immUnbindProgram(); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC); + + immUniform1i("colors_len", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.8f, 0.8f, 0.8f, 1.0f}, {0.0f, 0.0f, 0.0f, 1.0f}}, 2); + immUniform1f("dash_width", active ? 4.0f : 2.0f); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(shdr_pos, x, y1); + immVertex2f(shdr_pos, x, y2); + immEnd(); + + immUnbindProgram(); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* hide handles when zoomed out too far */ if (half_width < min_width) { @@ -1047,45 +1366,46 @@ static void ui_draw_colorband_handle( /* shift handle down */ y1 -= half_width; - glColor3ub(0, 0, 0); - ui_draw_colorband_handle_box(x - half_width, y1 - 1, x + half_width, y1 + height, false); + immUniformColor3ub(0, 0, 0); + ui_draw_colorband_handle_box(shdr_pos, x - half_width, y1 - 1, x + half_width, y1 + height, false); /* draw all triangles blended */ - glEnable(GL_BLEND); + GPU_blend(true); - ui_draw_colorband_handle_tri(x, y1 + height, half_width, half_width, true); + ui_draw_colorband_handle_tri(shdr_pos, x, y1 + height, half_width, half_width, true); if (active) - glColor3ub(196, 196, 196); + immUniformColor3ub(196, 196, 196); else - glColor3ub(96, 96, 96); - ui_draw_colorband_handle_tri(x, y1 + height, half_width, half_width, true); + immUniformColor3ub(96, 96, 96); + ui_draw_colorband_handle_tri(shdr_pos, x, y1 + height, half_width, half_width, true); if (active) - glColor3ub(255, 255, 255); + immUniformColor3ub(255, 255, 255); else - glColor3ub(128, 128, 128); - ui_draw_colorband_handle_tri_hlight(x, y1 + height - 1, (half_width - 1), (half_width - 1)); + immUniformColor3ub(128, 128, 128); + ui_draw_colorband_handle_tri_hlight(shdr_pos, x, y1 + height - 1, (half_width - 1), (half_width - 1)); - glColor3ub(0, 0, 0); - ui_draw_colorband_handle_tri_hlight(x, y1 + height, half_width, half_width); + immUniformColor3ub(0, 0, 0); + ui_draw_colorband_handle_tri_hlight(shdr_pos, x, y1 + height, half_width, half_width); - glDisable(GL_BLEND); + GPU_blend(false); - glColor3ub(128, 128, 128); - ui_draw_colorband_handle_box(x - (half_width - 1), y1, x + (half_width - 1), y1 + height, true); + immUniformColor3ub(128, 128, 128); + ui_draw_colorband_handle_box(shdr_pos, x - (half_width - 1), y1, x + (half_width - 1), y1 + height, true); if (display) { IMB_colormanagement_scene_linear_to_display_v3(colf, display); } - glColor3fv(colf); - ui_draw_colorband_handle_box(x - (half_width - 2), y1 + 1, x + (half_width - 2), y1 + height - 2, true); + immUniformColor3fv(colf); + ui_draw_colorband_handle_box(shdr_pos, x - (half_width - 2), y1 + 1, x + (half_width - 2), y1 + height - 2, true); } void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *rect) { struct ColorManagedDisplay *display = NULL; + uint pos_id, col_id; ColorBand *coba = (ColorBand *)(but->editcoba ? but->editcoba : but->poin); if (coba == NULL) return; @@ -1096,23 +1416,28 @@ void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti float x1 = rect->xmin; float sizex = rect->xmax - x1; float sizey = BLI_rcti_size_y(rect); - float sizey_solid = sizey / 4; + float sizey_solid = sizey * 0.25f; float y1 = rect->ymin; - /* Drawing the checkerboard. - * This could be optimized with a single checkerboard shader, - * instead of drawing twice and using stippling the second time. */ - /* layer: background, to show tranparency */ - glColor4ub(UI_ALPHA_CHECKER_DARK, UI_ALPHA_CHECKER_DARK, UI_ALPHA_CHECKER_DARK, 255); - glRectf(x1, y1, x1 + sizex, rect->ymax); - GPU_basic_shader_bind(GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - glColor4ub(UI_ALPHA_CHECKER_LIGHT, UI_ALPHA_CHECKER_LIGHT, UI_ALPHA_CHECKER_LIGHT, 255); - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_CHECKER_8PX); - glRectf(x1, y1, x1 + sizex, rect->ymax); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + GPUVertFormat *format = immVertexFormat(); + pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_CHECKER); + + /* Drawing the checkerboard. */ + immUniform4f("color1", UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_DARK / 255.0f, 1.0f); + immUniform4f("color2", UI_ALPHA_CHECKER_LIGHT / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 1.0f); + immUniform1i("size", 8); + immRectf(pos_id, x1, y1, x1 + sizex, rect->ymax); + immUnbindProgram(); + + /* New format */ + format = immVertexFormat(); + pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + col_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); /* layer: color ramp */ - glEnable(GL_BLEND); + GPU_blend(true); CBData *cbd = coba->data; @@ -1122,7 +1447,7 @@ void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti v1[1] = y1 + sizey_solid; v2[1] = rect->ymax; - glBegin(GL_TRIANGLE_STRIP); + immBegin(GPU_PRIM_TRI_STRIP, (sizex + 1) * 2); for (int a = 0; a <= sizex; a++) { float pos = ((float)a) / sizex; BKE_colorband_evaluate(coba, pos, colf); @@ -1131,17 +1456,17 @@ void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti v1[0] = v2[0] = x1 + a; - glColor4fv(colf); - glVertex2fv(v1); - glVertex2fv(v2); + immAttrib4fv(col_id, colf); + immVertex2fv(pos_id, v1); + immVertex2fv(pos_id, v2); } - glEnd(); + immEnd(); /* layer: color ramp without alpha for reference when manipulating ramp properties */ v1[1] = y1; v2[1] = y1 + sizey_solid; - glBegin(GL_TRIANGLE_STRIP); + immBegin(GPU_PRIM_TRI_STRIP, (sizex + 1) * 2); for (int a = 0; a <= sizex; a++) { float pos = ((float)a) / sizex; BKE_colorband_evaluate(coba, pos, colf); @@ -1150,31 +1475,48 @@ void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti v1[0] = v2[0] = x1 + a; - glColor4f(colf[0], colf[1], colf[2], 1.0f); - glVertex2fv(v1); - glVertex2fv(v2); + immAttrib4f(col_id, colf[0], colf[1], colf[2], 1.0f); + immVertex2fv(pos_id, v1); + immVertex2fv(pos_id, v2); } - glEnd(); + immEnd(); + + immUnbindProgram(); + + GPU_blend(false); - glDisable(GL_BLEND); + /* New format */ + format = immVertexFormat(); + pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* layer: box outline */ - glColor4f(0.0, 0.0, 0.0, 1.0); - fdrawbox(x1, y1, x1 + sizex, rect->ymax); + immUniformColor4f(0.0f, 0.0f, 0.0f, 1.0f); + imm_draw_box_wire_2d(pos_id, x1, y1, x1 + sizex, rect->ymax); /* layer: box outline */ - glEnable(GL_BLEND); - glColor4f(0.0f, 0.0f, 0.0f, 0.5f); - fdrawline(x1, y1, x1 + sizex, y1); - glColor4f(1.0f, 1.0f, 1.0f, 0.25f); - fdrawline(x1, y1 - 1, x1 + sizex, y1 - 1); - glDisable(GL_BLEND); + GPU_blend(true); + immUniformColor4f(0.0f, 0.0f, 0.0f, 0.5f); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos_id, x1, y1); + immVertex2f(pos_id, x1 + sizex, y1); + immEnd(); + + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.25f); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos_id, x1, y1 - 1); + immVertex2f(pos_id, x1 + sizex, y1 - 1); + immEnd(); + + GPU_blend(false); /* layer: draw handles */ for (int a = 0; a < coba->tot; a++, cbd++) { if (a != coba->cur) { float pos = x1 + cbd->pos * (sizex - 1) + 1; - ui_draw_colorband_handle(rect, pos, &cbd->r, display, false); + ui_draw_colorband_handle(pos_id, rect, pos, &cbd->r, display, false); } } @@ -1182,117 +1524,102 @@ void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti if (coba->tot != 0) { cbd = &coba->data[coba->cur]; float pos = x1 + cbd->pos * (sizex - 1) + 1; - ui_draw_colorband_handle(rect, pos, &cbd->r, display, true); + ui_draw_colorband_handle(pos_id, rect, pos, &cbd->r, display, true); } + + immUnbindProgram(); } void ui_draw_but_UNITVEC(uiBut *but, uiWidgetColors *wcol, const rcti *rect) { - static GLuint displist = 0; + /* sphere color */ float diffuse[3] = {1.0f, 1.0f, 1.0f}; + float light[3]; float size; /* backdrop */ - glColor3ubv((unsigned char *)wcol->inner); UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_gl_mode(GL_POLYGON, rect->xmin, rect->ymin, rect->xmax, rect->ymax, 5.0f); + UI_draw_roundbox_3ubAlpha(true, rect->xmin, rect->ymin, rect->xmax, rect->ymax, 5.0f, (unsigned char *)wcol->inner, 255); - /* sphere color */ glCullFace(GL_BACK); glEnable(GL_CULL_FACE); /* setup lights */ - GPULightData light = {0}; - light.type = GPU_LIGHT_SUN; - copy_v3_v3(light.diffuse, diffuse); - zero_v3(light.specular); - ui_but_v3_get(but, light.direction); - - GPU_basic_shader_light_set(0, &light); - for (int a = 1; a < 8; a++) - GPU_basic_shader_light_set(a, NULL); - - /* setup shader */ - GPU_basic_shader_colors(diffuse, NULL, 0, 1.0f); - GPU_basic_shader_bind(GPU_SHADER_LIGHTING); + ui_but_v3_get(but, light); /* transform to button */ - glPushMatrix(); - glTranslatef(rect->xmin + 0.5f * BLI_rcti_size_x(rect), rect->ymin + 0.5f * BLI_rcti_size_y(rect), 0.0f); + GPU_matrix_push(); if (BLI_rcti_size_x(rect) < BLI_rcti_size_y(rect)) - size = BLI_rcti_size_x(rect) / 200.f; + size = 0.5f * BLI_rcti_size_x(rect); else - size = BLI_rcti_size_y(rect) / 200.f; - - glScalef(size, size, MIN2(size, 1.0f)); - - if (displist == 0) { - GLUquadricObj *qobj; + size = 0.5f * BLI_rcti_size_y(rect); - displist = glGenLists(1); - glNewList(displist, GL_COMPILE); + GPU_matrix_translate_2f(rect->xmin + 0.5f * BLI_rcti_size_x(rect), rect->ymin + 0.5f * BLI_rcti_size_y(rect)); + GPU_matrix_scale_1f(size); - qobj = gluNewQuadric(); - gluQuadricDrawStyle(qobj, GLU_FILL); - GPU_basic_shader_bind(GPU_basic_shader_bound_options()); - gluSphere(qobj, 100.0, 32, 24); - gluDeleteQuadric(qobj); - - glEndList(); - } - - glCallList(displist); + GPUBatch *sphere = GPU_batch_preset_sphere(2); + GPU_batch_program_set_builtin(sphere, GPU_SHADER_SIMPLE_LIGHTING); + GPU_batch_uniform_4f(sphere, "color", diffuse[0], diffuse[1], diffuse[2], 1.0f); + GPU_batch_uniform_3fv(sphere, "light", light); + GPU_batch_draw(sphere); /* restore */ - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); - GPU_default_lights(); glDisable(GL_CULL_FACE); /* AA circle */ - glEnable(GL_BLEND); - glEnable(GL_LINE_SMOOTH); - glColor3ubv((unsigned char *)wcol->inner); - glutil_draw_lined_arc(0.0f, M_PI * 2.0, 100.0f, 32); - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3ubv((unsigned char *)wcol->inner); + + GPU_blend(true); + GPU_line_smooth(true); + imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, 1.0f, 32); + GPU_blend(false); + GPU_line_smooth(false); /* matrix after circle */ - glPopMatrix(); + GPU_matrix_pop(); - /* We disabled all blender lights above, so restore them here. */ - GPU_default_lights(); + immUnbindProgram(); } -static void ui_draw_but_curve_grid(const rcti *rect, float zoomx, float zoomy, float offsx, float offsy, float step) +static void ui_draw_but_curve_grid(unsigned int pos, const rcti *rect, float zoomx, float zoomy, float offsx, float offsy, float step) { - glBegin(GL_LINES); float dx = step * zoomx; float fx = rect->xmin + zoomx * (-offsx); if (fx > rect->xmin) fx -= dx * (floorf(fx - rect->xmin)); - while (fx < rect->xmax) { - glVertex2f(fx, rect->ymin); - glVertex2f(fx, rect->ymax); - fx += dx; - } float dy = step * zoomy; float fy = rect->ymin + zoomy * (-offsy); if (fy > rect->ymin) fy -= dy * (floorf(fy - rect->ymin)); + + float line_count = ( + floorf((rect->xmax - fx) / dx) + 1.0f + + floorf((rect->ymax - fy) / dy) + 1.0f); + + immBegin(GPU_PRIM_LINES, (int)line_count * 2); + while (fx < rect->xmax) { + immVertex2f(pos, fx, rect->ymin); + immVertex2f(pos, fx, rect->ymax); + fx += dx; + } while (fy < rect->ymax) { - glVertex2f(rect->xmin, fy); - glVertex2f(rect->xmax, fy); + immVertex2f(pos, rect->xmin, fy); + immVertex2f(pos, rect->xmax, fy); fy += dy; } - glEnd(); + immEnd(); } static void gl_shaded_color(unsigned char *col, int shade) { - glColor3ub(col[0] - shade > 0 ? col[0] - shade : 0, - col[1] - shade > 0 ? col[1] - shade : 0, - col[2] - shade > 0 ? col[2] - shade : 0); + immUniformColor3ub( + col[0] - shade > 0 ? col[0] - shade : 0, + col[1] - shade > 0 ? col[1] - shade : 0, + col[2] - shade > 0 ? col[2] - shade : 0); } void ui_draw_but_CURVE(ARegion *ar, uiBut *but, uiWidgetColors *wcol, const rcti *rect) @@ -1309,19 +1636,21 @@ void ui_draw_but_CURVE(ARegion *ar, uiBut *but, uiWidgetColors *wcol, const rcti CurveMap *cuma = &cumap->cm[cumap->cur]; /* need scissor test, curve can draw outside of boundary */ - GLint scissor[4]; - glGetIntegerv(GL_VIEWPORT, scissor); + int scissor[4]; + GPU_scissor_get_i(scissor); rcti scissor_new = { - .xmin = ar->winrct.xmin + rect->xmin, - .ymin = ar->winrct.ymin + rect->ymin, - .xmax = ar->winrct.xmin + rect->xmax, - .ymax = ar->winrct.ymin + rect->ymax + .xmin = rect->xmin, + .ymin = rect->ymin, + .xmax = rect->xmax, + .ymax = rect->ymax }; - BLI_rcti_isect(&scissor_new, &ar->winrct, &scissor_new); - glScissor(scissor_new.xmin, - scissor_new.ymin, - BLI_rcti_size_x(&scissor_new), - BLI_rcti_size_y(&scissor_new)); + rcti scissor_region = {0, ar->winx, 0, ar->winy}; + BLI_rcti_isect(&scissor_new, &scissor_region, &scissor_new); + GPU_scissor( + scissor_new.xmin, + scissor_new.ymin, + BLI_rcti_size_x(&scissor_new), + BLI_rcti_size_y(&scissor_new)); /* calculate offset and zoom */ float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&cumap->curr); @@ -1329,9 +1658,7 @@ void ui_draw_but_CURVE(ARegion *ar, uiBut *but, uiWidgetColors *wcol, const rcti float offsx = cumap->curr.xmin - (1.0f / zoomx); float offsy = cumap->curr.ymin - (1.0f / zoomy); - glLineWidth(1.0f); - - /* backdrop */ + /* Do this first to not mess imm context */ if (but->a1 == UI_GRAD_H) { /* magic trigger for curve backgrounds */ float col[3] = {0.0f, 0.0f, 0.0f}; /* dummy arg */ @@ -1344,96 +1671,106 @@ void ui_draw_but_CURVE(ARegion *ar, uiBut *but, uiWidgetColors *wcol, const rcti }; ui_draw_gradient(&grid, col, UI_GRAD_H, 1.0f); + } + GPU_line_width(1.0f); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + /* backdrop */ + if (but->a1 == UI_GRAD_H) { /* grid, hsv uses different grid */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4ub(0, 0, 0, 48); - ui_draw_but_curve_grid(rect, zoomx, zoomy, offsx, offsy, 0.1666666f); - glDisable(GL_BLEND); + GPU_blend(true); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + immUniformColor4ub(0, 0, 0, 48); + ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.1666666f); + GPU_blend(false); } else { if (cumap->flag & CUMA_DO_CLIP) { gl_shaded_color((unsigned char *)wcol->inner, -20); - glRectf(rect->xmin, rect->ymin, rect->xmax, rect->ymax); - glColor3ubv((unsigned char *)wcol->inner); - glRectf(rect->xmin + zoomx * (cumap->clipr.xmin - offsx), - rect->ymin + zoomy * (cumap->clipr.ymin - offsy), - rect->xmin + zoomx * (cumap->clipr.xmax - offsx), - rect->ymin + zoomy * (cumap->clipr.ymax - offsy)); + immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); + immUniformColor3ubv((unsigned char *)wcol->inner); + immRectf(pos, + rect->xmin + zoomx * (cumap->clipr.xmin - offsx), + rect->ymin + zoomy * (cumap->clipr.ymin - offsy), + rect->xmin + zoomx * (cumap->clipr.xmax - offsx), + rect->ymin + zoomy * (cumap->clipr.ymax - offsy)); } else { - glColor3ubv((unsigned char *)wcol->inner); - glRectf(rect->xmin, rect->ymin, rect->xmax, rect->ymax); + immUniformColor3ubv((unsigned char *)wcol->inner); + immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); } /* grid, every 0.25 step */ gl_shaded_color((unsigned char *)wcol->inner, -16); - ui_draw_but_curve_grid(rect, zoomx, zoomy, offsx, offsy, 0.25f); + ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.25f); /* grid, every 1.0 step */ gl_shaded_color((unsigned char *)wcol->inner, -24); - ui_draw_but_curve_grid(rect, zoomx, zoomy, offsx, offsy, 1.0f); + ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 1.0f); /* axes */ gl_shaded_color((unsigned char *)wcol->inner, -50); - glBegin(GL_LINES); - glVertex2f(rect->xmin, rect->ymin + zoomy * (-offsy)); - glVertex2f(rect->xmax, rect->ymin + zoomy * (-offsy)); - glVertex2f(rect->xmin + zoomx * (-offsx), rect->ymin); - glVertex2f(rect->xmin + zoomx * (-offsx), rect->ymax); - glEnd(); + immBegin(GPU_PRIM_LINES, 4); + immVertex2f(pos, rect->xmin, rect->ymin + zoomy * (-offsy)); + immVertex2f(pos, rect->xmax, rect->ymin + zoomy * (-offsy)); + immVertex2f(pos, rect->xmin + zoomx * (-offsx), rect->ymin); + immVertex2f(pos, rect->xmin + zoomx * (-offsx), rect->ymax); + immEnd(); } /* cfra option */ /* XXX 2.48 */ #if 0 if (cumap->flag & CUMA_DRAW_CFRA) { - glColor3ub(0x60, 0xc0, 0x40); - glBegin(GL_LINES); - glVertex2f(rect->xmin + zoomx * (cumap->sample[0] - offsx), rect->ymin); - glVertex2f(rect->xmin + zoomx * (cumap->sample[0] - offsx), rect->ymax); - glEnd(); + immUniformColor3ub(0x60, 0xc0, 0x40); + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, rect->xmin + zoomx * (cumap->sample[0] - offsx), rect->ymin); + immVertex2f(pos, rect->xmin + zoomx * (cumap->sample[0] - offsx), rect->ymax); + immEnd(); } #endif /* sample option */ if (cumap->flag & CUMA_DRAW_SAMPLE) { - glBegin(GL_LINES); /* will draw one of the following 3 lines */ + immBegin(GPU_PRIM_LINES, 2); /* will draw one of the following 3 lines */ if (but->a1 == UI_GRAD_H) { float tsample[3]; float hsv[3]; linearrgb_to_srgb_v3_v3(tsample, cumap->sample); rgb_to_hsv_v(tsample, hsv); - glColor3ub(240, 240, 240); + immUniformColor3ub(240, 240, 240); - glVertex2f(rect->xmin + zoomx * (hsv[0] - offsx), rect->ymin); - glVertex2f(rect->xmin + zoomx * (hsv[0] - offsx), rect->ymax); + immVertex2f(pos, rect->xmin + zoomx * (hsv[0] - offsx), rect->ymin); + immVertex2f(pos, rect->xmin + zoomx * (hsv[0] - offsx), rect->ymax); } else if (cumap->cur == 3) { float lum = IMB_colormanagement_get_luminance(cumap->sample); - glColor3ub(240, 240, 240); + immUniformColor3ub(240, 240, 240); - glVertex2f(rect->xmin + zoomx * (lum - offsx), rect->ymin); - glVertex2f(rect->xmin + zoomx * (lum - offsx), rect->ymax); + immVertex2f(pos, rect->xmin + zoomx * (lum - offsx), rect->ymin); + immVertex2f(pos, rect->xmin + zoomx * (lum - offsx), rect->ymax); } else { if (cumap->cur == 0) - glColor3ub(240, 100, 100); + immUniformColor3ub(240, 100, 100); else if (cumap->cur == 1) - glColor3ub(100, 240, 100); + immUniformColor3ub(100, 240, 100); else - glColor3ub(100, 100, 240); + immUniformColor3ub(100, 100, 240); - glVertex2f(rect->xmin + zoomx * (cumap->sample[cumap->cur] - offsx), rect->ymin); - glVertex2f(rect->xmin + zoomx * (cumap->sample[cumap->cur] - offsx), rect->ymax); + immVertex2f(pos, rect->xmin + zoomx * (cumap->sample[cumap->cur] - offsx), rect->ymin); + immVertex2f(pos, rect->xmin + zoomx * (cumap->sample[cumap->cur] - offsx), rect->ymax); } - glEnd(); + immEnd(); } /* the curve */ - glColor3ubv((unsigned char *)wcol->item); - glEnable(GL_LINE_SMOOTH); - glEnable(GL_BLEND); - glBegin(GL_LINE_STRIP); + immUniformColor3ubv((unsigned char *)wcol->item); + GPU_line_smooth(true); + GPU_blend(true); + immBegin(GPU_PRIM_LINE_STRIP, (CM_TABLE + 1) + 2); if (cuma->table == NULL) curvemapping_changed(cumap, false); @@ -1442,55 +1779,70 @@ void ui_draw_but_CURVE(ARegion *ar, uiBut *but, uiWidgetColors *wcol, const rcti /* first point */ if ((cuma->flag & CUMA_EXTEND_EXTRAPOLATE) == 0) { - glVertex2f(rect->xmin, rect->ymin + zoomy * (cmp[0].y - offsy)); + immVertex2f(pos, rect->xmin, rect->ymin + zoomy * (cmp[0].y - offsy)); } else { float fx = rect->xmin + zoomx * (cmp[0].x - offsx + cuma->ext_in[0]); float fy = rect->ymin + zoomy * (cmp[0].y - offsy + cuma->ext_in[1]); - glVertex2f(fx, fy); + immVertex2f(pos, fx, fy); } for (int a = 0; a <= CM_TABLE; a++) { float fx = rect->xmin + zoomx * (cmp[a].x - offsx); float fy = rect->ymin + zoomy * (cmp[a].y - offsy); - glVertex2f(fx, fy); + immVertex2f(pos, fx, fy); } /* last point */ if ((cuma->flag & CUMA_EXTEND_EXTRAPOLATE) == 0) { - glVertex2f(rect->xmax, rect->ymin + zoomy * (cmp[CM_TABLE].y - offsy)); + immVertex2f(pos, rect->xmax, rect->ymin + zoomy * (cmp[CM_TABLE].y - offsy)); } else { float fx = rect->xmin + zoomx * (cmp[CM_TABLE].x - offsx - cuma->ext_out[0]); float fy = rect->ymin + zoomy * (cmp[CM_TABLE].y - offsy - cuma->ext_out[1]); - glVertex2f(fx, fy); + immVertex2f(pos, fx, fy); } - glEnd(); - glDisable(GL_LINE_SMOOTH); - glDisable(GL_BLEND); + immEnd(); + GPU_line_smooth(false); + GPU_blend(false); + immUnbindProgram(); /* the points, use aspect to make them visible on edges */ + format = immVertexFormat(); + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + cmp = cuma->curve; - glPointSize(3.0f); - glBegin(GL_POINTS); + GPU_point_size(3.0f); + immBegin(GPU_PRIM_POINTS, cuma->totpoint); for (int a = 0; a < cuma->totpoint; a++) { + float color[4]; if (cmp[a].flag & CUMA_SELECT) - UI_ThemeColor(TH_TEXT_HI); + UI_GetThemeColor4fv(TH_TEXT_HI, color); else - UI_ThemeColor(TH_TEXT); + UI_GetThemeColor4fv(TH_TEXT, color); float fx = rect->xmin + zoomx * (cmp[a].x - offsx); float fy = rect->ymin + zoomy * (cmp[a].y - offsy); - glVertex2f(fx, fy); + immAttrib4fv(col, color); + immVertex2f(pos, fx, fy); } - glEnd(); + immEnd(); + immUnbindProgram(); /* restore scissortest */ - glScissor(scissor[0], scissor[1], scissor[2], scissor[3]); + GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]); /* outline */ - glColor3ubv((unsigned char *)wcol->outline); - fdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax); + format = immVertexFormat(); + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3ubv((unsigned char *)wcol->outline); + imm_draw_box_wire_2d(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); + + immUnbindProgram(); } -void ui_draw_but_TRACKPREVIEW(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *recti) +void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *recti) { bool ok = false; MovieClipScopes *scopes = (MovieClipScopes *)but->poin; @@ -1505,21 +1857,22 @@ void ui_draw_but_TRACKPREVIEW(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wc int width = BLI_rctf_size_x(&rect) + 1; int height = BLI_rctf_size_y(&rect); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GPU_blend(true); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); /* need scissor test, preview image can draw outside of boundary */ - GLint scissor[4]; - glGetIntegerv(GL_VIEWPORT, scissor); - glScissor(ar->winrct.xmin + (rect.xmin - 1), - ar->winrct.ymin + (rect.ymin - 1), - (rect.xmax + 1) - (rect.xmin - 1), - (rect.ymax + 1) - (rect.ymin - 1)); + int scissor[4]; + GPU_scissor_get_i(scissor); + GPU_scissor( + (rect.xmin - 1), + (rect.ymin - 1), + (rect.xmax + 1) - (rect.xmin - 1), + (rect.ymax + 1) - (rect.ymin - 1)); if (scopes->track_disabled) { - glColor4f(0.7f, 0.3f, 0.3f, 0.3f); + float color[4] = {0.7f, 0.3f, 0.3f, 0.3f}; UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_gl_mode(GL_POLYGON, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f); + UI_draw_roundbox_4fv(true, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f, color); ok = true; } @@ -1547,69 +1900,79 @@ void ui_draw_but_TRACKPREVIEW(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wc } if (!ok && scopes->track_preview) { - glPushMatrix(); + GPU_matrix_push(); /* draw content of pattern area */ - glScissor(ar->winrct.xmin + rect.xmin, ar->winrct.ymin + rect.ymin, scissor[2], scissor[3]); + GPU_scissor(rect.xmin, rect.ymin, scissor[2], scissor[3]); if (width > 0 && height > 0) { ImBuf *drawibuf = scopes->track_preview; + float col_sel[4], col_outline[4]; if (scopes->use_track_mask) { - glColor4f(0.0f, 0.0f, 0.0f, 0.3f); + float color[4] = {0.0f, 0.0f, 0.0f, 0.3f}; UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_gl_mode(GL_POLYGON, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f); + UI_draw_roundbox_4fv(true, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f, color); } - glaDrawPixelsSafe( - rect.xmin, rect.ymin + 1, drawibuf->x, drawibuf->y, - drawibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, drawibuf->rect); + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); + immDrawPixelsTex(&state, rect.xmin, rect.ymin + 1, drawibuf->x, drawibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_LINEAR, drawibuf->rect, 1.0f, 1.0f, NULL); /* draw cross for pixel position */ - glTranslatef(rect.xmin + scopes->track_pos[0], rect.ymin + scopes->track_pos[1], 0.f); - glScissor(ar->winrct.xmin + rect.xmin, - ar->winrct.ymin + rect.ymin, - BLI_rctf_size_x(&rect), - BLI_rctf_size_y(&rect)); - - GPU_basic_shader_bind_enable(GPU_SHADER_LINE); - - for (int a = 0; a < 2; a++) { - if (a == 1) { - GPU_basic_shader_bind_enable(GPU_SHADER_STIPPLE); - GPU_basic_shader_line_stipple(3, 0xAAAA); - UI_ThemeColor(TH_SEL_MARKER); - } - else { - UI_ThemeColor(TH_MARKER_OUTLINE); + GPU_matrix_translate_2f(rect.xmin + scopes->track_pos[0], rect.ymin + scopes->track_pos[1]); + GPU_scissor( + rect.xmin, + rect.ymin, + BLI_rctf_size_x(&rect), + BLI_rctf_size_y(&rect)); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + + UI_GetThemeColor4fv(TH_SEL_MARKER, col_sel); + UI_GetThemeColor4fv(TH_MARKER_OUTLINE, col_outline); + + /* Do stipple cross with geometry */ + immBegin(GPU_PRIM_LINES, 7 * 2 * 2); + float pos_sel[8] = {-10.0f, -7.0f, -4.0f, -1.0f, 2.0f, 5.0f, 8.0f, 11.0f}; + for (int axe = 0; axe < 2; ++axe) { + for (int i = 0; i < 7; ++i) { + float x1 = pos_sel[i] * (1 - axe); + float y1 = pos_sel[i] * axe; + float x2 = pos_sel[i + 1] * (1 - axe); + float y2 = pos_sel[i + 1] * axe; + + if (i % 2 == 1) + immAttrib4fv(col, col_sel); + else + immAttrib4fv(col, col_outline); + + immVertex2f(pos, x1, y1); + immVertex2f(pos, x2, y2); } - - glBegin(GL_LINES); - glVertex2f(-10.0f, 0.0f); - glVertex2f(10.0f, 0.0f); - glVertex2f(0.0f, -10.0f); - glVertex2f(0.0f, 10.0f); - glEnd(); } + immEnd(); - GPU_basic_shader_bind_disable(GPU_SHADER_LINE | GPU_SHADER_STIPPLE); + immUnbindProgram(); } - glPopMatrix(); + GPU_matrix_pop(); ok = true; } if (!ok) { - glColor4f(0.f, 0.f, 0.f, 0.3f); + float color[4] = {0.0f, 0.0f, 0.0f, 0.3f}; UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_gl_mode(GL_POLYGON, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f); + UI_draw_roundbox_4fv(true, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f, color); } /* outline */ draw_scope_end(&rect, scissor); - glDisable(GL_BLEND); + GPU_blend(false); } void ui_draw_but_NODESOCKET(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *recti) @@ -1631,93 +1994,145 @@ void ui_draw_but_NODESOCKET(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol 0.15142777f, 0.52896401f, 0.82076344f, 0.97952994f, }; - GLint scissor[4]; + int scissor[4]; /* need scissor test, can draw outside of boundary */ - glGetIntegerv(GL_VIEWPORT, scissor); + GPU_scissor_get_i(scissor); rcti scissor_new = { - .xmin = ar->winrct.xmin + recti->xmin, - .ymin = ar->winrct.ymin + recti->ymin, - .xmax = ar->winrct.xmin + recti->xmax, - .ymax = ar->winrct.ymin + recti->ymax + .xmin = recti->xmin, + .ymin = recti->ymin, + .xmax = recti->xmax, + .ymax = recti->ymax }; - BLI_rcti_isect(&scissor_new, &ar->winrct, &scissor_new); - glScissor(scissor_new.xmin, - scissor_new.ymin, - BLI_rcti_size_x(&scissor_new), - BLI_rcti_size_y(&scissor_new)); + rcti scissor_region = {0, ar->winx, 0, ar->winy}; - glColor4ubv(but->col); + BLI_rcti_isect(&scissor_new, &scissor_region, &scissor_new); + GPU_scissor( + scissor_new.xmin, + scissor_new.ymin, + BLI_rcti_size_x(&scissor_new), + BLI_rcti_size_y(&scissor_new)); float x = 0.5f * (recti->xmin + recti->xmax); float y = 0.5f * (recti->ymin + recti->ymax); - glEnable(GL_BLEND); - glBegin(GL_POLYGON); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4ubv(but->col); + + GPU_blend(true); + immBegin(GPU_PRIM_TRI_FAN, 16); for (int a = 0; a < 16; a++) - glVertex2f(x + size * si[a], y + size * co[a]); - glEnd(); + immVertex2f(pos, x + size * si[a], y + size * co[a]); + immEnd(); - glColor4ub(0, 0, 0, 150); - glLineWidth(1); - glEnable(GL_LINE_SMOOTH); - glBegin(GL_LINE_LOOP); + immUniformColor4ub(0, 0, 0, 150); + GPU_line_width(1); + GPU_line_smooth(true); + immBegin(GPU_PRIM_LINE_LOOP, 16); for (int a = 0; a < 16; a++) - glVertex2f(x + size * si[a], y + size * co[a]); - glEnd(); - glDisable(GL_LINE_SMOOTH); - glDisable(GL_BLEND); + immVertex2f(pos, x + size * si[a], y + size * co[a]); + immEnd(); + GPU_line_smooth(false); + GPU_blend(false); + + immUnbindProgram(); /* restore scissortest */ - glScissor(scissor[0], scissor[1], scissor[2], scissor[3]); + GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]); } /* ****************************************************** */ +/* TODO: high quality UI drop shadows using GLSL shader and single draw call + * would replace / modify the following 3 functions - merwin + */ -static void ui_shadowbox(float minx, float miny, float maxx, float maxy, float shadsize, unsigned char alpha) +static void ui_shadowbox(unsigned pos, unsigned color, float minx, float miny, float maxx, float maxy, float shadsize, unsigned char alpha) { + /* v1-_ + * | -_v2 + * | | + * | | + * | | + * v7_______v3____v4 + * \ | / + * \ | _v5 + * v8______v6_- + */ + const float v1[2] = {maxx, maxy - 0.3f * shadsize}; + const float v2[2] = {maxx + shadsize, maxy - 0.75f * shadsize}; + const float v3[2] = {maxx, miny}; + const float v4[2] = {maxx + shadsize, miny}; + + const float v5[2] = {maxx + 0.7f * shadsize, miny - 0.7f * shadsize}; + + const float v6[2] = {maxx, miny - shadsize}; + const float v7[2] = {minx + 0.3f * shadsize, miny}; + const float v8[2] = {minx + 0.5f * shadsize, miny - shadsize}; + /* right quad */ - glColor4ub(0, 0, 0, alpha); - glVertex2f(maxx, miny); - glVertex2f(maxx, maxy - 0.3f * shadsize); - glColor4ub(0, 0, 0, 0); - glVertex2f(maxx + shadsize, maxy - 0.75f * shadsize); - glVertex2f(maxx + shadsize, miny); + immAttrib4ub(color, 0, 0, 0, alpha); + immVertex2fv(pos, v3); + immVertex2fv(pos, v1); + immAttrib4ub(color, 0, 0, 0, 0); + immVertex2fv(pos, v2); + + immVertex2fv(pos, v2); + immVertex2fv(pos, v4); + immAttrib4ub(color, 0, 0, 0, alpha); + immVertex2fv(pos, v3); /* corner shape */ - glColor4ub(0, 0, 0, alpha); - glVertex2f(maxx, miny); - glColor4ub(0, 0, 0, 0); - glVertex2f(maxx + shadsize, miny); - glVertex2f(maxx + 0.7f * shadsize, miny - 0.7f * shadsize); - glVertex2f(maxx, miny - shadsize); + /* immAttrib4ub(color, 0, 0, 0, alpha); */ /* Not needed, done above in previous tri */ + immVertex2fv(pos, v3); + immAttrib4ub(color, 0, 0, 0, 0); + immVertex2fv(pos, v4); + immVertex2fv(pos, v5); + + immVertex2fv(pos, v5); + immVertex2fv(pos, v6); + immAttrib4ub(color, 0, 0, 0, alpha); + immVertex2fv(pos, v3); /* bottom quad */ - glColor4ub(0, 0, 0, alpha); - glVertex2f(minx + 0.3f * shadsize, miny); - glVertex2f(maxx, miny); - glColor4ub(0, 0, 0, 0); - glVertex2f(maxx, miny - shadsize); - glVertex2f(minx + 0.5f * shadsize, miny - shadsize); + /* immAttrib4ub(color, 0, 0, 0, alpha); */ /* Not needed, done above in previous tri */ + immVertex2fv(pos, v3); + immAttrib4ub(color, 0, 0, 0, 0); + immVertex2fv(pos, v6); + immVertex2fv(pos, v8); + + immVertex2fv(pos, v8); + immAttrib4ub(color, 0, 0, 0, alpha); + immVertex2fv(pos, v7); + immVertex2fv(pos, v3); } void UI_draw_box_shadow(unsigned char alpha, float minx, float miny, float maxx, float maxy) { - glEnable(GL_BLEND); + GPU_blend(true); - glBegin(GL_QUADS); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); + + immBegin(GPU_PRIM_TRIS, 54); /* accumulated outline boxes to make shade not linear, is more pleasant */ - ui_shadowbox(minx, miny, maxx, maxy, 11.0, (20 * alpha) >> 8); - ui_shadowbox(minx, miny, maxx, maxy, 7.0, (40 * alpha) >> 8); - ui_shadowbox(minx, miny, maxx, maxy, 5.0, (80 * alpha) >> 8); + ui_shadowbox(pos, color, minx, miny, maxx, maxy, 11.0, (20 * alpha) >> 8); + ui_shadowbox(pos, color, minx, miny, maxx, maxy, 7.0, (40 * alpha) >> 8); + ui_shadowbox(pos, color, minx, miny, maxx, maxy, 5.0, (80 * alpha) >> 8); - glEnd(); + immEnd(); - glDisable(GL_BLEND); + immUnbindProgram(); + + GPU_blend(false); } @@ -1725,8 +2140,8 @@ void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha { float rad; - if (radius > (BLI_rctf_size_y(rct) - 10.0f) / 2.0f) - rad = (BLI_rctf_size_y(rct) - 10.0f) / 2.0f; + if (radius > (BLI_rctf_size_y(rct) - 10.0f) * 0.5f) + rad = (BLI_rctf_size_y(rct) - 10.0f) * 0.5f; else rad = radius; @@ -1741,35 +2156,47 @@ void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha a = i * aspect; } - glEnable(GL_BLEND); - + GPU_blend(true); const float dalpha = alpha * 2.0f / 255.0f; float calpha = dalpha; - for (; i--; a -= aspect) { + float visibility = 1.0f; + for (; i--;) { /* alpha ranges from 2 to 20 or so */ - glColor4f(0.0f, 0.0f, 0.0f, calpha); +#if 0 /* Old Method (pre 2.8) */ + float color[4] = {0.0f, 0.0f, 0.0f, calpha}; + UI_draw_roundbox_4fv(true, rct->xmin - a, rct->ymin - a, rct->xmax + a, rct->ymax - 10.0f + a, rad + a, color); +#endif + /* Compute final visibility to match old method result. */ + /* TODO we could just find a better fit function inside the shader instead of this. */ + visibility = visibility * (1.0f - calpha); calpha += dalpha; - - UI_draw_roundbox_gl_mode(GL_POLYGON, rct->xmin - a, rct->ymin - a, rct->xmax + a, rct->ymax - 10.0f + a, rad + a); } - /* outline emphasis */ - glEnable(GL_LINE_SMOOTH); - glColor4ub(0, 0, 0, 100); - UI_draw_roundbox_gl_mode(GL_LINE_LOOP, rct->xmin - 0.5f, rct->ymin - 0.5f, rct->xmax + 0.5f, rct->ymax + 0.5f, radius + 0.5f); - glDisable(GL_LINE_SMOOTH); + uiWidgetBaseParameters widget_params = { + .recti.xmin = rct->xmin, .recti.ymin = rct->ymin, + .recti.xmax = rct->xmax, .recti.ymax = rct->ymax - 10.0f, + .rect.xmin = rct->xmin - a, .rect.ymin = rct->ymin - a, + .rect.xmax = rct->xmax + a, .rect.ymax = rct->ymax - 10.0f + a, + .radi = rad, + .rad = rad + a, + .round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f, + .round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f, + .round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f, + .round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f, + .alpha_discard = 1.0f, + }; - glDisable(GL_BLEND); -} + GPUBatch *batch = ui_batch_roundbox_shadow_get(); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_SHADOW); + GPU_batch_uniform_4fv_array(batch, "parameters", 4, (float *)&widget_params); + GPU_batch_uniform_1f(batch, "alpha", 1.0f - visibility); + GPU_batch_draw(batch); -/** - * Reset GL state (keep minimal). - * - * \note Blender's internal code doesn't assume these are reset, - * but external callbacks may depend on their state. - */ -void UI_reinit_gl_state(void) -{ - glLineWidth(1.0f); - glPointSize(1.0f); + /* outline emphasis */ + GPU_line_smooth(true); + float color[4] = {0.0f, 0.0f, 0.0f, 0.4f}; + UI_draw_roundbox_4fv(false, rct->xmin - 0.5f, rct->ymin - 0.5f, rct->xmax + 0.5f, rct->ymax + 0.5f, radius + 0.5f, color); + GPU_line_smooth(false); + + GPU_blend(false); } diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index 67486f38760..424019995ad 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -136,8 +136,8 @@ void eyedropper_draw_cursor_text(const struct bContext *C, const ARegion *ar, co wmWindow *win = CTX_wm_window(C); int x = win->eventstate->x; int y = win->eventstate->y; - const unsigned char fg[4] = {255, 255, 255, 255}; - const unsigned char bg[4] = {0, 0, 0, 50}; + const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; if ((name[0] == '\0') || @@ -151,7 +151,7 @@ void eyedropper_draw_cursor_text(const struct bContext *C, const ARegion *ar, co y += U.widget_unit; - UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, fg, bg); + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg); } @@ -166,8 +166,8 @@ void eyedropper_draw_cursor_text(const struct bContext *C, const ARegion *ar, co */ uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event) { - wmWindow *win = CTX_wm_window(C); - ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, event->x, event->y); + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_ANY, event->x, event->y); uiBut *but = ui_but_find_mouse_over(ar, event); diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 5eb4359607a..bcce70d9d8a 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -137,8 +137,8 @@ void eyedropper_color_sample_fl(bContext *C, int mx, int my, float r_col[3]) { /* we could use some clever */ Main *bmain = CTX_data_main(C); - wmWindow *win = CTX_wm_window(C); - ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my); + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mx, my); const char *display_device = CTX_data_scene(C)->display_settings.display_device; struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index 2bb575558fc..b1e649f4abe 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -150,8 +150,8 @@ static void datadropper_exit(bContext *C, wmOperator *op) static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int my, ID **r_id) { /* we could use some clever */ - wmWindow *win = CTX_wm_window(C); - ScrArea *sa = BKE_screen_find_area_xy(win->screen, -1, mx, my); + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, -1, mx, my); ScrArea *area_prev = CTX_wm_area(C); ARegion *ar_prev = CTX_wm_region(C); diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c index 954259140c3..fb125a3845b 100644 --- a/source/blender/editors/interface/interface_eyedropper_depth.c +++ b/source/blender/editors/interface/interface_eyedropper_depth.c @@ -45,6 +45,8 @@ #include "BKE_screen.h" #include "BKE_unit.h" +#include "DEG_depsgraph.h" + #include "RNA_access.h" #include "UI_interface.h" @@ -152,10 +154,10 @@ static void depthdropper_exit(bContext *C, wmOperator *op) static void depthdropper_depth_sample_pt(bContext *C, DepthDropper *ddr, int mx, int my, float *r_depth) { /* we could use some clever */ - Main *bmain = CTX_data_main(C); - wmWindow *win = CTX_wm_window(C); - ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my); - Scene *scene = win->screen->scene; + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mx, my); + Scene *scene = CTX_data_scene(C); + UnitSettings *unit = &scene->unit; const bool do_split = (unit->flag & USER_UNIT_OPT_SPLIT) != 0; @@ -168,6 +170,7 @@ static void depthdropper_depth_sample_pt(bContext *C, DepthDropper *ddr, int mx, if (sa->spacetype == SPACE_VIEW3D) { ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my); if (ar) { + struct Depsgraph *depsgraph = CTX_data_depsgraph(C); View3D *v3d = sa->spacedata.first; RegionView3D *rv3d = ar->regiondata; /* weak, we could pass in some reference point */ @@ -185,7 +188,7 @@ static void depthdropper_depth_sample_pt(bContext *C, DepthDropper *ddr, int mx, view3d_operator_needs_opengl(C); - if (ED_view3d_autodist(bmain, scene, ar, v3d, mval, co, true, NULL)) { + if (ED_view3d_autodist(depsgraph, ar, v3d, mval, co, true, NULL)) { const float mval_center_fl[2] = { (float)ar->winx / 2, (float)ar->winy / 2}; diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c index d6569bd840c..852f7ea71b6 100644 --- a/source/blender/editors/interface/interface_eyedropper_driver.c +++ b/source/blender/editors/interface/interface_eyedropper_driver.c @@ -42,7 +42,9 @@ #include "BKE_context.h" #include "BKE_animsys.h" -#include "BKE_depsgraph.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "RNA_access.h" #include "RNA_define.h" @@ -130,8 +132,8 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve if (success) { /* send updates */ UI_context_update_anim_flag(C); - DAG_relations_tag_update(CTX_data_main(C)); - DAG_id_tag_update(ddr->ptr.id.data, OB_RECALC_OB | OB_RECALC_DATA); + DEG_relations_tag_update(CTX_data_main(C)); + DEG_id_tag_update(ddr->ptr.id.data, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX } } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 06b6099cec7..ac7ed3d5106 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -38,11 +38,7 @@ #include "MEM_guardedalloc.h" #include "DNA_brush_types.h" -#include "DNA_sensor_types.h" -#include "DNA_controller_types.h" -#include "DNA_actuator_types.h" -#include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -72,6 +68,7 @@ #include "ED_undo.h" #include "UI_interface.h" +#include "UI_view2d.h" #include "BLF_api.h" @@ -109,8 +106,6 @@ #define UI_MAX_PASSWORD_STR 128 /* proto */ -static void ui_but_smart_controller_add(bContext *C, uiBut *from, uiBut *to); -static void ui_but_link_add(bContext *C, uiBut *from, uiBut *to); static int ui_do_but_EXIT(bContext *C, uiBut *but, struct uiHandleButtonData *data, const wmEvent *event); static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b); static void ui_textedit_string_set(uiBut *but, struct uiHandleButtonData *data, const char *str); @@ -125,7 +120,7 @@ static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEve #define BUTTON_FLASH_DELAY 0.020 #define MENU_SCROLL_INTERVAL 0.1 #define PIE_MENU_INTERVAL 0.01 -#define BUTTON_AUTO_OPEN_THRESH 0.3 +#define BUTTON_AUTO_OPEN_THRESH 0.2 #define BUTTON_MOUSE_TOWARDS_THRESH 1.0 /* pixels to move the cursor to get out of keyboard navigation */ #define BUTTON_KEYNAV_PX_LIMIT 8 @@ -276,6 +271,7 @@ typedef struct uiHandleButtonData { /* booleans (could be made into flags) */ bool cancel, escapecancel; bool applied, applied_interactive; + bool changed_cursor; wmTimer *flashtimer; /* edited value */ @@ -619,11 +615,8 @@ PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, static void popup_check(bContext *C, wmOperator *op) { - if (op && op->type->check && op->type->check(C, op)) { - /* check for popup and re-layout buttons */ - ARegion *ar_menu = CTX_wm_menu(C); - if (ar_menu) - ED_region_tag_refresh_ui(ar_menu); + if (op && op->type->check) { + op->type->check(C, op); } } @@ -704,8 +697,7 @@ static void ui_apply_but_undo(uiBut *but) const char *str = NULL; /* define which string to use for undo */ - if (ELEM(but->type, UI_BTYPE_LINK, UI_BTYPE_INLINK)) str = "Add button link"; - else if (but->type == UI_BTYPE_MENU) str = but->drawstr; + if (but->type == UI_BTYPE_MENU) str = but->drawstr; else if (but->drawstr[0]) str = but->drawstr; else str = but->tip; @@ -917,6 +909,20 @@ static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data) data->applied = true; } +static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data) +{ + if (data->str) { + ui_but_string_set(C, but, data->str); + ui_but_update_edited(but); + } + else { + ui_apply_but_func(C, but); + } + + data->retval = but->retval; + data->applied = true; +} + static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data) { if (data->str) { @@ -1219,6 +1225,9 @@ static bool ui_drag_toggle_but_is_supported(const uiBut *but) if (ui_but_is_bool(but)) { return true; } + else if (UI_but_is_decorator(but)) { + return ELEM(but->icon, ICON_SPACE2, ICON_SPACE3, ICON_DOT, ICON_LIBRARY_DATA_OVERRIDE); + } else { return false; } @@ -1229,6 +1238,9 @@ static bool ui_drag_toggle_but_is_pushed(uiBut *but) if (ui_but_is_bool(but)) { return ui_but_is_pushed(but); } + else if (UI_but_is_decorator(but)) { + return (but->icon == ICON_SPACE2); + } else { return false; } @@ -1239,7 +1251,6 @@ typedef struct uiDragToggleHandle { bool is_init; bool is_set; float but_cent_start[2]; - eButType but_type_start; bool xy_lock[2]; int xy_init[2]; @@ -1247,7 +1258,7 @@ typedef struct uiDragToggleHandle { } uiDragToggleHandle; static bool ui_drag_toggle_set_xy_xy( - bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start, + bContext *C, ARegion *ar, const bool is_set, const int xy_src[2], const int xy_dst[2]) { /* popups such as layers won't re-evaluate on redraw */ @@ -1271,7 +1282,7 @@ static bool ui_drag_toggle_set_xy_xy( if (BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) { /* execute the button */ - if (ui_drag_toggle_but_is_supported(but) && but->type == but_type_start) { + if (ui_drag_toggle_but_is_supported(but)) { /* is it pressed? */ bool is_set_but = ui_drag_toggle_but_is_pushed(but); if (is_set_but != is_set) { @@ -1345,7 +1356,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const /* touch all buttons between last mouse coord and this one */ - do_draw = ui_drag_toggle_set_xy_xy(C, ar, drag_info->is_set, drag_info->but_type_start, drag_info->xy_last, xy); + do_draw = ui_drag_toggle_set_xy_xy(C, ar, drag_info->is_set, drag_info->xy_last, xy); if (do_draw) { ED_region_tag_redraw(ar); @@ -1725,7 +1736,6 @@ static bool ui_but_drag_init( drag_info->is_set = ui_drag_toggle_but_is_pushed(but); drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect); drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect); - drag_info->but_type_start = but->type; copy_v2_v2_int(drag_info->xy_init, &event->x); copy_v2_v2_int(drag_info->xy_last, &event->x); @@ -1792,223 +1802,6 @@ static bool ui_but_drag_init( /* ********************** linklines *********************** */ -static void ui_linkline_remove_active(uiBlock *block) -{ - uiBut *but; - uiLink *link; - uiLinkLine *line, *nline; - int a, b; - - for (but = block->buttons.first; but; but = but->next) { - if (but->type == UI_BTYPE_LINK && but->link) { - for (line = but->link->lines.first; line; line = nline) { - nline = line->next; - - if (line->flag & UI_SELECT) { - BLI_remlink(&but->link->lines, line); - - link = line->from->link; - - /* are there more pointers allowed? */ - if (link->ppoin) { - - if (*(link->totlink) == 1) { - *(link->totlink) = 0; - MEM_freeN(*(link->ppoin)); - *(link->ppoin) = NULL; - } - else { - b = 0; - for (a = 0; a < (*(link->totlink)); a++) { - - if ((*(link->ppoin))[a] != line->to->poin) { - (*(link->ppoin))[b] = (*(link->ppoin))[a]; - b++; - } - } - (*(link->totlink))--; - } - } - else { - *(link->poin) = NULL; - } - - MEM_freeN(line); - } - } - } - } -} - - -static uiLinkLine *ui_but_find_link(uiBut *from, uiBut *to) -{ - uiLinkLine *line; - uiLink *link; - - link = from->link; - if (link) { - for (line = link->lines.first; line; line = line->next) { - if (line->from == from && line->to == to) { - return line; - } - } - } - return NULL; -} - -/* XXX BAD BAD HACK, fixme later **************** */ -/* Try to add an AND Controller between the sensor and the actuator logic bricks and to connect them all */ -static void ui_but_smart_controller_add(bContext *C, uiBut *from, uiBut *to) -{ - Object *ob = NULL; - bSensor *sens_iter; - bActuator *act_to, *act_iter; - bController *cont; - bController ***sens_from_links; - uiBut *tmp_but; - - uiLink *link = from->link; - - PointerRNA props_ptr, object_ptr; - - if (link->ppoin) - sens_from_links = (bController ***)(link->ppoin); - else return; - - act_to = (bActuator *)(to->poin); - - /* (1) get the object */ - CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) - { - for (sens_iter = ob_iter->sensors.first; sens_iter; sens_iter = sens_iter->next) { - if (&(sens_iter->links) == sens_from_links) { - ob = ob_iter; - break; - } - } - if (ob) break; - } CTX_DATA_END; - - if (!ob) return; - - /* (2) check if the sensor and the actuator are from the same object */ - for (act_iter = ob->actuators.first; act_iter; act_iter = (bActuator *)act_iter->next) { - if (act_iter == act_to) - break; - } - - /* only works if the sensor and the actuator are from the same object */ - if (!act_iter) return; - - /* in case the linked controller is not the active one */ - RNA_pointer_create((ID *)ob, &RNA_Object, ob, &object_ptr); - - WM_operator_properties_create(&props_ptr, "LOGIC_OT_controller_add"); - RNA_string_set(&props_ptr, "object", ob->id.name + 2); - - /* (3) add a new controller */ - if (WM_operator_name_call(C, "LOGIC_OT_controller_add", WM_OP_EXEC_DEFAULT, &props_ptr) & OPERATOR_FINISHED) { - cont = (bController *)ob->controllers.last; - /* Quick fix to make sure we always have an AND controller. - * It might be nicer to make sure the operator gives us the right one though... */ - cont->type = CONT_LOGIC_AND; - - /* (4) link the sensor->controller->actuator */ - tmp_but = MEM_callocN(sizeof(uiBut), "uiBut"); - UI_but_link_set( - tmp_but, (void **)&cont, (void ***)&(cont->links), - &cont->totlinks, from->link->tocode, (int)to->hardmin); - tmp_but->hardmin = from->link->tocode; - tmp_but->poin = (char *)cont; - - tmp_but->type = UI_BTYPE_INLINK; - ui_but_link_add(C, from, tmp_but); - - tmp_but->type = UI_BTYPE_LINK; - ui_but_link_add(C, tmp_but, to); - - /* (5) garbage collection */ - MEM_freeN(tmp_but->link); - MEM_freeN(tmp_but); - } - WM_operator_properties_free(&props_ptr); -} - -static void ui_but_link_add(bContext *C, uiBut *from, uiBut *to) -{ - /* in 'from' we have to add a link to 'to' */ - uiLink *link; - uiLinkLine *line; - void **oldppoin; - int a; - - if ((line = ui_but_find_link(from, to))) { - line->flag |= UI_SELECT; - ui_linkline_remove_active(from->block); - return; - } - - if (from->type == UI_BTYPE_INLINK && to->type == UI_BTYPE_INLINK) { - return; - } - else if (from->type == UI_BTYPE_LINK && to->type == UI_BTYPE_INLINK) { - if (from->link->tocode != (int)to->hardmin) { - ui_but_smart_controller_add(C, from, to); - return; - } - } - else if (from->type == UI_BTYPE_INLINK && to->type == UI_BTYPE_LINK) { - if (to->link->tocode == (int)from->hardmin) { - return; - } - } - - link = from->link; - - /* are there more pointers allowed? */ - if (link->ppoin) { - oldppoin = *(link->ppoin); - - (*(link->totlink))++; - *(link->ppoin) = MEM_callocN(*(link->totlink) * sizeof(void *), "new link"); - - for (a = 0; a < (*(link->totlink)) - 1; a++) { - (*(link->ppoin))[a] = oldppoin[a]; - } - (*(link->ppoin))[a] = to->poin; - - if (oldppoin) MEM_freeN(oldppoin); - } - else { - *(link->poin) = to->poin; - } - -} - - -static void ui_apply_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data) -{ - ARegion *ar = CTX_wm_region(C); - uiBut *bt; - - for (bt = but->block->buttons.first; bt; bt = bt->next) { - if (ui_but_contains_point_px(ar, bt, but->linkto[0] + ar->winrct.xmin, but->linkto[1] + ar->winrct.ymin) ) - break; - } - if (bt && bt != but) { - if (!ELEM(bt->type, UI_BTYPE_LINK, UI_BTYPE_INLINK) || !ELEM(but->type, UI_BTYPE_LINK, UI_BTYPE_INLINK)) - return; - - if (but->type == UI_BTYPE_LINK) ui_but_link_add(C, but, bt); - else ui_but_link_add(C, bt, but); - - ui_apply_but_func(C, but); - data->retval = but->retval; - } - data->applied = true; -} - static void ui_apply_but_IMAGE(bContext *C, uiBut *but, uiHandleButtonData *data) { ui_apply_but_func(C, but); @@ -2127,6 +1920,9 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton case UI_BTYPE_LISTROW: ui_apply_but_ROW(C, block, but, data); break; + case UI_BTYPE_TAB: + ui_apply_but_TAB(C, but, data); + break; case UI_BTYPE_SCROLL: case UI_BTYPE_GRIP: case UI_BTYPE_NUM: @@ -2162,10 +1958,6 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton case UI_BTYPE_HOTKEY_EVENT: ui_apply_but_BUT(C, but, data); break; - case UI_BTYPE_LINK: - case UI_BTYPE_INLINK: - ui_apply_but_LINK(C, but, data); - break; case UI_BTYPE_IMAGE: ui_apply_but_IMAGE(C, but, data); break; @@ -3179,6 +2971,9 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) ui_searchbox_free(C, data->searchbox); data->searchbox = NULL; + if (but->free_search_arg) { + MEM_SAFE_FREE(but->search_arg); + } } but->editstr = NULL; @@ -3658,6 +3453,7 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat uiBlockCreateFunc func = NULL; uiBlockHandleCreateFunc handlefunc = NULL; uiMenuCreateFunc menufunc = NULL; + uiMenuCreateFunc popoverfunc = NULL; void *arg = NULL; switch (but->type) { @@ -3677,6 +3473,11 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat menufunc = but->menu_create_func; arg = but->poin; break; + case UI_BTYPE_POPOVER: + BLI_assert(but->menu_create_func); + popoverfunc = but->menu_create_func; + arg = but->poin; + break; case UI_BTYPE_COLOR: ui_but_v3_get(but, data->origvec); copy_v3_v3(data->vec, data->origvec); @@ -3701,6 +3502,11 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat if (but->block->handle) data->menu->popup = but->block->handle->popup; } + else if (popoverfunc) { + data->menu = ui_popover_panel_create(C, data->region, but, popoverfunc, arg); + if (but->block->handle) + data->menu->popup = but->block->handle->popup; + } #ifdef USE_ALLSELECT { @@ -3947,6 +3753,43 @@ static bool ui_but_is_mouse_over_icon_extra(const ARegion *region, uiBut *but, c return BLI_rcti_isect_pt(&icon_rect, x, y); } +static int ui_do_but_TAB(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +{ + if (data->state == BUTTON_STATE_HIGHLIGHT) { + if ((event->type == LEFTMOUSE) && + ((event->val == KM_DBL_CLICK) || event->ctrl)) + { + button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); + return WM_UI_HANDLER_BREAK; + } + else if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && (event->val == KM_CLICK)) { + const bool has_icon_extra = ui_but_icon_extra_get(but) == UI_BUT_ICONEXTRA_CLEAR; + + if (has_icon_extra && ui_but_is_mouse_over_icon_extra(data->region, but, &event->x)) { + uiButTab *tab = (uiButTab *)but; + wmOperatorType *ot_backup = but->optype; + + but->optype = tab->unlink_ot; + /* Force calling unlink/delete operator. */ + ui_apply_but(C, block, but, data, true); + but->optype = ot_backup; + } + button_activate_state(C, but, BUTTON_STATE_EXIT); + return WM_UI_HANDLER_BREAK; + } + } + else if (data->state == BUTTON_STATE_TEXT_EDITING) { + ui_do_but_textedit(C, block, but, data, event); + return WM_UI_HANDLER_BREAK; + } + else if (data->state == BUTTON_STATE_TEXT_SELECTING) { + ui_do_but_textedit_select(C, block, but, data, event); + return WM_UI_HANDLER_BREAK; + } + + return WM_UI_HANDLER_CONTINUE; +} + static int ui_do_but_TEX( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) @@ -4375,6 +4218,54 @@ static bool ui_numedit_but_NUM( return changed; } +static void ui_numedit_set_active(uiBut *but) +{ + int oldflag = but->drawflag; + but->drawflag &= ~(UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT); + + uiHandleButtonData *data = but->active; + if (!data) { + return; + } + + /* Ignore once we start dragging. */ + if (data->dragchange == false) { + const float handle_width = min_ff(BLI_rctf_size_x(&but->rect) / 3, BLI_rctf_size_y(&but->rect) * 0.7f); + /* we can click on the side arrows to increment/decrement, + * or click inside to edit the value directly */ + int mx = data->window->eventstate->x; + int my = data->window->eventstate->x; + ui_window_to_block(data->region, but->block, &mx, &my); + + if (mx < (but->rect.xmin + handle_width)) { + but->drawflag |= UI_BUT_ACTIVE_LEFT; + } + else if (mx > (but->rect.xmax - handle_width)) { + but->drawflag |= UI_BUT_ACTIVE_RIGHT; + } + } + + /* Don't change the cursor once pressed. */ + if ((but->flag & UI_SELECT) == 0) { + if ((but->drawflag & (UI_BUT_ACTIVE_LEFT)) || (but->drawflag & (UI_BUT_ACTIVE_RIGHT))) { + if (data->changed_cursor) { + WM_cursor_modal_restore(data->window); + data->changed_cursor = false; + } + } + else { + if (data->changed_cursor == false) { + WM_cursor_modal_set(data->window, CURSOR_X_MOVE); + data->changed_cursor = true; + } + } + } + + if (but->drawflag != oldflag) { + ED_region_tag_redraw(data->region); + } +} + static int ui_do_but_NUM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) @@ -4388,6 +4279,7 @@ static int ui_do_but_NUM( my = screen_my = event->y; ui_window_to_block(data->region, block, &mx, &my); + ui_numedit_set_active(but); if (data->state == BUTTON_STATE_HIGHLIGHT) { int type = event->type, val = event->val; @@ -4401,10 +4293,14 @@ static int ui_do_but_NUM( retval = WM_UI_HANDLER_BREAK; /* allow accumulating values, otherwise scrolling gets preference */ else if (type == WHEELDOWNMOUSE && event->ctrl) { mx = but->rect.xmin; + but->drawflag &= ~UI_BUT_ACTIVE_RIGHT; + but->drawflag |= UI_BUT_ACTIVE_LEFT; click = 1; } else if (type == WHEELUPMOUSE && event->ctrl) { mx = but->rect.xmax; + but->drawflag &= ~UI_BUT_ACTIVE_LEFT; + but->drawflag |= UI_BUT_ACTIVE_RIGHT; click = 1; } else if (event->val == KM_PRESS) { @@ -4497,16 +4393,13 @@ static int ui_do_but_NUM( /* we can click on the side arrows to increment/decrement, * or click inside to edit the value directly */ float tempf, softmin, softmax; - float handlewidth; int temp; softmin = but->softmin; softmax = but->softmax; - handlewidth = min_ff(BLI_rctf_size_x(&but->rect) / 3, BLI_rctf_size_y(&but->rect)); - if (!ui_but_is_float(but)) { - if (mx < (but->rect.xmin + handlewidth)) { + if (but->drawflag & UI_BUT_ACTIVE_LEFT) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); temp = (int)data->value - 1; @@ -4517,7 +4410,7 @@ static int ui_do_but_NUM( button_activate_state(C, but, BUTTON_STATE_EXIT); } - else if (mx > (but->rect.xmax - handlewidth)) { + else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); temp = (int)data->value + 1; @@ -4533,7 +4426,7 @@ static int ui_do_but_NUM( } } else { - if (mx < (but->rect.xmin + handlewidth)) { + if (but->drawflag & UI_BUT_ACTIVE_LEFT) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); tempf = (float)data->value - (UI_PRECISION_FLOAT_SCALE * but->a1); @@ -4542,7 +4435,7 @@ static int ui_do_but_NUM( button_activate_state(C, but, BUTTON_STATE_EXIT); } - else if (mx > but->rect.xmax - handlewidth) { + else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); tempf = (float)data->value + (UI_PRECISION_FLOAT_SCALE * but->a1); @@ -5265,7 +5158,8 @@ static int ui_do_but_COLOR( if (!event->ctrl) { float color[3]; Scene *scene = CTX_data_scene(C); - Paint *paint = BKE_paint_get_active(scene); + ViewLayer *view_layer = CTX_data_view_layer(C); + Paint *paint = BKE_paint_get_active(scene, view_layer); Brush *brush = BKE_paint_brush(paint); if (brush->flag & BRUSH_USE_GRADIENT) { @@ -6182,6 +6076,7 @@ static int ui_do_but_CURVE( int mx, my, a; bool changed = false; Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); mx = event->x; my = event->y; @@ -6310,7 +6205,7 @@ static int ui_do_but_CURVE( } else { curvemapping_changed(cumap, true); /* remove doubles */ - BKE_paint_invalidate_cursor_overlay(scene, cumap); + BKE_paint_invalidate_cursor_overlay(scene, view_layer, cumap); } } @@ -6476,35 +6371,6 @@ static int ui_do_but_WAVEFORM( return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_LINK( - bContext *C, uiBut *but, - uiHandleButtonData *data, const wmEvent *event) -{ - VECCOPY2D(but->linkto, event->mval); - - if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (event->type == LEFTMOUSE && event->val == KM_PRESS) { - button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE); - return WM_UI_HANDLER_BREAK; - } - else if (event->type == LEFTMOUSE && but->block->handle) { - button_activate_state(C, but, BUTTON_STATE_EXIT); - return WM_UI_HANDLER_BREAK; - } - } - else if (data->state == BUTTON_STATE_WAIT_RELEASE) { - - if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { - if (!(but->flag & UI_SELECT)) - data->cancel = true; - button_activate_state(C, but, BUTTON_STATE_EXIT); - return WM_UI_HANDLER_BREAK; - } - } - - return WM_UI_HANDLER_CONTINUE; -} - static bool ui_numedit_but_TRACKPREVIEW( bContext *C, uiBut *but, uiHandleButtonData *data, int mx, int my, @@ -6648,6 +6514,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_HOTKEY_EVENT: retval = ui_do_but_HOTKEYEVT(C, but, data, event); break; + case UI_BTYPE_TAB: + retval = ui_do_but_TAB(C, block, but, data, event); + break; case UI_BTYPE_BUT_TOGGLE: case UI_BTYPE_TOGGLE: case UI_BTYPE_ICON_TOGGLE: @@ -6705,6 +6574,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * retval = ui_do_but_TEX(C, block, but, data, event); break; case UI_BTYPE_MENU: + case UI_BTYPE_POPOVER: case UI_BTYPE_BLOCK: case UI_BTYPE_PULLDOWN: retval = ui_do_but_BLOCK(C, but, data, event); @@ -6733,10 +6603,6 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_HSVCIRCLE: retval = ui_do_but_HSVCIRCLE(C, block, but, data, event); break; - case UI_BTYPE_LINK: - case UI_BTYPE_INLINK: - retval = ui_do_but_LINK(C, but, data, event); - break; case UI_BTYPE_TRACK_PREVIEW: retval = ui_do_but_TRACKPREVIEW(C, block, but, data, event); break; @@ -6744,6 +6610,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* quiet warnings for unhandled types */ case UI_BTYPE_SEPR: case UI_BTYPE_SEPR_LINE: + case UI_BTYPE_SEPR_SPACER: case UI_BTYPE_EXTRA: break; } @@ -6920,12 +6787,11 @@ bool ui_but_is_active(ARegion *ar) /* is called by notifier */ void UI_screen_free_active_but(const bContext *C, bScreen *screen) { - ScrArea *sa = screen->areabase.first; + wmWindow *win = CTX_wm_window(C); - for (; sa; sa = sa->next) { - ARegion *ar = sa->regionbase.first; - for (; ar; ar = ar->next) { - uiBut *but = ui_but_find_active_in_region(ar); + ED_screen_areas_iter(win, screen, area) { + for (ARegion *region = area->regionbase.first; region; region = region->next) { + uiBut *but = ui_but_find_active_in_region(region); if (but) { uiHandleButtonData *data = but->active; @@ -7011,7 +6877,7 @@ static bool ui_region_contains_point_px(ARegion *ar, int x, int y) ui_window_to_region(ar, &mx, &my); /* check if in the rect */ - if (!BLI_rcti_isect_pt(&v2d->mask, mx, my)) { + if (!BLI_rcti_isect_pt(&v2d->mask, mx, my) || UI_view2d_mouse_in_scrollers(ar, &ar->v2d, x, y)) { return false; } } @@ -7166,7 +7032,7 @@ void UI_but_tooltip_refresh(bContext *C, uiBut *but) { uiHandleButtonData *data = but->active; if (data) { - bScreen *sc = data->window->screen; + bScreen *sc = WM_window_get_active_screen(data->window); if (sc->tool_tip && sc->tool_tip->region) { WM_tooltip_refresh(C, data->window); } @@ -7232,7 +7098,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s button_tooltip_timer_reset(C, but); /* automatic open pulldown block timer */ - if (ELEM(but->type, UI_BTYPE_BLOCK, UI_BTYPE_PULLDOWN)) { + if (ELEM(but->type, UI_BTYPE_BLOCK, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER)) { if (data->used_mouse && !data->autoopentimer) { int time; @@ -7442,6 +7308,9 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA const bool horizontal = (BLI_rctf_size_x(&but->rect) < BLI_rctf_size_y(&but->rect)); WM_cursor_modal_set(data->window, horizontal ? CURSOR_X_MOVE : CURSOR_Y_MOVE); } + else if (but->type == UI_BTYPE_NUM) { + ui_numedit_set_active(but); + } } static void button_activate_exit( @@ -7535,8 +7404,13 @@ static void button_activate_exit( ui_selectcontext_end(but, &data->select_others); #endif - /* redraw (data is but->active!) */ + if (data->changed_cursor) { + WM_cursor_modal_restore(data->window); + } + + /* redraw and refresh (for popups) */ ED_region_tag_redraw(data->region); + ED_region_tag_refresh_ui(data->region); /* clean up button */ if (but->active) { @@ -7722,6 +7596,11 @@ void UI_context_update_anim_flag(const bContext *C) for (block = ar->uiblocks.first; block; block = block->next) { for (but = block->buttons.first; but; but = but->next) { ui_but_anim_flag(but, (scene) ? scene->r.cfra : 0.0f); + ui_but_override_flag(but); + if (UI_but_is_decorator(but)) { + ui_but_anim_decorate_update_from_flag(but); + } + ED_region_tag_redraw(ar); if (but->active) { @@ -7909,8 +7788,29 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) case EVT_BUT_CANCEL: data->cancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); - retval = WM_UI_HANDLER_CONTINUE; break; +#ifdef USE_UI_POPOVER_ONCE + case LEFTMOUSE: + { + if (event->val == KM_RELEASE) { + if (block->flag & UI_BLOCK_POPOVER_ONCE) { + if (!(but->flag & UI_BUT_DISABLED)) { + if (ui_but_is_popover_once_compat(but)) { + data->cancel = false; + button_activate_state(C, but, BUTTON_STATE_EXIT); + retval = WM_UI_HANDLER_BREAK; + block->handle->menuretval = UI_RETURN_OK; + } + else if (ui_but_is_editable_as_text(but)) { + ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE_TEXT_EDITING); + retval = WM_UI_HANDLER_BREAK; + } + } + } + } + break; + } +#endif case MOUSEMOVE: { uiBut *but_other = ui_but_find_mouse_over(ar, event); @@ -7949,7 +7849,6 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); } - retval = WM_UI_HANDLER_CONTINUE; break; } /* XXX hardcoded keymap check... but anyway, while view changes, tooltips should be removed */ @@ -7960,10 +7859,11 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) UI_but_tooltip_timer_remove(C, but); ATTR_FALLTHROUGH; default: - /* handle button type specific events */ - retval = ui_do_button(C, block, but, event); break; } + + /* handle button type specific events */ + retval = ui_do_button(C, block, but, event); } else if (data->state == BUTTON_STATE_WAIT_RELEASE) { switch (event->type) { @@ -7992,12 +7892,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) break; } case MOUSEMOVE: - if (ELEM(but->type, UI_BTYPE_LINK, UI_BTYPE_INLINK)) { - but->flag |= UI_SELECT; - ui_do_button(C, block, but, event); - ED_region_tag_redraw(ar); - } - else { + { /* deselect the button when moving the mouse away */ /* also de-activate for buttons that only show higlights */ if (ui_but_contains_point_px(ar, but, event->x, event->y)) { @@ -8260,13 +8155,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *ar, } if (redraw) { - if (listbox->block->flag & UI_BLOCK_POPUP) { - /* popups need special refreshing */ - ED_region_tag_refresh_ui(ar); - } - else { - ED_region_tag_redraw(ar); - } + ED_region_tag_redraw(ar); + ED_region_tag_refresh_ui(ar); } return retval; @@ -8525,6 +8415,9 @@ static int ui_menu_scroll(ARegion *ar, uiBlock *block, int my, uiBut *to_bt) dy = block->rect.ymin - ymin + UI_MENU_SCROLL_PAD; } + /* remember scroll offset for refreshes */ + block->handle->scrolloffset += dy; + /* apply scroll offset */ for (bt = block->buttons.first; bt; bt = bt->next) { bt->rect.ymin += dy; @@ -8677,7 +8570,7 @@ static int ui_handle_menu_event( add_v2_v2v2_int(menu->popup_create_vars.event_xy, menu->popup_create_vars.event_xy, mdiff); - ui_popup_translate(C, ar, mdiff); + ui_popup_translate(ar, mdiff); } return retval; @@ -9061,6 +8954,15 @@ static int ui_handle_menu_event( retval = ui_handle_menu_button(C, event, menu); } +#ifdef USE_UI_POPOVER_ONCE + if (block->flag & UI_BLOCK_POPOVER_ONCE) { + if ((event->type == LEFTMOUSE) && (event->val == KM_RELEASE)) { + UI_popover_once_clear(menu->popup_create_vars.arg); + block->flag &= ~UI_BLOCK_POPOVER_ONCE; + } + } +#endif + /* Don't handle double click events, rehandle as regular press/release. */ if (retval == WM_UI_HANDLER_CONTINUE && event->val == KM_DBL_CLICK) { return retval; @@ -9680,10 +9582,10 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE if ((data->state == BUTTON_STATE_MENU_OPEN) && (is_inside_menu == false) && /* make sure mouse isn't inside another menu (see T43247) */ - (but->type == UI_BTYPE_PULLDOWN) && + (ELEM(but->type, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER)) && (but_other = ui_but_find_mouse_over(ar, event)) && (but != but_other) && - (but->type == but_other->type)) + (ELEM(but_other->type, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER))) { /* if mouse moves to a different root-level menu button, * open it to replace the current menu */ diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index eed7f2ec0f2..22b82898288 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -33,12 +33,16 @@ #include "MEM_guardedalloc.h" -#include "GPU_extensions.h" -#include "GPU_basic_shader.h" +#include "GPU_draw.h" +#include "GPU_matrix.h" +#include "GPU_batch.h" +#include "GPU_immediate.h" +#include "GPU_state.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_fileops_types.h" +#include "BLI_math_vector.h" #include "DNA_brush_types.h" #include "DNA_curve_types.h" @@ -46,6 +50,7 @@ #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "DNA_workspace_types.h" #include "RNA_access.h" #include "RNA_enum_types.h" @@ -54,14 +59,18 @@ #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_appdir.h" +#include "BKE_studiolight.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_thumbs.h" -#include "BIF_gl.h" #include "BIF_glutil.h" +#include "DEG_depsgraph.h" + +#include "DRW_engine.h" + #include "ED_datafiles.h" #include "ED_keyframes_draw.h" #include "ED_render.h" @@ -69,6 +78,9 @@ #include "UI_interface.h" #include "UI_interface_icons.h" +#include "WM_api.h" +#include "WM_types.h" + #include "interface_intern.h" #ifndef WITH_HEADLESS @@ -90,10 +102,13 @@ typedef struct IconImage { typedef void (*VectorDrawFunc)(int x, int y, int w, int h, float alpha); -#define ICON_TYPE_PREVIEW 0 -#define ICON_TYPE_TEXTURE 1 -#define ICON_TYPE_BUFFER 2 -#define ICON_TYPE_VECTOR 3 +#define ICON_TYPE_PREVIEW 0 +#define ICON_TYPE_TEXTURE 1 +#define ICON_TYPE_MONO_TEXTURE 2 +#define ICON_TYPE_BUFFER 3 +#define ICON_TYPE_VECTOR 4 +#define ICON_TYPE_GEOM 5 +#define ICON_TYPE_EVENT 6 /* draw keymap entries using custom renderer. */ typedef struct DrawInfo { int type; @@ -104,11 +119,22 @@ typedef struct DrawInfo { VectorDrawFunc func; } vector; struct { + ImBuf *image_cache; + } geom; + struct { IconImage *image; } buffer; struct { int x, y, w, h; } texture; + struct { + /* Can be packed into a single int. */ + short event_type; + short event_value; + int icon; + /* Allow lookups. */ + struct DrawInfo *next; + } input; } data; } DrawInfo; @@ -144,7 +170,7 @@ static DrawInfo *def_internal_icon(ImBuf *bbuf, int icon_id, int xofs, int yofs, di = MEM_callocN(sizeof(DrawInfo), "drawinfo"); di->type = type; - if (type == ICON_TYPE_TEXTURE) { + if (ELEM(type, ICON_TYPE_TEXTURE, ICON_TYPE_MONO_TEXTURE)) { di->data.texture.x = xofs; di->data.texture.y = yofs; di->data.texture.w = size; @@ -224,13 +250,17 @@ static void vicon_small_tri_right_draw(int x, int y, int w, int UNUSED(h), float viconutil_set_point(pts[1], cx - d2, cy - d); viconutil_set_point(pts[2], cx + d2, cy); - glColor4f(0.2f, 0.2f, 0.2f, alpha); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4f(0.2f, 0.2f, 0.2f, alpha); + + immBegin(GPU_PRIM_TRIS, 3); + immVertex2iv(pos, pts[0]); + immVertex2iv(pos, pts[1]); + immVertex2iv(pos, pts[2]); + immEnd(); - glBegin(GL_TRIANGLES); - glVertex2iv(pts[0]); - glVertex2iv(pts[1]); - glVertex2iv(pts[2]); - glEnd(); + immUnbindProgram(); } static void vicon_keytype_draw_wrapper(int x, int y, int w, int h, float alpha, short key_type) @@ -239,7 +269,6 @@ static void vicon_keytype_draw_wrapper(int x, int y, int w, int h, float alpha, * (since we're doing this offscreen, free from any particular space_id) */ struct bThemeState theme_state; - int xco, yco; UI_Theme_Store(&theme_state); UI_SetTheme(SPACE_ACTION, RGN_TYPE_WINDOW); @@ -248,15 +277,29 @@ static void vicon_keytype_draw_wrapper(int x, int y, int w, int h, float alpha, * while the draw_keyframe_shape() function needs the midpoint for * the keyframe */ - xco = x + w / 2; - yco = y + h / 2; + int xco = x + w / 2; + int yco = y + h / 2; + + GPUVertFormat *format = immVertexFormat(); + uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + uint outline_color_id = GPU_vertformat_attr_add(format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + GPU_enable_program_point_size(); + immBegin(GPU_PRIM_POINTS, 1); /* draw keyframe - * - xscale: 1.0 (since there's no timeline scaling to compensate for) - * - yscale: 0.3 * h (found out experimentally... dunno why!) + * - size: 0.6 * h (found out experimentally... dunno why!) * - sel: true (so that "keyframe" state shows the iconic yellow icon) */ - draw_keyframe_shape(xco, yco, 1.0f, 0.3f * h, true, key_type, KEYFRAME_SHAPE_BOTH, alpha); + draw_keyframe_shape(xco, yco, 0.6f * h, true, key_type, KEYFRAME_SHAPE_BOTH, alpha, + pos_id, size_id, color_id, outline_color_id); + + immEnd(); + GPU_disable_program_point_size(); + immUnbindProgram(); UI_Theme_Restore(&theme_state); } @@ -300,18 +343,23 @@ static void vicon_colorset_draw(int index, int x, int y, int w, int h, float UNU const int b = x + w / 3 * 2; const int c = x + w; + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + /* XXX: Include alpha into this... */ /* normal */ - glColor3ubv((unsigned char *)cs->solid); - glRecti(x, y, a, y + h); + immUniformColor3ubv((unsigned char *)cs->solid); + immRecti(pos, x, y, a, y + h); /* selected */ - glColor3ubv((unsigned char *)cs->select); - glRecti(a, y, b, y + h); + immUniformColor3ubv((unsigned char *)cs->select); + immRecti(pos, a, y, b, y + h); /* active */ - glColor3ubv((unsigned char *)cs->active); - glRecti(b, y, c, y + h); + immUniformColor3ubv((unsigned char *)cs->active); + immRecti(pos, b, y, c, y + h); + + immUnbindProgram(); } #define DEF_VICON_COLORSET_DRAW_NTH(prefix, index) \ @@ -398,6 +446,143 @@ static void init_brush_icons(void) #undef INIT_BRUSH_ICON } +static DrawInfo *g_di_event_list = NULL; + +int UI_icon_from_event_type(short event_type, short event_value) +{ + if (event_type == RIGHTSHIFTKEY) { + event_type = LEFTSHIFTKEY; + } + else if (event_type == RIGHTCTRLKEY) { + event_type = LEFTCTRLKEY; + } + else if (event_type == RIGHTALTKEY) { + event_type = LEFTALTKEY; + } + else if (event_type == EVT_TWEAK_L) { + event_type = LEFTMOUSE; + event_value = KM_CLICK_DRAG; + } + else if (event_type == EVT_TWEAK_M) { + event_type = MIDDLEMOUSE; + event_value = KM_CLICK_DRAG; + } + else if (event_type == EVT_TWEAK_R) { + event_type = RIGHTMOUSE; + event_value = KM_CLICK_DRAG; + } + + DrawInfo *di = g_di_event_list; + do { + if (di->data.input.event_type == event_type) { + return di->data.input.icon; + } + } while ((di = di->data.input.next)); + + if (event_type == LEFTMOUSE) { + return ELEM(event_value, KM_CLICK, KM_PRESS) ? ICON_MOUSE_LMB : ICON_MOUSE_LMB_DRAG; + } + else if (event_type == MIDDLEMOUSE) { + return ELEM(event_value, KM_CLICK, KM_PRESS) ? ICON_MOUSE_MMB : ICON_MOUSE_MMB_DRAG; + } + else if (event_type == RIGHTMOUSE) { + return ELEM(event_value, KM_CLICK, KM_PRESS) ? ICON_MOUSE_RMB : ICON_MOUSE_RMB_DRAG; + } + + return ICON_NONE; +} + +int UI_icon_from_keymap_item(const wmKeyMapItem *kmi, int r_icon_mod[4]) +{ + if (r_icon_mod) { + memset(r_icon_mod, 0x0, sizeof(int[4])); + int i = 0; + if (!ELEM(kmi->ctrl, KM_NOTHING, KM_ANY)) { + r_icon_mod[i++] = ICON_EVENT_CTRL; + } + if (!ELEM(kmi->alt, KM_NOTHING, KM_ANY)) { + r_icon_mod[i++] = ICON_EVENT_ALT; + } + if (!ELEM(kmi->shift, KM_NOTHING, KM_ANY)) { + r_icon_mod[i++] = ICON_EVENT_SHIFT; + } + if (!ELEM(kmi->oskey, KM_NOTHING, KM_ANY)) { + r_icon_mod[i++] = ICON_EVENT_OS; + } + } + return UI_icon_from_event_type(kmi->type, kmi->val); +} + +static void init_event_icons(void) +{ + DrawInfo *di_next = NULL; + +#define INIT_EVENT_ICON(icon_id, type, value) \ + { \ + DrawInfo *di = def_internal_icon(NULL, icon_id, 0, 0, w, ICON_TYPE_EVENT); \ + di->data.input.event_type = type; \ + di->data.input.event_value = value; \ + di->data.input.icon = icon_id; \ + di->data.input.next = di_next; \ + di_next = di; \ + } + /* end INIT_EVENT_ICON */ + + const int w = 16; /* DUMMY */ + + INIT_EVENT_ICON(ICON_EVENT_A, AKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_B, BKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_C, CKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_D, DKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_E, EKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F, FKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_G, GKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_H, HKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_I, IKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_J, JKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_K, KKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_L, LKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_M, MKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_N, NKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_O, OKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_P, PKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_Q, QKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_R, RKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_S, SKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_T, TKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_U, UKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_V, VKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_W, WKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_X, XKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_Y, YKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_Z, ZKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_SHIFT, LEFTSHIFTKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_CTRL, LEFTCTRLKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_ALT, LEFTALTKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_OS, OSKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F1, F1KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F2, F2KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F3, F3KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F4, F4KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F5, F5KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F6, F6KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F7, F7KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F8, F8KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F9, F9KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F10, F10KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F11, F11KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_F12, F12KEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_ESC, ESCKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_TAB, TABKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_PAGEUP, PAGEUPKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_PAGEDOWN, PAGEDOWNKEY, KM_ANY); + INIT_EVENT_ICON(ICON_EVENT_RETURN, RETKEY, KM_ANY); + + g_di_event_list = di_next; + +#undef INIT_EVENT_ICON +} + static void icon_verify_datatoc(IconImage *iimg) { /* if it has own rect, things are all OK */ @@ -417,54 +602,11 @@ static void icon_verify_datatoc(IconImage *iimg) } } -static void init_matcap_icons(void) -{ - /* dynamic allocation now, tucking datatoc pointers in DrawInfo */ -#define INIT_MATCAP_ICON(icon_id, name) \ - { \ - unsigned char *rect = (unsigned char *)datatoc_ ##name## _jpg; \ - int size = datatoc_ ##name## _jpg_size; \ - DrawInfo *di; \ - \ - di = def_internal_icon(NULL, icon_id, 0, 0, 96, ICON_TYPE_BUFFER); \ - di->data.buffer.image->datatoc_rect = rect; \ - di->data.buffer.image->datatoc_size = size; \ - } (void)0 - - INIT_MATCAP_ICON(ICON_MATCAP_01, mc01); - INIT_MATCAP_ICON(ICON_MATCAP_02, mc02); - INIT_MATCAP_ICON(ICON_MATCAP_03, mc03); - INIT_MATCAP_ICON(ICON_MATCAP_04, mc04); - INIT_MATCAP_ICON(ICON_MATCAP_05, mc05); - INIT_MATCAP_ICON(ICON_MATCAP_06, mc06); - INIT_MATCAP_ICON(ICON_MATCAP_07, mc07); - INIT_MATCAP_ICON(ICON_MATCAP_08, mc08); - INIT_MATCAP_ICON(ICON_MATCAP_09, mc09); - INIT_MATCAP_ICON(ICON_MATCAP_10, mc10); - INIT_MATCAP_ICON(ICON_MATCAP_11, mc11); - INIT_MATCAP_ICON(ICON_MATCAP_12, mc12); - INIT_MATCAP_ICON(ICON_MATCAP_13, mc13); - INIT_MATCAP_ICON(ICON_MATCAP_14, mc14); - INIT_MATCAP_ICON(ICON_MATCAP_15, mc15); - INIT_MATCAP_ICON(ICON_MATCAP_16, mc16); - INIT_MATCAP_ICON(ICON_MATCAP_17, mc17); - INIT_MATCAP_ICON(ICON_MATCAP_18, mc18); - INIT_MATCAP_ICON(ICON_MATCAP_19, mc19); - INIT_MATCAP_ICON(ICON_MATCAP_20, mc20); - INIT_MATCAP_ICON(ICON_MATCAP_21, mc21); - INIT_MATCAP_ICON(ICON_MATCAP_22, mc22); - INIT_MATCAP_ICON(ICON_MATCAP_23, mc23); - INIT_MATCAP_ICON(ICON_MATCAP_24, mc24); - -#undef INIT_MATCAP_ICON - -} - static void init_internal_icons(void) { // bTheme *btheme = UI_GetTheme(); ImBuf *b16buf = NULL, *b32buf = NULL; - int x, y, icontype; + int x, y; #if 0 // temp disabled if ((btheme != NULL) && btheme->tui.iconfile[0]) { @@ -498,57 +640,46 @@ static void init_internal_icons(void) IMB_premultiply_alpha(b32buf); if (b16buf && b32buf) { - /* free existing texture if any */ + /* Free existing texture if any. */ if (icongltex.id) { glDeleteTextures(1, &icongltex.id); icongltex.id = 0; } - /* we only use a texture for cards with non-power of two */ - if (GPU_full_non_power_of_two_support()) { - glGenTextures(1, &icongltex.id); + /* Allocate OpenGL texture. */ + glGenTextures(1, &icongltex.id); - if (icongltex.id) { - int level = 2; - - icongltex.w = b32buf->x; - icongltex.h = b32buf->y; - icongltex.invw = 1.0f / b32buf->x; - icongltex.invh = 1.0f / b32buf->y; + if (icongltex.id) { + int level = 2; - glBindTexture(GL_TEXTURE_2D, icongltex.id); + icongltex.w = b32buf->x; + icongltex.h = b32buf->y; + icongltex.invw = 1.0f / b32buf->x; + icongltex.invh = 1.0f / b32buf->y; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, b32buf->x, b32buf->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, b32buf->rect); - glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, b16buf->x, b16buf->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, b16buf->rect); + glBindTexture(GL_TEXTURE_2D, icongltex.id); - while (b16buf->x > 1) { - ImBuf *nbuf = IMB_onehalf(b16buf); - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, nbuf->x, nbuf->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nbuf->rect); - level++; - IMB_freeImBuf(b16buf); - b16buf = nbuf; - } + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, b32buf->x, b32buf->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, b32buf->rect); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, b16buf->x, b16buf->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, b16buf->rect); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + while (b16buf->x > 1) { + ImBuf *nbuf = IMB_onehalf(b16buf); + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, nbuf->x, nbuf->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nbuf->rect); + level++; + IMB_freeImBuf(b16buf); + b16buf = nbuf; + } - glBindTexture(GL_TEXTURE_2D, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (glGetError() == GL_OUT_OF_MEMORY) { - glDeleteTextures(1, &icongltex.id); - icongltex.id = 0; - } - } + glBindTexture(GL_TEXTURE_2D, 0); } - } - - if (icongltex.id) - icontype = ICON_TYPE_TEXTURE; - else - icontype = ICON_TYPE_BUFFER; - if (b32buf) { + /* Define icons. */ for (y = 0; y < ICON_GRID_ROWS; y++) { + /* Row W has monochrome icons. */ + int icontype = (y == 8) ? ICON_TYPE_MONO_TEXTURE : ICON_TYPE_TEXTURE; for (x = 0; x < ICON_GRID_COLS; x++) { def_internal_icon(b32buf, BIFICONID_FIRST + y * ICON_GRID_COLS + x, x * (ICON_GRID_W + ICON_GRID_MARGIN) + ICON_GRID_MARGIN, @@ -717,21 +848,53 @@ void UI_icons_free_drawinfo(void *drawinfo) MEM_freeN(di->data.buffer.image); } } + else if (di->type == ICON_TYPE_GEOM) { + if (di->data.geom.image_cache) { + IMB_freeImBuf(di->data.geom.image_cache); + } + } MEM_freeN(di); } } -static DrawInfo *icon_create_drawinfo(void) +/** + * #Icon.data_type and #Icon.obj + */ +static DrawInfo *icon_create_drawinfo(Icon *icon) { + int icon_data_type = icon->obj_type; DrawInfo *di = NULL; di = MEM_callocN(sizeof(DrawInfo), "di_icon"); - di->type = ICON_TYPE_PREVIEW; + + if (ELEM(icon_data_type, ICON_DATA_ID, ICON_DATA_PREVIEW)) { + di->type = ICON_TYPE_PREVIEW; + } + else if (icon_data_type == ICON_DATA_GEOM) { + di->type = ICON_TYPE_GEOM; + } + else if (icon_data_type == ICON_DATA_STUDIOLIGHT) { + di->type = ICON_TYPE_BUFFER; + } + else { + BLI_assert(0); + } return di; } +static DrawInfo *icon_ensure_drawinfo(Icon *icon) +{ + if (icon->drawinfo) { + return icon->drawinfo; + } + DrawInfo *di = icon_create_drawinfo(icon); + icon->drawinfo = di; + icon->drawinfo_free = UI_icons_free_drawinfo; + return di; +} + /* note!, returns unscaled by DPI */ int UI_icon_get_width(int icon_id) { @@ -746,40 +909,27 @@ int UI_icon_get_width(int icon_id) return 0; } - di = (DrawInfo *)icon->drawinfo; - if (!di) { - di = icon_create_drawinfo(); - icon->drawinfo = di; - } - - if (di) + di = icon_ensure_drawinfo(icon); + if (di) { return ICON_DEFAULT_WIDTH; + } return 0; } int UI_icon_get_height(int icon_id) { - Icon *icon = NULL; - DrawInfo *di = NULL; - - icon = BKE_icon_get(icon_id); - + Icon *icon = BKE_icon_get(icon_id); if (icon == NULL) { if (G.debug & G_DEBUG) printf("%s: Internal error, no icon for icon ID: %d\n", __func__, icon_id); return 0; } - di = (DrawInfo *)icon->drawinfo; - - if (!di) { - di = icon_create_drawinfo(); - icon->drawinfo = di; - } - - if (di) + DrawInfo *di = icon_ensure_drawinfo(icon); + if (di) { return ICON_DEFAULT_HEIGHT; + } return 0; } @@ -791,7 +941,7 @@ void UI_icons_init(int first_dyn_id) init_iconfile_list(&iconfilelist); init_internal_icons(); init_brush_icons(); - init_matcap_icons(); + init_event_icons(); #endif } @@ -831,19 +981,47 @@ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size) static void ui_id_preview_image_render_size( const bContext *C, Scene *scene, ID *id, PreviewImage *pi, int size, const bool use_job); -void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool big) +static void ui_studiolight_icon_job_exec(void *customdata, short *UNUSED(stop), short *UNUSED(do_update), float *UNUSED(progress)) +{ + Icon **tmp = (Icon **)customdata; + Icon *icon = *tmp; + DrawInfo *di = icon_ensure_drawinfo(icon); + StudioLight *sl = icon->obj; + BKE_studiolight_preview(di->data.buffer.image->rect, sl, icon->id_type); +} + +static void ui_studiolight_kill_icon_preview_job(wmWindowManager *wm, int icon_id) { Icon *icon = BKE_icon_get(icon_id); + WM_jobs_kill_type(wm, icon, WM_JOB_TYPE_STUDIOLIGHT); + icon->obj = NULL; +} - if (icon) { - DrawInfo *di = (DrawInfo *)icon->drawinfo; +static void ui_studiolight_free_function(StudioLight *sl, void *data) +{ + wmWindowManager *wm = data; - if (!di) { - di = icon_create_drawinfo(); + // get icons_id, get icons and kill wm jobs + if (sl->icon_id_radiance) { + ui_studiolight_kill_icon_preview_job(wm, sl->icon_id_radiance); + } + if (sl->icon_id_irradiance) { + ui_studiolight_kill_icon_preview_job(wm, sl->icon_id_irradiance); + } + if (sl->icon_id_matcap) { + ui_studiolight_kill_icon_preview_job(wm, sl->icon_id_matcap); + } + if (sl->icon_id_matcap_flipped) { + ui_studiolight_kill_icon_preview_job(wm, sl->icon_id_matcap_flipped); + } +} - icon->drawinfo = di; - icon->drawinfo_free = UI_icons_free_drawinfo; - } +void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool big) +{ + Icon *icon = BKE_icon_get(icon_id); + + if (icon) { + DrawInfo *di = icon_ensure_drawinfo(icon); if (di) { switch (di->type) { @@ -851,12 +1029,42 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi { ID *id = (icon->id_type != 0) ? icon->obj : NULL; PreviewImage *prv = id ? BKE_previewimg_id_ensure(id) : icon->obj; + /* Using jobs for screen previews crashes due to offscreen rendering. + * XXX would be nicer if PreviewImage could store if it supports jobs */ + const bool use_jobs = !id || (GS(id->name) != ID_SCR); if (prv) { const int size = big ? ICON_SIZE_PREVIEW : ICON_SIZE_ICON; if (id || (prv->tag & PRV_TAG_DEFFERED) != 0) { - ui_id_preview_image_render_size(C, NULL, id, prv, size, true); + ui_id_preview_image_render_size(C, NULL, id, prv, size, use_jobs); + } + } + break; + } + case ICON_TYPE_BUFFER: + { + if (icon->obj_type == ICON_DATA_STUDIOLIGHT) { + if (di->data.buffer.image == NULL) { + wmWindowManager *wm = CTX_wm_manager(C); + StudioLight *sl = icon->obj; + BKE_studiolight_set_free_function(sl, &ui_studiolight_free_function, wm); + IconImage *img = MEM_mallocN(sizeof(IconImage), __func__); + + img->w = STUDIOLIGHT_ICON_SIZE; + img->h = STUDIOLIGHT_ICON_SIZE; + size_t size = STUDIOLIGHT_ICON_SIZE * STUDIOLIGHT_ICON_SIZE * sizeof(uint); + img->rect = MEM_mallocN(size, __func__); + memset(img->rect, 0, size); + di->data.buffer.image = img; + + wmJob *wm_job = WM_jobs_get(wm, CTX_wm_window(C), icon, "StudioLight Icon", 0, WM_JOB_TYPE_STUDIOLIGHT); + Icon **tmp = MEM_callocN(sizeof(Icon *), __func__); + *tmp = icon; + WM_jobs_customdata_set(wm_job, tmp, MEM_freeN); + WM_jobs_timer(wm_job, 0.01, 0, NC_WINDOW); + WM_jobs_callbacks(wm_job, ui_studiolight_icon_job_exec, NULL, NULL, NULL); + WM_jobs_start(CTX_wm_manager(C), wm_job); } } break; @@ -936,7 +1144,7 @@ PreviewImage *UI_icon_to_preview(int icon_id) } static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect), int rw, int rh, - unsigned int *rect, float alpha, const float rgb[3], const bool is_preview) + unsigned int *rect, float alpha, const float rgb[3], const float desaturate) { ImBuf *ima = NULL; int draw_w = w; @@ -950,15 +1158,13 @@ static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect), BLI_assert(!"invalid icon size"); return; } - /* modulate color */ - if (alpha != 1.0f) - glPixelTransferf(GL_ALPHA_SCALE, alpha); + float col[4] = {1.0f, 1.0f, 1.0f, alpha}; if (rgb) { - glPixelTransferf(GL_RED_SCALE, rgb[0]); - glPixelTransferf(GL_GREEN_SCALE, rgb[1]); - glPixelTransferf(GL_BLUE_SCALE, rgb[2]); + col[0] = rgb[0]; + col[1] = rgb[1]; + col[2] = rgb[2]; } /* rect contains image in 'rendersize', we only scale if needed */ @@ -983,72 +1189,169 @@ static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect), rect = ima->rect; } + /* We need to flush widget base first to ensure correct ordering. */ + UI_widgetbase_draw_cache_flush(); + /* draw */ - if (is_preview) { - glaDrawPixelsSafe(draw_x, draw_y, draw_w, draw_h, draw_w, GL_RGBA, GL_UNSIGNED_BYTE, rect); + GPUBuiltinShader shader; + if (desaturate != 0.0f) { + shader = GPU_SHADER_2D_IMAGE_DESATURATE_COLOR; } else { - int bound_options; - GPU_BASIC_SHADER_DISABLE_AND_STORE(bound_options); - - glRasterPos2f(draw_x, draw_y); - glDrawPixels(draw_w, draw_h, GL_RGBA, GL_UNSIGNED_BYTE, rect); + shader = GPU_SHADER_2D_IMAGE_COLOR; + } + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(shader); - GPU_BASIC_SHADER_ENABLE_AND_RESTORE(bound_options); + if (shader == GPU_SHADER_2D_IMAGE_DESATURATE_COLOR) { + immUniform1f("factor", desaturate); } + immDrawPixelsTex(&state, draw_x, draw_y, draw_w, draw_h, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, rect, + 1.0f, 1.0f, col); + if (ima) IMB_freeImBuf(ima); +} - /* restore color */ - if (alpha != 0.0f) - glPixelTransferf(GL_ALPHA_SCALE, 1.0f); +/* High enough to make a difference, low enough so that + * small draws are still efficient with the use of glUniform. + * NOTE TODO: We could use UBO but we would need some triple + * buffer system + persistent mapping for this to be more + * efficient than simple glUniform calls. */ +#define ICON_DRAW_CACHE_SIZE 16 + +typedef struct IconDrawCall { + rctf pos; + rctf tex; + float color[4]; +} IconDrawCall; + +static struct { + IconDrawCall drawcall_cache[ICON_DRAW_CACHE_SIZE]; + int calls; /* Number of calls batched together */ + bool enabled; + float mat[4][4]; +} g_icon_draw_cache = {{{{0}}}}; + +void UI_icon_draw_cache_begin(void) +{ + BLI_assert(g_icon_draw_cache.enabled == false); + g_icon_draw_cache.enabled = true; +} - if (rgb) { - glPixelTransferf(GL_RED_SCALE, 1.0f); - glPixelTransferf(GL_GREEN_SCALE, 1.0f); - glPixelTransferf(GL_BLUE_SCALE, 1.0f); +static void icon_draw_cache_flush_ex(void) +{ + if (g_icon_draw_cache.calls == 0) + return; + + /* We need to flush widget base first to ensure correct ordering. */ + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + UI_widgetbase_draw_cache_flush(); + + GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, icongltex.id); + + GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR); + GPU_shader_bind(shader); + + int img_loc = GPU_shader_get_uniform(shader, "image"); + int data_loc = GPU_shader_get_uniform(shader, "calls_data[0]"); + + glUniform1i(img_loc, 0); + glUniform4fv(data_loc, ICON_DRAW_CACHE_SIZE * 3, (float *)g_icon_draw_cache.drawcall_cache); + + GPU_draw_primitive(GPU_PRIM_TRIS, 6 * g_icon_draw_cache.calls); + + glBindTexture(GL_TEXTURE_2D, 0); + + g_icon_draw_cache.calls = 0; +} + +void UI_icon_draw_cache_end(void) +{ + BLI_assert(g_icon_draw_cache.enabled == true); + g_icon_draw_cache.enabled = false; + + /* Don't change blend state if it's not needed. */ + if (g_icon_draw_cache.calls == 0) + return; + + GPU_blend(true); + + icon_draw_cache_flush_ex(); + + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(false); +} + +static void icon_draw_texture_cached( + float x, float y, float w, float h, int ix, int iy, + int UNUSED(iw), int ih, float alpha, const float rgb[3]) +{ + + float mvp[4][4]; + GPU_matrix_model_view_projection_get(mvp); + + IconDrawCall *call = &g_icon_draw_cache.drawcall_cache[g_icon_draw_cache.calls]; + g_icon_draw_cache.calls++; + + /* Manual mat4*vec2 */ + call->pos.xmin = x * mvp[0][0] + y * mvp[1][0] + mvp[3][0]; + call->pos.ymin = x * mvp[0][1] + y * mvp[1][1] + mvp[3][1]; + call->pos.xmax = call->pos.xmin + w * mvp[0][0] + h * mvp[1][0]; + call->pos.ymax = call->pos.ymin + w * mvp[0][1] + h * mvp[1][1]; + + call->tex.xmin = ix * icongltex.invw; + call->tex.xmax = (ix + ih) * icongltex.invw; + call->tex.ymin = iy * icongltex.invh; + call->tex.ymax = (iy + ih) * icongltex.invh; + + if (rgb) copy_v4_fl4(call->color, rgb[0], rgb[1], rgb[2], alpha); + else copy_v4_fl(call->color, alpha); + + if (g_icon_draw_cache.calls == ICON_DRAW_CACHE_SIZE) { + icon_draw_cache_flush_ex(); } } static void icon_draw_texture( float x, float y, float w, float h, int ix, int iy, - int UNUSED(iw), int ih, float alpha, const float rgb[3]) + int iw, int ih, float alpha, const float rgb[3]) { - float x1, x2, y1, y2; + if (g_icon_draw_cache.enabled) { + icon_draw_texture_cached(x, y, w, h, ix, iy, iw, ih, alpha, rgb); + return; + } - if (rgb) glColor4f(rgb[0], rgb[1], rgb[2], alpha); - else glColor4f(alpha, alpha, alpha, alpha); + /* We need to flush widget base first to ensure correct ordering. */ + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + UI_widgetbase_draw_cache_flush(); + + float x1, x2, y1, y2; x1 = ix * icongltex.invw; x2 = (ix + ih) * icongltex.invw; y1 = iy * icongltex.invh; y2 = (iy + ih) * icongltex.invh; - GPU_basic_shader_bind(GPU_SHADER_TEXTURE_2D | GPU_SHADER_USE_COLOR); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, icongltex.id); - /* sharper downscaling, has no effect when scale matches with a mip level */ - glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -0.5f); + GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR); + GPU_shader_bind(shader); - glBegin(GL_QUADS); - glTexCoord2f(x1, y1); - glVertex2f(x, y); + if (rgb) glUniform4f(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR), rgb[0], rgb[1], rgb[2], alpha); + else glUniform4f(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR), alpha, alpha, alpha, alpha); - glTexCoord2f(x2, y1); - glVertex2f(x + w, y); + glUniform1i(GPU_shader_get_uniform(shader, "image"), 0); + glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"), x1, y1, x2, y2); + glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"), x, y, x + w, y + h); - glTexCoord2f(x2, y2); - glVertex2f(x + w, y + h); - - glTexCoord2f(x1, y2); - glVertex2f(x, y + h); - glEnd(); - - glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.0f); + GPU_draw_primitive(GPU_PRIM_TRI_STRIP, 4); glBindTexture(GL_TEXTURE_2D, 0); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); } /* Drawing size for preview images */ @@ -1068,11 +1371,10 @@ static int get_draw_size(enum eIconSizes size) static void icon_draw_size( float x, float y, int icon_id, float aspect, float alpha, const float rgb[3], - enum eIconSizes size, int draw_size, const bool UNUSED(nocreate), const bool is_preview) + enum eIconSizes size, int draw_size, const float desaturate) { bTheme *btheme = UI_GetTheme(); Icon *icon = NULL; - DrawInfo *di = NULL; IconImage *iimg; const float fdraw_size = (float)draw_size; int w, h; @@ -1086,31 +1388,77 @@ static void icon_draw_size( return; } - di = (DrawInfo *)icon->drawinfo; - - if (!di) { - di = icon_create_drawinfo(); - - icon->drawinfo = di; - icon->drawinfo_free = UI_icons_free_drawinfo; - } - /* scale width and height according to aspect */ w = (int)(fdraw_size / aspect + 0.5f); h = (int)(fdraw_size / aspect + 0.5f); + DrawInfo *di = icon_ensure_drawinfo(icon); + if (di->type == ICON_TYPE_VECTOR) { + /* We need to flush widget base first to ensure correct ordering. */ + UI_widgetbase_draw_cache_flush(); /* vector icons use the uiBlock transformation, they are not drawn * with untransformed coordinates like the other icons */ di->data.vector.func((int)x, (int)y, w, h, 1.0f); } + else if (di->type == ICON_TYPE_GEOM) { + /* We need to flush widget base first to ensure correct ordering. */ + UI_widgetbase_draw_cache_flush(); + +#ifdef USE_UI_TOOLBAR_HACK + /* TODO(campbell): scale icons up for toolbar, we need a way to detect larger buttons and do this automatic. */ + { + float scale = (float)ICON_DEFAULT_HEIGHT_TOOLBAR / (float)ICON_DEFAULT_HEIGHT; + y = (y + (h / 2)) - ((h * scale) / 2); + w *= scale; + h *= scale; + } +#endif + + /* This could re-generate often if rendered at different sizes in the one interface. + * TODO(campbell): support caching multiple sizes. */ + ImBuf *ibuf = di->data.geom.image_cache; + if ((ibuf == NULL) || + (ibuf->x != w) || + (ibuf->y != h)) + { + if (ibuf) { + IMB_freeImBuf(ibuf); + } + ibuf = BKE_icon_geom_rasterize(icon->obj, w, h); + di->data.geom.image_cache = ibuf; + } + glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + icon_draw_rect(x, y, w, h, aspect, w, h, ibuf->rect, alpha, rgb, desaturate); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + } + else if (di->type == ICON_TYPE_EVENT) { + const short event_type = di->data.input.event_type; + const short event_value = di->data.input.event_value; + icon_draw_rect_input(x, y, w, h, alpha, event_type, event_value); + } else if (di->type == ICON_TYPE_TEXTURE) { /* texture image use premul alpha for correct scaling */ - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); icon_draw_texture(x, y, (float)w, (float)h, di->data.texture.x, di->data.texture.y, di->data.texture.w, di->data.texture.h, alpha, rgb); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); } + else if (di->type == ICON_TYPE_MONO_TEXTURE) { + /* icon that matches text color, assumed to be white */ + float text_color[4]; + UI_GetThemeColor4fv(TH_TEXT, text_color); + if (rgb) { + mul_v3_v3(text_color, rgb); + } + text_color[3] *= alpha; + + GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + icon_draw_texture(x, y, (float)w, (float)h, di->data.texture.x, di->data.texture.y, + di->data.texture.w, di->data.texture.h, text_color[3], text_color); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + } + else if (di->type == ICON_TYPE_BUFFER) { /* it is a builtin icon */ iimg = di->data.buffer.image; @@ -1119,9 +1467,9 @@ static void icon_draw_size( #endif if (!iimg->rect) return; /* something has gone wrong! */ - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - icon_draw_rect(x, y, w, h, aspect, iimg->w, iimg->h, iimg->rect, alpha, rgb, is_preview); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + icon_draw_rect(x, y, w, h, aspect, iimg->w, iimg->h, iimg->rect, alpha, rgb, desaturate); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); } else if (di->type == ICON_TYPE_PREVIEW) { PreviewImage *pi = (icon->id_type != 0) ? BKE_previewimg_id_ensure((ID *)icon->obj) : icon->obj; @@ -1131,10 +1479,10 @@ static void icon_draw_size( if (!pi->rect[size]) return; /* something has gone wrong! */ /* preview images use premul alpha ... */ - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - icon_draw_rect(x, y, w, h, aspect, pi->w[size], pi->h[size], pi->rect[size], alpha, rgb, is_preview); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + icon_draw_rect(x, y, w, h, aspect, pi->w[size], pi->h[size], pi->rect[size], alpha, rgb, desaturate); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); } } } @@ -1162,7 +1510,7 @@ void UI_id_icon_render(const bContext *C, Scene *scene, ID *id, const bool big, } } -static void ui_id_brush_render(const bContext *C, ID *id) +static void ui_id_icon_render(const bContext *C, ID *id, bool use_jobs) { PreviewImage *pi = BKE_previewimg_id_ensure(id); enum eIconSizes i; @@ -1174,7 +1522,7 @@ static void ui_id_brush_render(const bContext *C, ID *id) /* check if rect needs to be created; changed * only set by dynamic icons */ if (((pi->flag[i] & PRV_CHANGED) || !pi->rect[i])) { - icon_set_image(C, NULL, id, pi, i, true); + icon_set_image(C, NULL, id, pi, i, use_jobs); pi->flag[i] &= ~PRV_CHANGED; } } @@ -1187,19 +1535,25 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) if (br->flag & BRUSH_CUSTOM_ICON) { BKE_icon_id_ensure(id); - ui_id_brush_render(C, id); + ui_id_icon_render(C, id, true); } else { + WorkSpace *workspace = CTX_wm_workspace(C); Object *ob = CTX_data_active_object(C); - SpaceImage *sima; const EnumPropertyItem *items = NULL; int tool = PAINT_TOOL_DRAW, mode = 0; + ScrArea *sa = CTX_wm_area(C); + char space_type = sa->spacetype; + /* When in an unsupported space. */ + if (!ELEM(space_type, SPACE_VIEW3D, SPACE_IMAGE)) { + space_type = workspace->tools_space_type; + } /* XXX: this is not nice, should probably make brushes * be strictly in one paint mode only to avoid * checking various context stuff here */ - if (CTX_wm_view3d(C) && ob) { + if ((space_type == SPACE_VIEW3D) && ob) { if (ob->mode & OB_MODE_SCULPT) mode = OB_MODE_SCULPT; else if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) @@ -1207,10 +1561,19 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) else if (ob->mode & OB_MODE_TEXTURE_PAINT) mode = OB_MODE_TEXTURE_PAINT; } - else if ((sima = CTX_wm_space_image(C)) && - (sima->mode == SI_MODE_PAINT)) - { - mode = OB_MODE_TEXTURE_PAINT; + else if (space_type == SPACE_IMAGE) { + int sima_mode; + if (sa->spacetype == space_type) { + SpaceImage *sima = sa->spacedata.first; + sima_mode = sima->mode; + } + else { + sima_mode = workspace->tools_mode; + } + + if (sima_mode == SI_MODE_PAINT) { + mode = OB_MODE_TEXTURE_PAINT; + } } /* reset the icon */ @@ -1234,6 +1597,15 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) return id->icon_id; } +static int ui_id_screen_get_icon(const bContext *C, ID *id) +{ + BKE_icon_id_ensure(id); + /* Don't use jobs here, offscreen rendering doesn't like this and crashes. */ + ui_id_icon_render(C, id, false); + + return id->icon_id; +} + int ui_id_icon_get(const bContext *C, ID *id, const bool big) { int iconid = 0; @@ -1252,6 +1624,9 @@ int ui_id_icon_get(const bContext *C, ID *id, const bool big) /* checks if not exists, or changed */ UI_id_icon_render(C, NULL, id, big, true); break; + case ID_SCR: + iconid = ui_id_screen_get_icon(C, id); + break; default: break; } @@ -1277,7 +1652,7 @@ int UI_rnaptr_icon_get(bContext *C, PointerRNA *ptr, int rnaicon, const bool big id = RNA_pointer_get(ptr, "texture").data; } else if (RNA_struct_is_a(ptr->type, &RNA_DynamicPaintSurface)) { - DynamicPaintSurface *surface = (DynamicPaintSurface *)ptr->data; + DynamicPaintSurface *surface = ptr->data; if (surface->format == MOD_DPAINT_SURFACE_F_PTEX) return ICON_TEXTURE_SHADED; @@ -1286,6 +1661,18 @@ int UI_rnaptr_icon_get(bContext *C, PointerRNA *ptr, int rnaicon, const bool big else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) return ICON_FILE_IMAGE; } + else if (RNA_struct_is_a(ptr->type, &RNA_StudioLight)) { + StudioLight *sl = ptr->data; + switch (sl->flag & STUDIOLIGHT_FLAG_ORIENTATIONS) { + case STUDIOLIGHT_ORIENTATION_CAMERA: + return sl->icon_id_irradiance; + case STUDIOLIGHT_ORIENTATION_WORLD: + default: + return sl->icon_id_radiance; + case STUDIOLIGHT_ORIENTATION_VIEWNORMAL: + return sl->icon_id_matcap; + } + } /* get icon from ID */ if (id) { @@ -1319,7 +1706,7 @@ int UI_idcode_icon_get(const int idcode) case ID_IM: return ICON_IMAGE_DATA; case ID_LA: - return ICON_LAMP_DATA; + return ICON_LIGHT_DATA; case ID_LS: return ICON_LINE_DATA; case ID_LT: @@ -1344,6 +1731,8 @@ int UI_idcode_icon_get(const int idcode) return ICON_COLOR; /* TODO! this would need its own icon! */ case ID_PC: return ICON_CURVE_BEZCURVE; /* TODO! this would need its own icon! */ + case ID_LP: + return ICON_LIGHTPROBE_CUBEMAP; case ID_SCE: return ICON_SCENE_DATA; case ID_SPK: @@ -1365,21 +1754,26 @@ int UI_idcode_icon_get(const int idcode) static void icon_draw_at_size( float x, float y, int icon_id, float aspect, float alpha, - enum eIconSizes size, const bool nocreate) + enum eIconSizes size, const float desaturate) { int draw_size = get_draw_size(size); - icon_draw_size(x, y, icon_id, aspect, alpha, NULL, size, draw_size, nocreate, false); + icon_draw_size(x, y, icon_id, aspect, alpha, NULL, size, draw_size, desaturate); } void UI_icon_draw_aspect(float x, float y, int icon_id, float aspect, float alpha) { - icon_draw_at_size(x, y, icon_id, aspect, alpha, ICON_SIZE_ICON, 0); + icon_draw_at_size(x, y, icon_id, aspect, alpha, ICON_SIZE_ICON, 0.0f); } void UI_icon_draw_aspect_color(float x, float y, int icon_id, float aspect, const float rgb[3]) { int draw_size = get_draw_size(ICON_SIZE_ICON); - icon_draw_size(x, y, icon_id, aspect, 1.0f, rgb, ICON_SIZE_ICON, draw_size, false, false); + icon_draw_size(x, y, icon_id, aspect, 1.0f, rgb, ICON_SIZE_ICON, draw_size, false); +} + +void UI_icon_draw_desaturate(float x, float y, int icon_id, float aspect, float alpha, float desaturate) +{ + icon_draw_at_size(x, y, icon_id, aspect, alpha, ICON_SIZE_ICON, desaturate); } /* draws icon with dpi scale factor */ @@ -1388,22 +1782,27 @@ void UI_icon_draw(float x, float y, int icon_id) UI_icon_draw_aspect(x, y, icon_id, 1.0f / UI_DPI_FAC, 1.0f); } +void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha) +{ + UI_icon_draw_aspect(x, y, icon_id, 1.0f / UI_DPI_FAC, alpha); +} + void UI_icon_draw_size(float x, float y, int size, int icon_id, float alpha) { - icon_draw_size(x, y, icon_id, 1.0f, alpha, NULL, ICON_SIZE_ICON, size, true, false); + icon_draw_size(x, y, icon_id, 1.0f, alpha, NULL, ICON_SIZE_ICON, size, false); } void UI_icon_draw_preview(float x, float y, int icon_id) { - icon_draw_at_size(x, y, icon_id, 1.0f, 1.0f, ICON_SIZE_PREVIEW, 0); + icon_draw_at_size(x, y, icon_id, 1.0f, 1.0f, ICON_SIZE_PREVIEW, false); } void UI_icon_draw_preview_aspect(float x, float y, int icon_id, float aspect) { - icon_draw_at_size(x, y, icon_id, aspect, 1.0f, ICON_SIZE_PREVIEW, 0); + icon_draw_at_size(x, y, icon_id, aspect, 1.0f, ICON_SIZE_PREVIEW, false); } void UI_icon_draw_preview_aspect_size(float x, float y, int icon_id, float aspect, float alpha, int size) { - icon_draw_size(x, y, icon_id, aspect, alpha, NULL, ICON_SIZE_PREVIEW, size, false, true); + icon_draw_size(x, y, icon_id, aspect, alpha, NULL, ICON_SIZE_PREVIEW, size, false); } diff --git a/source/blender/editors/interface/interface_icons_event.c b/source/blender/editors/interface/interface_icons_event.c new file mode 100644 index 00000000000..fabf5f9bf48 --- /dev/null +++ b/source/blender/editors/interface/interface_icons_event.c @@ -0,0 +1,295 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/interface/interface_icons_event.c + * \ingroup edinterface + * + * A special set of icons to represent input devices, + * this is a mix of text (via fonts) and a handful of custom glyphs for special keys. + * + * Event codes are used as identifiers. + */ + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "GPU_draw.h" +#include "GPU_matrix.h" +#include "GPU_batch.h" +#include "GPU_immediate.h" +#include "GPU_state.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_fileops_types.h" +#include "BLI_math_vector.h" + +#include "DNA_brush_types.h" +#include "DNA_curve_types.h" +#include "DNA_dynamicpaint_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_workspace_types.h" + +#include "RNA_access.h" +#include "RNA_enum_types.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_icons.h" +#include "BKE_appdir.h" +#include "BKE_studiolight.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" +#include "IMB_thumbs.h" + +#include "BIF_glutil.h" +#include "BLF_api.h" + +#include "DEG_depsgraph.h" + +#include "DRW_engine.h" + +#include "ED_datafiles.h" +#include "ED_keyframes_draw.h" +#include "ED_render.h" + +#include "UI_interface.h" +#include "UI_interface_icons.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "interface_intern.h" + +static void icon_draw_rect_input_small_text_ex( + const rctf *rect, const float color[4], const float margin[2], const char *str, + int font_size) +{ + BLF_batch_draw_flush(); + const int font_id = BLF_default(); + BLF_color4fv(font_id, color); + BLF_size(font_id, font_size * U.pixelsize, U.dpi); + BLF_position(font_id, rect->xmin + margin[0] * 2, rect->ymin + margin[1] * 5, 0.0f); + BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX); + BLF_batch_draw_flush(); +} + +static void icon_draw_rect_input_small_text( + const rctf *rect, const float color[4], const float margin[2], const char *str) +{ + icon_draw_rect_input_small_text_ex(rect, color, margin, str, 8); +} + +static void icon_draw_rect_input_default_text( + const rctf *rect, + const float color[4], const float margin[2], const char *str) +{ + BLF_batch_draw_flush(); + const int font_id = BLF_default(); + BLF_color4fv(font_id, color); + BLF_position(font_id, (int)(rect->xmin + margin[0] * 5), (int)(rect->ymin + margin[1] * 5), 0.0f); + BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX); + BLF_batch_draw_flush(); +} + +static void icon_draw_rect_input_mono_text( + const rctf *rect, + const float color[4], const float margin[2], const char *str) +{ + BLF_batch_draw_flush(); + const int font_id = blf_mono_font; + BLF_color4fv(font_id, color); + BLF_size(font_id, 20 * U.pixelsize, U.dpi); + BLF_position(font_id, (int)(rect->xmin + margin[0] * 5), (int)(rect->ymin + margin[1] * 5), 0.0f); + BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX); + BLF_batch_draw_flush(); +} + +static void icon_draw_rect_input_line_prim( + const rctf *rect, + const float color[4], + const int prim, + const char lines[][2], int lines_len) +{ + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + BLI_assert(ELEM(prim, GPU_PRIM_LINE_LOOP, GPU_PRIM_LINE_STRIP)); + const uint pos_id = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4fv(color); + immBegin(prim, lines_len); + float w_inv = BLI_rctf_size_x(rect) / 255.0f; + float h_inv = BLI_rctf_size_y(rect) / 255.0f; + for (int i = 0; i < lines_len; i++) { + immVertex2f( + pos_id, + round_fl_to_int(rect->xmin + ((float)lines[i][0] * w_inv)), + round_fl_to_int(rect->ymin + ((float)lines[i][1] * h_inv)) + ); + } + immEnd(); + immUnbindProgram(); + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); +} + +void icon_draw_rect_input( + float x, float y, int w, int h, float UNUSED(alpha), + short event_type, short UNUSED(event_value)) +{ + float color[4]; + const float margin[2] = {w / 20.0f, h / 20.0f}; + UI_GetThemeColor4fv(TH_TEXT, color); + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_aa( + false, + (int)x, + (int)y, + (int)(x + w), + (int)(y + h), 4.0f, color + ); + + const rctf rect = { + .xmin = x, + .ymin = y, + .xmax = x + w, + .ymax = y + h, + }; + + const bool simple_text = false; + + if ((event_type >= AKEY) || (ZKEY <= event_type)) { + char str[2] = {'A' + (event_type - AKEY), '\0'}; + icon_draw_rect_input_default_text(&rect, color, margin, str); + } + if ((event_type >= F1KEY) || (F12KEY <= event_type)) { + char str[3] = {'F', '1' + (event_type - F1KEY), '\0'}; + icon_draw_rect_input_default_text(&rect, color, margin, str); + } + else if (event_type == LEFTSHIFTKEY) { + if (simple_text) { + icon_draw_rect_input_small_text(&rect, color, margin, "Shift"); + } + else { + rctf rect_ofs = rect; + BLI_rctf_translate(&rect_ofs, (w / -14.0f), (w / -14.0f)); + icon_draw_rect_input_mono_text(&rect_ofs, color, margin, (const char[]){0xe2, 0x87, 0xa7, 0x0}); + } + } + else if (event_type == LEFTCTRLKEY) { + if (simple_text) { + icon_draw_rect_input_small_text(&rect, color, margin, "Ctrl"); + } + else { + rctf rect_ofs = rect; + BLI_rctf_translate(&rect_ofs, (w / -16.0f), 0.0f); + icon_draw_rect_input_default_text(&rect_ofs, color, margin, "^"); + } + } + else if (event_type == LEFTALTKEY) { + if (simple_text) { + icon_draw_rect_input_small_text(&rect, color, margin, "Alt"); + } + else { + rctf rect_ofs = rect; + BLI_rctf_translate(&rect_ofs, (w / -8.0f), 0.0f); + icon_draw_rect_input_default_text(&rect_ofs, color, margin, (const char[]){0xe2, 0x8c, 0xa5, 0x0}); + } + } + else if (event_type == OSKEY) { + icon_draw_rect_input_small_text(&rect, color, margin, "OS"); + } + else if (event_type == DELKEY) { + icon_draw_rect_input_small_text(&rect, color, margin, "Del"); + } + else if (event_type == TABKEY) { + if (simple_text) { + icon_draw_rect_input_small_text(&rect, color, margin, "Tab"); + } + else { + rctf rect_ofs = rect; + BLI_rctf_translate(&rect_ofs, (w / -12.0f), (w / -12.0f)); + icon_draw_rect_input_mono_text(&rect_ofs, color, margin, (const char[]){0xe2, 0x86, 0xb9, 0x0}); + } + } + else if (event_type == HOMEKEY) { + if (simple_text) { + icon_draw_rect_input_small_text(&rect, color, margin, "Home"); + } + else { + rctf rect_ofs = rect; + BLI_rctf_translate(&rect_ofs, (w / -12.0f), (w / -12.0f)); + icon_draw_rect_input_mono_text(&rect_ofs, color, margin, (const char[]){0xe2, 0x87, 0xa4, 0x0}); + } + } + else if (event_type == ENDKEY) { + if (simple_text) { + icon_draw_rect_input_small_text(&rect, color, margin, "End"); + } + else { + rctf rect_ofs = rect; + BLI_rctf_translate(&rect_ofs, (w / -12.0f), (w / -12.0f)); + icon_draw_rect_input_mono_text(&rect_ofs, color, margin, (const char[]){0xe2, 0x87, 0xa5, 0x0}); + } + } + else if (event_type == RETKEY) { + if (simple_text) { + icon_draw_rect_input_small_text(&rect, color, margin, "Ret"); + } + else { + rctf rect_ofs = rect; + BLI_rctf_translate(&rect_ofs, (w / -8.0f), (w / -6.0f)); + icon_draw_rect_input_mono_text(&rect_ofs, color, margin, (const char[]){0xe2, 0x8f, 0x8e, 0x0}); + } + } + else if (event_type == ESCKEY) { + icon_draw_rect_input_small_text(&rect, color, margin, "Esc"); + } + else if (event_type == PAGEUPKEY) { + icon_draw_rect_input_small_text_ex(&rect, color, margin, (const char[]){'P', 0xe2, 0x86, 0x91, 0x0}, 10); + } + else if (event_type == PAGEDOWNKEY) { + icon_draw_rect_input_small_text_ex(&rect, color, margin, (const char[]){'P', 0xe2, 0x86, 0x93, 0x0}, 10); + } + else if (event_type == LEFTARROWKEY) { + icon_draw_rect_input_default_text(&rect, color, margin, (const char[]){0xe2, 0x86, 0x90, 0x0}); + } + else if (event_type == UPARROWKEY) { + icon_draw_rect_input_default_text(&rect, color, margin, (const char[]){0xe2, 0x86, 0x91, 0x0}); + } + else if (event_type == RIGHTARROWKEY) { + icon_draw_rect_input_default_text(&rect, color, margin, (const char[]){0xe2, 0x86, 0x92, 0x0}); + } + else if (event_type == DOWNARROWKEY) { + icon_draw_rect_input_default_text(&rect, color, margin, (const char[]){0xe2, 0x86, 0x93, 0x0}); + } + else if (event_type == SPACEKEY) { + const uchar lines[] = {60, 118, 60, 60, 195, 60, 195, 118}; + icon_draw_rect_input_line_prim( + &rect, color, GPU_PRIM_LINE_STRIP, + (const void *)lines, ARRAY_SIZE(lines) / 2); + } +} diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index dc5e100b5f2..fa60ac0552b 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -72,6 +72,8 @@ typedef enum { UI_WTYPE_NUMBER, UI_WTYPE_SLIDER, UI_WTYPE_EXEC, + UI_WTYPE_TOOLBAR_ITEM, + UI_WTYPE_TAB, UI_WTYPE_TOOLTIP, /* strings */ @@ -103,6 +105,9 @@ typedef enum { UI_WTYPE_PROGRESSBAR, } uiWidgetTypeEnum; +#define UI_MENU_WIDTH_MIN (UI_UNIT_Y * 9) +#define UI_MENU_SUBMENU_PADDING (6 * UI_DPI_FAC) /* some extra padding added to menus containing submenu icons */ + /* menu scrolling */ #define UI_MENU_SCROLL_ARROW 12 #define UI_MENU_SCROLL_MOUSE (UI_MENU_SCROLL_ARROW + 2) @@ -112,6 +117,9 @@ typedef enum { #define UI_PANEL_MINX 100 #define UI_PANEL_MINY 70 +/* popover width (multiplied by 'U.widget_unit') */ +#define UI_POPOVER_WIDTH_UNITS 10 + /* uiBut->flag */ enum { UI_SELECT = (1 << 0), /* use when the button is pressed */ @@ -187,23 +195,6 @@ enum { /* max amount of items a radial menu (pie menu) can contain */ #define PIE_MAX_ITEMS 8 -typedef struct uiLinkLine { /* only for draw/edit */ - struct uiLinkLine *next, *prev; - struct uiBut *from, *to; - short flag, deactive; -} uiLinkLine; - -typedef struct { - void **poin; /* pointer to original pointer */ - void ***ppoin; /* pointer to original pointer-array */ - short *totlink; /* if pointer-array, here is the total */ - - short maxlink, pad; - short fromcode, tocode; - - ListBase lines; -} uiLink; - struct uiBut { struct uiBut *next, *prev; int flag, drawflag; @@ -258,6 +249,7 @@ struct uiBut { uiButSearchCreateFunc search_create_func; uiButSearchFunc search_func; + bool free_search_arg; void *search_arg; uiButHandleRenameFunc rename_func; @@ -268,9 +260,6 @@ struct uiBut { uiButHandleHoldFunc hold_func; void *hold_argN; - uiLink *link; - short linkto[2]; /* region relative coords */ - const char *tip; uiButToolTipFunc tip_func; void *tip_argN; @@ -281,7 +270,7 @@ struct uiBut { BIFIconID icon; char dt; /* drawtype: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied from the block */ signed char pie_dir; /* direction in a pie menu, used for collision detection (RadialDirection) */ - char changed; /* could be made into a single flag */ + bool changed; /* could be made into a single flag */ unsigned char unit_type; /* so buttons can support unit systems which are not RNA */ short modifier_key; short iconadd; @@ -331,6 +320,11 @@ struct uiBut { uiBlock *block; }; +typedef struct uiButTab { + uiBut but; + struct wmOperatorType *unlink_ot; +} uiButTab; + typedef struct ColorPicker { struct ColorPicker *next, *prev; float color_data[3]; /* colr data may be HSV or HSL for now */ @@ -356,6 +350,13 @@ struct PieMenuData { float alphafac; }; +/* uiBlock.content_hints */ +enum eBlockContentHints { + /* In a menu block, if there is a single sub-menu button, we add some + * padding to the right to put nicely aligned triangle icons there. */ + BLOCK_CONTAINS_SUBMENU_BUT = (1 << 0), +}; + struct uiBlock { uiBlock *next, *prev; @@ -402,11 +403,15 @@ struct uiBlock { int flag; short alignnr; + /* Hints about the buttons of this block. Used to avoid iterating over + * buttons to find out if some criteria is met by any. Instead, check this + * criteria when adding the button and set a flag here if it's met. */ + short content_hints; /* eBlockContentHints */ char direction; char dt; /* drawtype: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied to buttons */ bool auto_open; - char _pad[7]; + char _pad[5]; double auto_open_last; const char *lockstr; @@ -449,8 +454,6 @@ typedef struct uiSafetyRct { /* interface.c */ -extern void ui_linkline_remove(uiLinkLine *line, uiBut *but); - void ui_fontscale(short *points, float aspect); extern void ui_block_to_window_fl(const struct ARegion *ar, uiBlock *block, float *x, float *y); @@ -505,9 +508,9 @@ extern bool ui_but_supports_cycling(const uiBut *but) ATTR_WARN_UNUSED_RESULT; extern int ui_but_is_pushed_ex(uiBut *but, double *value) ATTR_WARN_UNUSED_RESULT; extern int ui_but_is_pushed(uiBut *but) ATTR_WARN_UNUSED_RESULT; +void ui_but_override_flag(uiBut *but); extern void ui_block_bounds_calc(uiBlock *block); -extern void ui_block_translate(uiBlock *block, int x, int y); extern struct ColorManagedDisplay *ui_block_cm_display_get(uiBlock *block); void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]); @@ -524,16 +527,19 @@ struct uiKeyNavLock { }; typedef uiBlock * (*uiBlockHandleCreateFunc)(struct bContext *C, struct uiPopupBlockHandle *handle, void *arg1); +typedef void (*uiBlockHandleFreeFunc)(struct uiPopupBlockHandle *handle, void *arg1); struct uiPopupBlockCreate { - uiBlockCreateFunc create_func; + uiBlockCreateFunc create_func; uiBlockHandleCreateFunc handle_create_func; + uiBlockHandleFreeFunc free_func; void *arg; int event_xy[2]; /* when popup is initialized from a button */ ARegion *butregion; + uiBut *but; }; struct uiPopupBlockHandle { @@ -554,8 +560,10 @@ struct uiPopupBlockHandle { struct uiPopupBlockCreate popup_create_vars; /* true if we can re-create the popup using 'popup_create_vars' */ bool can_refresh; + bool refresh; struct wmTimer *scrolltimer; + float scrolloffset; struct uiKeyNavLock keynav_state; @@ -575,6 +583,15 @@ struct uiPopupBlockHandle { /* menu direction */ int direction; + /* Previous values so we don't resize or reposition on refresh. */ + rctf prev_block_rect; + rctf prev_butrct; + short prev_dir1, prev_dir2; + int prev_mx, prev_my; + + /* Maximum estimated size to avoid having to reposition on refresh. */ + float max_size_x, max_size_y; + /* #ifdef USE_DRAG_POPUP */ bool is_grab; int grab_xy_prev[2]; @@ -627,13 +644,25 @@ uiPopupBlockHandle *ui_popup_menu_create( struct bContext *C, struct ARegion *butregion, uiBut *but, uiMenuCreateFunc create_func, void *arg); +/* interface_region_popover.c */ +uiBlock *ui_popover_block_refresh( + struct bContext *C, uiPopupBlockHandle *handle, + ARegion *butregion, uiBut *but); +uiPopupBlockHandle *ui_popover_block_create( + struct bContext *C, struct ARegion *butregion, uiBut *but, + uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, + void *arg); +uiPopupBlockHandle *ui_popover_panel_create( + struct bContext *C, struct ARegion *butregion, uiBut *but, + uiMenuCreateFunc create_func, void *arg); + /* interface_region_menu_pie.c */ void ui_pie_menu_level_create( uiBlock *block, struct wmOperatorType *ot, const char *propname, IDProperty *properties, const EnumPropertyItem *items, int totitem, int context, int flag); /* interface_region_popup.c */ -void ui_popup_translate(struct bContext *C, struct ARegion *ar, const int mdiff[2]); +void ui_popup_translate(struct ARegion *ar, const int mdiff[2]); void ui_popup_block_free(struct bContext *C, uiPopupBlockHandle *handle); void ui_popup_block_scrolltest(struct uiBlock *block); @@ -651,6 +680,8 @@ extern void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, floa void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, const float alpha); + +void ui_draw_but_TAB_outline(const rcti *rect, float rad, unsigned char highlight[3], unsigned char highlight_fade[3]); void ui_draw_but_HISTOGRAM(ARegion *ar, uiBut *but, struct uiWidgetColors *wcol, const rcti *rect); void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, struct uiWidgetColors *wcol, const rcti *rect); void ui_draw_but_VECTORSCOPE(ARegion *ar, uiBut *but, struct uiWidgetColors *wcol, const rcti *rect); @@ -690,20 +721,55 @@ struct wmIMEData *ui_but_ime_data_get(uiBut *but); #endif /* interface_widgets.c */ -void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3); -void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy, float rad, bool use_alpha); + +/* Widget shader parameters, must match the shader layout. */ +typedef struct uiWidgetBaseParameters { + rctf recti, rect; + float radi, rad; + float facxi, facyi; + float round_corners[4]; + float color_inner1[4], color_inner2[4]; + float color_outline[4], color_emboss[4]; + float color_tria[4]; + float tria1_center[2], tria2_center[2]; + float tria1_size, tria2_size; + float shade_dir; + /* We pack alpha check and discard factor in alpha_discard. + * If the value is negative then we do alpha check. + * The absolute value itself is the discard factor. + * Initialize value to 1.0.f if you don't want discard */ + float alpha_discard; +} uiWidgetBaseParameters; + +enum { + ROUNDBOX_TRIA_NONE = 0, + ROUNDBOX_TRIA_ARROWS, + ROUNDBOX_TRIA_SCROLL, + ROUNDBOX_TRIA_MENU, + ROUNDBOX_TRIA_CHECK, + ROUNDBOX_TRIA_HOLD_ACTION_ARROW, + + ROUNDBOX_TRIA_MAX, /* don't use */ +}; + +struct GPUBatch *ui_batch_roundbox_get(bool filled, bool antialiased); +struct GPUBatch *ui_batch_roundbox_widget_get(int tria); +struct GPUBatch *ui_batch_roundbox_shadow_get(void); + +void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]); void ui_draw_menu_back(struct uiStyle *style, uiBlock *block, rcti *rect); +void ui_draw_popover_back(ARegion *ar, struct uiStyle *style, uiBlock *block, rcti *rect); void ui_draw_pie_center(uiBlock *block); struct uiWidgetColors *ui_tooltip_get_theme(void); + +void ui_draw_widget_back_color( + uiWidgetTypeEnum type, bool use_shadow, const rcti *rect, + const float color[4]); +void ui_draw_widget_back( + uiWidgetTypeEnum type, bool use_shadow, const rcti *rect); void ui_draw_tooltip_background(struct uiStyle *UNUSED(style), uiBlock *block, rcti *rect); -void ui_draw_search_back(struct uiStyle *style, uiBlock *block, rcti *rect); -bool ui_link_bezier_points(const rcti *rect, float coord_array[][2], int resol); -void ui_draw_link_bezier(const rcti *rect); extern void ui_draw_but(const struct bContext *C, ARegion *ar, struct uiStyle *style, uiBut *but, rcti *rect); -/* theme color init */ -struct ThemeUI; -void ui_widget_color_init(struct ThemeUI *tui); void ui_draw_menu_item(struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state, bool use_sep); void ui_draw_preview_item(struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state); @@ -713,6 +779,9 @@ void ui_draw_preview_item(struct uiFontStyle *fstyle, rcti *rect, const char *na /* margin at top of screen for popups */ #define UI_POPUP_MENU_TOP (int)(8 * UI_DPI_FAC) +#define UI_PIXEL_AA_JITTER 8 +const float ui_pixel_jitter[UI_PIXEL_AA_JITTER][2]; + /* interface_style.c */ void uiStyleInit(void); @@ -720,6 +789,11 @@ void uiStyleInit(void); void ui_icon_ensure_deferred(const struct bContext *C, const int icon_id, const bool big); int ui_id_icon_get(const struct bContext *C, struct ID *id, const bool big); +/* interface_icons_event.c */ +void icon_draw_rect_input( + float x, float y, int w, int h, float alpha, + short event_type, short event_value); + /* resources.c */ void init_userdef_do_versions(struct Main *bmain); void ui_theme_init_default(void); @@ -731,10 +805,12 @@ void ui_resources_free(void); void ui_layout_add_but(uiLayout *layout, uiBut *but); void ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop); void ui_layout_list_set_labels_active(uiLayout *layout); +/* menu callback */ +void ui_item_paneltype_func(struct bContext *C, struct uiLayout *layout, void *arg_pt); /* interface_align.c */ bool ui_but_can_align(const uiBut *but) ATTR_WARN_UNUSED_RESULT; -void ui_block_align_calc(uiBlock *block); +void ui_block_align_calc(uiBlock *block, const ARegion *region); /* interface_anim.c */ void ui_but_anim_flag(uiBut *but, float cfra); @@ -745,10 +821,14 @@ bool ui_but_anim_expression_set(uiBut *but, const char *str); bool ui_but_anim_expression_create(uiBut *but, const char *str); void ui_but_anim_autokey(struct bContext *C, uiBut *but, struct Scene *scene, float cfra); +void ui_but_anim_decorate_cb(struct bContext *C, void *arg_but, void *arg_dummy); +void ui_but_anim_decorate_update_from_flag(uiBut *but); + /* interface_query.c */ bool ui_but_is_editable(const uiBut *but); bool ui_but_is_editable_as_text(const uiBut *but); bool ui_but_is_toggle(const uiBut *but); +bool ui_but_is_popover_once_compat(const uiBut *but); extern bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; extern bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; @@ -778,4 +858,20 @@ void UI_OT_eyedropper_depth(struct wmOperatorType *ot); /* interface_eyedropper_driver.c */ void UI_OT_eyedropper_driver(struct wmOperatorType *ot); +/* interface_util.c */ + +/** + * For use with #ui_rna_collection_search_cb. + */ +typedef struct uiRNACollectionSearch { + PointerRNA target_ptr; + PropertyRNA *target_prop; + + PointerRNA search_ptr; + PropertyRNA *search_prop; + + bool *but_changed; /* pointer to uiBut.changed */ +} uiRNACollectionSearch; +void ui_rna_collection_search_cb(const struct bContext *C, void *arg, const char *str, uiSearchItems *items); + #endif /* __INTERFACE_INTERN_H__ */ diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index e830d70e308..82ed4c5acba 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -37,6 +37,7 @@ #include "DNA_armature_types.h" #include "DNA_userdef_types.h" +#include "BLI_alloca.h" #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_rect.h" @@ -49,6 +50,7 @@ #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_screen.h" +#include "BKE_animsys.h" #include "RNA_access.h" @@ -62,6 +64,13 @@ #include "interface_intern.h" +/* Show an icon button after each RNA button to use to quickly set keyframes, + * this is a way to display animation/driven/override status, see T54951. */ +#define UI_PROP_DECORATE +/* Alternate draw mode where some buttons can use single icon width, + * giving more room for the text at the expense of nicely aligned text. */ +#define UI_PROP_SEP_ICON_WIDTH_EXCEPTION + /************************ Structs and Defines *************************/ #define UI_OPERATOR_ERROR_RET(_ot, _opname, return_statement) \ @@ -71,6 +80,7 @@ return_statement; \ } (void)0 \ +#define UI_ITEM_PROP_SEP_DIVIDE 0.5f /* uiLayoutRoot */ @@ -100,6 +110,7 @@ typedef enum uiItemType { ITEM_LAYOUT_COLUMN, ITEM_LAYOUT_COLUMN_FLOW, ITEM_LAYOUT_ROW_FLOW, + ITEM_LAYOUT_GRID_FLOW, ITEM_LAYOUT_BOX, ITEM_LAYOUT_ABSOLUTE, ITEM_LAYOUT_SPLIT, @@ -128,6 +139,10 @@ enum { UI_ITEM_MIN = 1 << 1, UI_ITEM_BOX_ITEM = 1 << 2, /* The item is "inside" a box item */ + UI_ITEM_PROP_SEP = 1 << 3, + /* Show an icon button next to each property (to set keyframes, show status). + * Enabled by default, depends on 'UI_ITEM_PROP_SEP'. */ + UI_ITEM_PROP_DECORATE = 1 << 4, }; typedef struct uiButtonItem { @@ -150,7 +165,9 @@ struct uiLayout { bool enabled; bool redalert; bool keepaspect; + bool variable_size; /* For layouts inside gridflow, they and their items shall never have a fixed maximal size. */ char alignment; + char emboss; }; typedef struct uiLayoutItemFlow { @@ -159,6 +176,22 @@ typedef struct uiLayoutItemFlow { int totcol; } uiLayoutItemFlow; +typedef struct uiLayoutItemGridFlow { + uiLayout litem; + + /* Extra parameters */ + bool row_major; /* Fill first row first, instead of filling first column first. */ + bool even_columns; /* Same width for all columns. */ + bool even_rows; /* Same height for all rows. */ + /* If positive, absolute fixed number of columns. + * If 0, fully automatic (based on available width). + * If negative, automatic but only generates number of columns/rows multiple of given (absolute) value. */ + int columns_len; + + /* Pure internal runtime storage. */ + int tot_items, tot_columns, tot_rows; +} uiLayoutItemGridFlow; + typedef struct uiLayoutItemBx { uiLayout litem; uiBut *roundbox; @@ -234,29 +267,37 @@ static int ui_layout_vary_direction(uiLayout *layout) UI_ITEM_VARY_X : UI_ITEM_VARY_Y); } +static bool ui_layout_variable_size(uiLayout *layout) +{ + /* Note that this code is probably a bit flacky, we'd probably want to know whether it's variable in X and/or Y, + * etc. But for now it mimics previous one, with addition of variable flag set for children of gridflow layouts. */ + return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size; +} + /* estimated size of text + icon */ static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool compact) { bool variable; + const int unit_x = UI_UNIT_X * (layout->scale[0] ? layout->scale[0] : 1.0f); if (icon && !name[0]) - return UI_UNIT_X; /* icon only */ + return unit_x; /* icon only */ - variable = (ui_layout_vary_direction(layout) == UI_ITEM_VARY_X); + variable = ui_layout_variable_size(layout); if (variable) { if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) { layout->item.flag |= UI_ITEM_MIN; } const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - /* it may seem odd that the icon only adds (UI_UNIT_X / 4) + /* it may seem odd that the icon only adds (unit_x / 4) * but taking margins into account its fine */ return (UI_fontstyle_string_width(fstyle, name) + - (UI_UNIT_X * ((compact ? 1.25f : 1.50f) + - (icon ? 0.25f : 0.0f)))); + (unit_x * ((compact ? 1.25f : 1.50f) + + (icon ? 0.25f : 0.0f)))); } else { - return UI_UNIT_X * 10; + return unit_x * 10; } } @@ -343,6 +384,7 @@ static int ui_layout_local_dir(uiLayout *layout) return UI_LAYOUT_HORIZONTAL; case ITEM_LAYOUT_COLUMN: case ITEM_LAYOUT_COLUMN_FLOW: + case ITEM_LAYOUT_GRID_FLOW: case ITEM_LAYOUT_SPLIT: case ITEM_LAYOUT_ABSOLUTE: case ITEM_LAYOUT_BOX: @@ -392,7 +434,7 @@ static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index) static void ui_item_array( uiLayout *layout, uiBlock *block, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int len, int x, int y, int w, int UNUSED(h), - bool expand, bool slider, bool toggle, bool icon_only) + bool expand, bool slider, bool toggle, bool icon_only, bool compact, bool show_text) { uiStyle *style = layout->root->style; uiBut *but; @@ -409,8 +451,9 @@ static void ui_item_array( UI_block_layout_set_current(block, sub); /* create label */ - if (name[0]) + if (name[0] && show_text) { uiDefBut(block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + } /* create buttons */ if (type == PROP_BOOLEAN && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) { @@ -528,7 +571,7 @@ static void ui_item_array( /* layout for known array subtypes */ char str[3] = {'\0'}; - if (!icon_only) { + if (!icon_only && show_text) { if (type != PROP_BOOLEAN) { str[1] = ':'; } @@ -540,10 +583,22 @@ static void ui_item_array( RNA_property_boolean_get_array(ptr, prop, boolarr); } + const char *str_buf = show_text ? str: ""; for (a = 0; a < len; a++) { - if (!icon_only) str[0] = RNA_property_array_item_char(prop, a); - if (boolarr) icon = boolarr[a] ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT; - but = uiDefAutoButR(block, ptr, prop, a, str, icon, 0, 0, w, UI_UNIT_Y); + int width_item; + + if (!icon_only && show_text) { + str[0] = RNA_property_array_item_char(prop, a); + } + if (boolarr) { + icon = boolarr[a] ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT; + } + + width_item = ( + (compact && type == PROP_BOOLEAN) ? + min_ii(w, ui_text_icon_width(layout, str_buf, icon, false)) : w); + + but = uiDefAutoButR(block, ptr, prop, a, str_buf, icon, 0, 0, width_item, UI_UNIT_Y); if (slider && but->type == UI_BTYPE_NUM) but->type = UI_BTYPE_NUM_SLIDER; if (toggle && but->type == UI_BTYPE_CHECKBOX) @@ -626,8 +681,14 @@ static void ui_item_enum_expand( for (item = item_array; item->identifier; item++) { if (!item->identifier[0]) { - if (radial && layout_radial) { - uiItemS(layout_radial); + const EnumPropertyItem *next_item = item + 1; + if (next_item->identifier) { + if (radial && layout_radial) { + uiItemS(layout_radial); + } + else { + uiItemS(block->curlayout); + } } continue; } @@ -669,27 +730,49 @@ static void ui_keymap_but_cb(bContext *UNUSED(C), void *but_v, void *UNUSED(key_ RNA_boolean_set(&but->rnapoin, "oskey", (but->modifier_key & KM_OSKEY) != 0); } -/* create label + button for RNA property */ -static uiBut *ui_item_with_label(uiLayout *layout, uiBlock *block, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int index, int x, int y, int w, int h, int flag) +/** + * Create label + button for RNA property + * + * \param w_hint: For varying width layout, this becomes the label width. + * Otherwise it's used to fit both items into it. + **/ +static uiBut *ui_item_with_label( + uiLayout *layout, uiBlock *block, const char *name, int icon, + PointerRNA *ptr, PropertyRNA *prop, int index, + int x, int y, int w_hint, int h, int flag) { uiLayout *sub; uiBut *but = NULL; PropertyType type; PropertySubType subtype; - int labelw; + int prop_but_width = w_hint; + const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0); sub = uiLayoutRow(layout, layout->align); UI_block_layout_set_current(block, sub); if (name[0]) { - /* XXX UI_fontstyle_string_width is not accurate */ -#if 0 - labelw = UI_fontstyle_string_width(fstyle, name); - CLAMP(labelw, w / 4, 3 * w / 4); -#endif - labelw = w / 3; - uiDefBut(block, UI_BTYPE_LABEL, 0, name, x, y, labelw, h, NULL, 0.0, 0.0, 0, 0, ""); - w = w - labelw; + int w_label; + + if (use_prop_sep) { + w_label = (int)((w_hint * 2) * UI_ITEM_PROP_SEP_DIVIDE); + } + else { + if (ui_layout_variable_size(layout)) { + /* w_hint is width for label in this case. Use a default width for property button(s) */ + prop_but_width = UI_UNIT_X * 5; + w_label = w_hint; + } + else { + w_label = w_hint / 3; + } + } + + uiBut *but_label = uiDefBut(block, UI_BTYPE_LABEL, 0, name, x, y, w_label, h, NULL, 0.0, 0.0, 0, 0, ""); + if (use_prop_sep) { + but_label->drawflag |= UI_BUT_TEXT_RIGHT; + but_label->drawflag &= ~UI_BUT_TEXT_LEFT; + } } type = RNA_property_type(prop); @@ -697,7 +780,7 @@ static uiBut *ui_item_with_label(uiLayout *layout, uiBlock *block, const char *n if (subtype == PROP_FILEPATH || subtype == PROP_DIRPATH) { UI_block_layout_set_current(block, uiLayoutRow(sub, true)); - but = uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, w - UI_UNIT_X, h); + but = uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, prop_but_width - UI_UNIT_X, h); /* BUTTONS_OT_file_browse calls UI_context_active_but_prop_get_filebrowser */ uiDefIconButO( @@ -705,7 +788,7 @@ static uiBut *ui_item_with_label(uiLayout *layout, uiBlock *block, const char *n WM_OP_INVOKE_DEFAULT, ICON_FILESEL, x, y, UI_UNIT_X, h, NULL); } else if (flag & UI_ITEM_R_EVENT) { - but = uiDefButR_prop(block, UI_BTYPE_KEY_EVENT, 0, name, x, y, w, h, ptr, prop, index, 0, 0, -1, -1, NULL); + but = uiDefButR_prop(block, UI_BTYPE_KEY_EVENT, 0, name, x, y, prop_but_width, h, ptr, prop, index, 0, 0, -1, -1, NULL); } else if (flag & UI_ITEM_R_FULL_EVENT) { if (RNA_struct_is_a(ptr->type, &RNA_KeyMapItem)) { @@ -713,14 +796,18 @@ static uiBut *ui_item_with_label(uiLayout *layout, uiBlock *block, const char *n WM_keymap_item_to_string(ptr->data, false, buf, sizeof(buf)); - but = uiDefButR_prop(block, UI_BTYPE_HOTKEY_EVENT, 0, buf, x, y, w, h, ptr, prop, 0, 0, 0, -1, -1, NULL); + but = uiDefButR_prop(block, UI_BTYPE_HOTKEY_EVENT, 0, buf, x, y, prop_but_width, h, ptr, prop, 0, 0, 0, -1, -1, NULL); UI_but_func_set(but, ui_keymap_but_cb, but, NULL); if (flag & UI_ITEM_R_IMMEDIATE) UI_but_flag_enable(but, UI_BUT_IMMEDIATE); } } - else - but = uiDefAutoButR(block, ptr, prop, index, (type == PROP_ENUM && !(flag & UI_ITEM_R_ICON_ONLY)) ? NULL : "", icon, x, y, w, h); + else { + const char *str = (type == PROP_ENUM && !(flag & UI_ITEM_R_ICON_ONLY)) ? NULL : ""; + but = uiDefAutoButR( + block, ptr, prop, index, str, icon, + x, y, prop_but_width, h); + } UI_block_layout_set_current(block, layout); return but; @@ -821,8 +908,10 @@ static uiBut *uiItemFullO_ptr_ex( w = ui_text_icon_width(layout, name, icon, 0); - if (flag & UI_ITEM_R_NO_BG) - UI_block_emboss_set(block, UI_EMBOSS_NONE); + int prev_emboss = layout->emboss; + if (flag & UI_ITEM_R_NO_BG) { + layout->emboss = UI_EMBOSS_NONE; + } /* create the button */ if (icon) { @@ -843,8 +932,9 @@ static uiBut *uiItemFullO_ptr_ex( if ((layout->root->type == UI_LAYOUT_TOOLBAR) && !icon) but->drawflag |= UI_BUT_TEXT_LEFT; - if (flag & UI_ITEM_R_NO_BG) - UI_block_emboss_set(block, UI_EMBOSS); + if (flag & UI_ITEM_R_NO_BG) { + layout->emboss = prev_emboss; + } if (flag & UI_ITEM_O_DEPRESS) { but->flag |= UI_SELECT_DRAW; @@ -871,7 +961,7 @@ static uiBut *uiItemFullO_ptr_ex( return but; } -static void ui_item_hold_menu(struct bContext *C, ARegion *butregion, uiBut *but) +static void ui_item_menu_hold(struct bContext *C, ARegion *butregion, uiBut *but) { uiPopupMenu *pup = UI_popup_menu_begin(C, "", ICON_NONE); uiLayout *layout = UI_popup_menu_layout(pup); @@ -879,6 +969,24 @@ static void ui_item_hold_menu(struct bContext *C, ARegion *butregion, uiBut *but UI_popup_menu_but_set(pup, butregion, but); block->flag |= UI_BLOCK_POPUP_HOLD; + block->flag |= UI_BLOCK_IS_FLIP; + + char direction = UI_DIR_DOWN; + if (!but->drawstr[0]) { + if (butregion->alignment == RGN_ALIGN_LEFT) { + direction = UI_DIR_RIGHT; + } + else if (butregion->alignment == RGN_ALIGN_RIGHT) { + direction = UI_DIR_LEFT; + } + else if (butregion->alignment == RGN_ALIGN_BOTTOM) { + direction = UI_DIR_UP; + } + else { + direction = UI_DIR_DOWN; + } + } + UI_block_direction_set(block, direction); const char *menu_id = but->hold_argN; MenuType *mt = WM_menutype_find(menu_id, true); @@ -908,7 +1016,7 @@ void uiItemFullOMenuHold_ptr( PointerRNA *r_opptr) { uiBut *but = uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr); - UI_but_func_hold_set(but, ui_item_hold_menu, BLI_strdup(menu_id)); + UI_but_func_hold_set(but, ui_item_menu_hold, BLI_strdup(menu_id)); } void uiItemFullO( @@ -1302,8 +1410,10 @@ void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname) /* RNA property items */ static void ui_item_rna_size( - uiLayout *layout, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, - int index, bool icon_only, int *r_w, int *r_h) + uiLayout *layout, const char *name, int icon, + PointerRNA *ptr, PropertyRNA *prop, + int index, bool icon_only, bool compact, + int *r_w, int *r_h) { PropertyType type; PropertySubType subtype; @@ -1329,7 +1439,7 @@ static void ui_item_rna_size( RNA_property_enum_items_gettexted(layout->root->block->evil_C, ptr, prop, &item_array, NULL, &free); for (item = item_array; item->identifier; item++) { if (item->identifier[0]) { - w = max_ii(w, ui_text_icon_width(layout, item->name, item->icon, 0)); + w = max_ii(w, ui_text_icon_width(layout, item->name, item->icon, compact)); } } if (free) { @@ -1340,12 +1450,13 @@ static void ui_item_rna_size( if (!w) { if (type == PROP_ENUM && icon_only) { - w = ui_text_icon_width(layout, "", ICON_BLANK1, 0); + w = ui_text_icon_width(layout, "", ICON_BLANK1, compact); if (index != RNA_ENUM_VALUE) w += 0.6f * UI_UNIT_X; } else { - w = ui_text_icon_width(layout, name, icon, 0); + /* not compact for float/int buttons, looks too squashed */ + w = ui_text_icon_width(layout, name, icon, ELEM(type, PROP_FLOAT, PROP_INT) ? false : compact); } } h = UI_UNIT_Y; @@ -1354,7 +1465,8 @@ static void ui_item_rna_size( if (index == RNA_NO_INDEX && len > 0) { if (!name[0] && icon == ICON_NONE) h = 0; - + if (layout->item.flag & UI_ITEM_PROP_SEP) + h = 0; if (ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) h += 2 * UI_UNIT_Y; else if (subtype == PROP_MATRIX) @@ -1362,7 +1474,7 @@ static void ui_item_rna_size( else h += len * UI_UNIT_Y; } - else if (ui_layout_vary_direction(layout) == UI_ITEM_VARY_X) { + else if (ui_layout_variable_size(layout)) { if (type == PROP_BOOLEAN && name[0]) w += UI_UNIT_X / 5; else if (type == PROP_ENUM && !icon_only) @@ -1382,8 +1494,22 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index PropertyType type; char namestr[UI_MAX_NAME_STR]; int len, w, h; - bool slider, toggle, expand, icon_only, no_bg; + bool slider, toggle, expand, icon_only, no_bg, compact; bool is_array; + const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0); + +#ifdef UI_PROP_DECORATE + struct { + bool use_prop_decorate; + int len; + uiLayout *layout; + uiBut *but; + } ui_decorate = { + .use_prop_decorate = ( + ((layout->item.flag & UI_ITEM_PROP_DECORATE) != 0) && + (use_prop_sep && ptr->id.data && id_can_have_animdata(ptr->id.data))), + }; +#endif /* UI_PROP_DECORATE */ UI_block_layout_set_current(block, layout); @@ -1409,13 +1535,24 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index /* pass */ } else if (ELEM(type, PROP_INT, PROP_FLOAT, PROP_STRING, PROP_POINTER)) { - name = ui_item_name_add_colon(name, namestr); + if (use_prop_sep == false) { + name = ui_item_name_add_colon(name, namestr); + } } else if (type == PROP_BOOLEAN && is_array && index == RNA_NO_INDEX) { - name = ui_item_name_add_colon(name, namestr); + if (use_prop_sep == false) { + name = ui_item_name_add_colon(name, namestr); + } } else if (type == PROP_ENUM && index != RNA_ENUM_VALUE) { - name = ui_item_name_add_colon(name, namestr); + if (flag & UI_ITEM_R_COMPACT) { + name = ""; + } + else { + if (use_prop_sep == false) { + name = ui_item_name_add_colon(name, namestr); + } + } } /* menus and pie-menus don't show checkbox without this */ @@ -1423,13 +1560,24 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index /* use checkboxes only as a fallback in pie-menu's, when no icon is defined */ ((layout->root->type == UI_LAYOUT_PIEMENU) && (icon == ICON_NONE))) { + int prop_flag = RNA_property_flag(prop); if (type == PROP_BOOLEAN && ((is_array == false) || (index != RNA_NO_INDEX))) { - if (is_array) icon = (RNA_property_boolean_get_index(ptr, prop, index)) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT; - else icon = (RNA_property_boolean_get(ptr, prop)) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT; + if (prop_flag & PROP_ICONS_CONSECUTIVE) { + icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */ + } + else if (is_array) { + icon = (RNA_property_boolean_get_index(ptr, prop, index)) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT; + } + else { + icon = (RNA_property_boolean_get(ptr, prop)) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT; + } } else if (type == PROP_ENUM && index == RNA_ENUM_VALUE) { int enum_value = RNA_property_enum_get(ptr, prop); - if (RNA_property_flag(prop) & PROP_ENUM_FLAG) { + if (prop_flag & PROP_ICONS_CONSECUTIVE) { + icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */ + } + else if (prop_flag & PROP_ENUM_FLAG) { icon = (enum_value & value) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT; } else { @@ -1443,16 +1591,111 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index expand = (flag & UI_ITEM_R_EXPAND) != 0; icon_only = (flag & UI_ITEM_R_ICON_ONLY) != 0; no_bg = (flag & UI_ITEM_R_NO_BG) != 0; + compact = (flag & UI_ITEM_R_COMPACT) != 0; /* get size */ - ui_item_rna_size(layout, name, icon, ptr, prop, index, icon_only, &w, &h); + ui_item_rna_size(layout, name, icon, ptr, prop, index, icon_only, compact, &w, &h); + + int prev_emboss = layout->emboss; + if (no_bg) { + layout->emboss = UI_EMBOSS_NONE; + } + + /* Split the label / property. */ + if (use_prop_sep) { + uiLayout *layout_row = NULL; +#ifdef UI_PROP_DECORATE + if (ui_decorate.use_prop_decorate) { + layout_row = uiLayoutRow(layout, true); + layout_row->space = 0; + ui_decorate.len = max_ii(1, len); + } +#endif /* UI_PROP_DECORATE */ + + if (name[0] == '\0') { + /* Ensure we get a column when text is not set. */ + layout = uiLayoutColumn(layout_row ? layout_row : layout, true); + layout->space = 0; + } + else { + const PropertySubType subtype = RNA_property_subtype(prop); + uiLayout *layout_split; +#ifdef UI_PROP_SEP_ICON_WIDTH_EXCEPTION + if (type == PROP_BOOLEAN && (icon == ICON_NONE) && !icon_only) { + w = UI_UNIT_X; + layout_split = uiLayoutRow(layout_row ? layout_row : layout, true); + } + else +#endif /* UI_PROP_SEP_ICON_WIDTH_EXCEPTION */ + { + layout_split = uiLayoutSplit( + layout_row ? layout_row : layout, + UI_ITEM_PROP_SEP_DIVIDE, true); + } + layout_split->space = 0; + uiLayout *layout_sub = uiLayoutColumn(layout_split, true); + layout_sub->space = 0; + + if ((index == RNA_NO_INDEX && is_array) && + ((!expand && ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA, PROP_DIRECTION)) == 0)) + { + char name_with_suffix[UI_MAX_DRAW_STR + 2]; + char str[2] = {'\0'}; + for (int a = 0; a < len; a++) { + str[0] = RNA_property_array_item_char(prop, a); + const bool use_prefix = (a == 0 && name && name[0]); + if (use_prefix) { + char *s = name_with_suffix; + s += STRNCPY_RLEN(name_with_suffix, name); + *s++ = ' '; + *s++ = str[0]; + *s++ = '\0'; + } + but = uiDefBut( + block, UI_BTYPE_LABEL, 0, use_prefix ? name_with_suffix : str, + 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + but->drawflag |= UI_BUT_TEXT_RIGHT; + but->drawflag &= ~UI_BUT_TEXT_LEFT; + } + } + else { + if (name) { + but = uiDefBut( + block, UI_BTYPE_LABEL, 0, name, + 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + but->drawflag |= UI_BUT_TEXT_RIGHT; + but->drawflag &= ~UI_BUT_TEXT_LEFT; + } + } - if (no_bg) - UI_block_emboss_set(block, UI_EMBOSS_NONE); + /* Watch out! We can only write into the new column now. */ + layout = uiLayoutColumn(layout_split, true); + layout->space = 0; + if ((type == PROP_ENUM) && (flag & UI_ITEM_R_EXPAND)) { + /* pass (expanded enums each have their own name) */ + } + else { + name = ""; + } + } + +#ifdef UI_PROP_DECORATE + if (ui_decorate.use_prop_decorate) { + ui_decorate.layout = uiLayoutColumn(layout_row, true); + ui_decorate.layout->space = 0; + UI_block_layout_set_current(block, layout); + ui_decorate.but = block->buttons.last; + } +#endif /* UI_PROP_DECORATE */ + } + /* End split. */ /* array property */ - if (index == RNA_NO_INDEX && is_array) - ui_item_array(layout, block, name, icon, ptr, prop, len, 0, 0, w, h, expand, slider, toggle, icon_only); + if (index == RNA_NO_INDEX && is_array) { + ui_item_array( + layout, block, name, icon, ptr, prop, len, 0, 0, w, h, + expand, slider, toggle, icon_only, compact, !use_prop_sep); + } /* enum item */ else if (type == PROP_ENUM && index == RNA_ENUM_VALUE) { if (icon && name[0] && !icon_only) @@ -1492,8 +1735,43 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index UI_but_flag_enable(but, UI_BUT_LIST_ITEM); } - if (no_bg) - UI_block_emboss_set(block, UI_EMBOSS); +#ifdef UI_PROP_DECORATE + if (ui_decorate.use_prop_decorate) { + const bool is_anim = RNA_property_animateable(ptr, prop); + uiBut *but_decorate = ui_decorate.but ? ui_decorate.but->next : block->buttons.first; + uiLayout *layout_col = uiLayoutColumn(ui_decorate.layout, false); + layout_col->space = 0; + layout_col->emboss = UI_EMBOSS_NONE; + int i; + for (i = 0; i < ui_decorate.len && but_decorate; i++) { + /* The icons are set in 'ui_but_anim_flag' */ + if (is_anim) { + but = uiDefIconBut( + block, UI_BTYPE_BUT, 0, ICON_DOT, 0, 0, UI_UNIT_X, UI_UNIT_Y, + NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Animate property")); + UI_but_func_set(but, ui_but_anim_decorate_cb, but, NULL); + but->flag |= UI_BUT_UNDO | UI_BUT_DRAG_LOCK; + } + else { + /* We may show other information here in future, for now use empty space. */ + but = uiDefIconBut( + block, UI_BTYPE_BUT, 0, ICON_BLANK1, 0, 0, UI_UNIT_X, UI_UNIT_Y, + NULL, 0.0, 0.0, 0.0, 0.0, ""); + but->flag |= UI_BUT_DISABLED; + } + /* Order the decorator after the button we decorate, this is used so we can always + * do a quick lookup. */ + BLI_remlink(&block->buttons, but); + BLI_insertlinkafter(&block->buttons, but_decorate, but); + but_decorate = but->next; + } + BLI_assert(ELEM(i, 1, ui_decorate.len)); + } +#endif /* UI_PROP_DECORATE */ + + if (no_bg) { + layout->emboss = prev_emboss; + } /* ensure text isn't added to icon_only buttons */ if (but && icon_only) { @@ -1641,94 +1919,6 @@ void uiItemsEnumR(uiLayout *layout, struct PointerRNA *ptr, const char *propname /* Pointer RNA button with search */ -typedef struct CollItemSearch { - struct CollItemSearch *next, *prev; - char *name; - int index; - int iconid; -} CollItemSearch; - -static int sort_search_items_list(const void *a, const void *b) -{ - const CollItemSearch *cis1 = a; - const CollItemSearch *cis2 = b; - - if (BLI_strcasecmp(cis1->name, cis2->name) > 0) - return 1; - else - return 0; -} - -static void rna_search_cb(const struct bContext *C, void *arg_but, const char *str, uiSearchItems *items) -{ - uiBut *but = arg_but; - char *name; - int i = 0, iconid = 0, flag = RNA_property_flag(but->rnaprop); - ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list"); - CollItemSearch *cis; - const bool skip_filter = !but->changed; - - /* build a temporary list of relevant items first */ - RNA_PROP_BEGIN (&but->rnasearchpoin, itemptr, but->rnasearchprop) - { - if (flag & PROP_ID_SELF_CHECK) - if (itemptr.data == but->rnapoin.id.data) - continue; - - /* use filter */ - if (RNA_property_type(but->rnaprop) == PROP_POINTER) { - if (RNA_property_pointer_poll(&but->rnapoin, but->rnaprop, &itemptr) == 0) - continue; - } - - if (itemptr.type && RNA_struct_is_ID(itemptr.type)) { - ID *id = itemptr.data; - char name_ui[MAX_ID_NAME]; - -#if 0 /* this name is used for a string comparison and can't be modified, TODO */ - /* if ever enabled, make name_ui be MAX_ID_NAME+1 */ - BKE_id_ui_prefix(name_ui, id); -#else - BLI_strncpy(name_ui, id->name + 2, sizeof(name_ui)); -#endif - name = BLI_strdup(name_ui); - iconid = ui_id_icon_get(C, id, false); - } - else { - name = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); /* could use the string length here */ - iconid = 0; - } - - if (name) { - if (skip_filter || BLI_strcasestr(name, str)) { - cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch"); - cis->name = MEM_dupallocN(name); - cis->index = i; - cis->iconid = iconid; - BLI_addtail(items_list, cis); - } - MEM_freeN(name); - } - - i++; - } - RNA_PROP_END; - - BLI_listbase_sort(items_list, sort_search_items_list); - - /* add search items from temporary list */ - for (cis = items_list->first; cis; cis = cis->next) { - if (false == UI_search_item_add(items, cis->name, SET_INT_IN_POINTER(cis->index), cis->iconid)) { - break; - } - } - - for (cis = items_list->first; cis; cis = cis->next) { - MEM_freeN(cis->name); - } - BLI_freelistN(items_list); - MEM_freeN(items_list); -} static void search_id_collection(StructRNA *ptype, PointerRNA *ptr, PropertyRNA **prop) { @@ -1771,6 +1961,8 @@ void ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRN /* turn button into search button */ if (searchprop) { + uiRNACollectionSearch *coll_search = MEM_mallocN(sizeof(*coll_search), __func__); + but->type = UI_BTYPE_SEARCH_MENU; but->hardmax = MAX2(but->hardmax, 256.0f); but->rnasearchpoin = *searchptr; @@ -1780,13 +1972,22 @@ void ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRN but->flag |= UI_BUT_VALUE_CLEAR; } + coll_search->target_ptr = *ptr; + coll_search->target_prop = prop; + coll_search->search_ptr = *searchptr; + coll_search->search_prop = searchprop; + coll_search->but_changed = &but->changed; + if (RNA_property_type(prop) == PROP_ENUM) { /* XXX, this will have a menu string, * but in this case we just want the text */ but->str[0] = 0; } - UI_but_func_search_set(but, ui_searchbox_create_generic, rna_search_cb, but, NULL, NULL); + UI_but_func_search_set( + but, ui_searchbox_create_generic, ui_rna_collection_search_cb, + coll_search, NULL, NULL); + but->free_search_arg = true; } else if (but->type == UI_BTYPE_SEARCH_MENU) { /* In case we fail to find proper searchprop, so other code might have already set but->type to search menu... */ @@ -1803,6 +2004,7 @@ void uiItemPointerR(uiLayout *layout, struct PointerRNA *ptr, const char *propna StructRNA *icontype; int w, h; char namestr[UI_MAX_NAME_STR]; + const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0); /* validate arguments */ prop = RNA_struct_find_property(ptr, propname); @@ -1845,12 +2047,14 @@ void uiItemPointerR(uiLayout *layout, struct PointerRNA *ptr, const char *propna if (!name) name = RNA_property_ui_name(prop); - name = ui_item_name_add_colon(name, namestr); + if (use_prop_sep == false) { + name = ui_item_name_add_colon(name, namestr); + } /* create button */ block = uiLayoutGetBlock(layout); - ui_item_rna_size(layout, name, icon, ptr, prop, 0, 0, &w, &h); + ui_item_rna_size(layout, name, icon, ptr, prop, 0, 0, false, &w, &h); w += UI_UNIT_X; /* X icon needs more space */ but = ui_item_with_label(layout, block, name, icon, ptr, prop, 0, 0, 0, w, h, 0); @@ -1868,6 +2072,15 @@ static void ui_item_menutype_func(bContext *C, uiLayout *layout, void *arg_mt) layout->root->block->flag ^= UI_BLOCK_IS_FLIP; } +void ui_item_paneltype_func(bContext *C, uiLayout *layout, void *arg_pt) +{ + PanelType *pt = (PanelType *)arg_pt; + UI_paneltype_draw(C, pt, layout); + + /* panels are created flipped (from event handling pov) */ + layout->root->block->flag ^= UI_BLOCK_IS_FLIP; +} + static uiBut *ui_item_menu( uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg, void *argN, const char *tip, bool force_menu) @@ -1878,9 +2091,6 @@ static uiBut *ui_item_menu( UI_block_layout_set_current(block, layout); - if (layout->root->type == UI_LAYOUT_HEADER) - UI_block_emboss_set(block, UI_EMBOSS); - if (!name) name = ""; if (layout->root->type == UI_LAYOUT_MENU && !icon) @@ -1890,7 +2100,10 @@ static uiBut *ui_item_menu( h = UI_UNIT_Y; if (layout->root->type == UI_LAYOUT_HEADER) { /* ugly .. */ - if (force_menu) { + if (icon == ICON_NONE && force_menu) { + /* pass */ + } + else if (force_menu) { w += UI_UNIT_X; } else { @@ -1912,9 +2125,6 @@ static uiBut *ui_item_menu( but->func_argN = argN; } - if (layout->root->type == UI_LAYOUT_HEADER) { - UI_block_emboss_set(block, UI_EMBOSS); - } if (ELEM(layout->root->type, UI_LAYOUT_PANEL, UI_LAYOUT_TOOLBAR) || (force_menu && layout->root->type != UI_LAYOUT_MENU)) /* We never want a dropdown in menu! */ { @@ -1945,6 +2155,76 @@ void uiItemM(uiLayout *layout, const char *menuname, const char *name, int icon) ui_item_menu(layout, name, icon, ui_item_menutype_func, mt, NULL, TIP_(mt->description), false); } +/* popover */ +void uiItemPopoverPanel_ptr(uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon) +{ + if (!name) { + name = CTX_IFACE_(pt->translation_context, pt->label); + } + + if (layout->root->type == UI_LAYOUT_MENU && !icon) { + icon = ICON_BLANK1; + } + + const bool ok = (pt->poll == NULL) || pt->poll(C, pt); + if (ok && (pt->draw_header != NULL)) { + layout = uiLayoutRow(layout, true); + Panel panel = { + .type = pt, + .layout = layout, + .flag = PNL_POPOVER, + }; + pt->draw_header(C, &panel); + } + uiBut *but = ui_item_menu(layout, name, icon, ui_item_paneltype_func, pt, NULL, NULL, true); + but->type = UI_BTYPE_POPOVER; + if (!ok) { + but->flag |= UI_BUT_DISABLED; + } +} + +void uiItemPopoverPanel( + uiLayout *layout, bContext *C, + const char *panel_type, const char *name, int icon) +{ + PanelType *pt = WM_paneltype_find(panel_type, true); + if (pt == NULL) { + RNA_warning("Panel type not found '%s'", panel_type); + return; + } + uiItemPopoverPanel_ptr(layout, C, pt, name, icon); +} + +void uiItemPopoverPanelFromGroup( + uiLayout *layout, bContext *C, + int space_id, int region_id, const char *context, const char *category) +{ + SpaceType *st = BKE_spacetype_from_id(space_id); + if (st == NULL) { + RNA_warning("space type not found %d", space_id); + return; + } + ARegionType *art = BKE_regiontype_from_id(st, region_id); + if (art == NULL) { + RNA_warning("region type not found %d", region_id); + return; + } + + for (PanelType *pt = art->paneltypes.first; pt; pt = pt->next) { + /* Causes too many panels, check context. */ + if (pt->parent_id[0] == '\0') { + if (/* (*context == '\0') || */ STREQ(pt->context, context)) { + if ((*category == '\0') || STREQ(pt->category, category)) { + if (pt->poll == NULL || pt->poll(C, pt)) { + uiItemPopoverPanel_ptr(layout, C, pt, NULL, ICON_NONE); + } + } + } + } + } +} + + /* label item */ static uiBut *uiItemL_(uiLayout *layout, const char *name, int icon) { @@ -2035,6 +2315,26 @@ void uiItemS(uiLayout *layout) uiDefBut(block, (is_menu) ? UI_BTYPE_SEPR_LINE : UI_BTYPE_SEPR, 0, "", 0, 0, space, space, NULL, 0.0, 0.0, 0, 0, ""); } +/* Flexible spacing. */ +void uiItemSpacer(uiLayout *layout) +{ + uiBlock *block = layout->root->block; + bool is_menu = ui_block_is_menu(block); + + if (is_menu) { + printf("Error: separator_spacer() not supported in menus.\n"); + return; + } + + if (block->direction & UI_DIR_RIGHT) { + printf("Error: separator_spacer() only supported in horizontal blocks.\n"); + return; + } + + UI_block_layout_set_current(block, layout); + uiDefBut(block, UI_BTYPE_SEPR_SPACER, 0, "", 0, 0, 0.3f * UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); +} + /* level items */ void uiItemMenuF(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg) { @@ -2419,7 +2719,7 @@ static bool ui_item_is_radial_displayable(uiItem *item) static bool ui_item_is_radial_drawable(uiButtonItem *bitem) { - if (ELEM(bitem->but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) + if (ELEM(bitem->but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER)) return false; return true; @@ -2692,6 +2992,364 @@ static void ui_litem_layout_column_flow(uiLayout *litem) litem->y = miny; } +/* multi-column and multi-row layout. */ +typedef struct UILayoutGridFlowInput { + /* General layout controll settings. */ + const bool row_major : 1; /* Fill rows before columns */ + const bool even_columns : 1; /* All columns will have same width. */ + const bool even_rows : 1; /* All rows will have same height. */ + const int space_x; /* Space between columns. */ + const int space_y; /* Space between rows. */ + /* Real data about current position and size of this layout item (either estimated, or final values). */ + const int litem_w; /* Layout item width. */ + const int litem_x; /* Layout item X position. */ + const int litem_y; /* Layout item Y position. */ + /* Actual number of columns and rows to generate (computed from first pass usually). */ + const int tot_columns; /* Number of columns. */ + const int tot_rows; /* Number of rows. */ +} UILayoutGridFlowInput; + +typedef struct UILayoutGridFlowOutput { + int *tot_items; /* Total number of items in this grid layout. */ + /* Width / X pos data. */ + float *global_avg_w; /* Computed average width of the columns. */ + int *cos_x_array; /* Computed X coordinate of each column. */ + int *widths_array; /* Computed width of each column. */ + int *tot_w; /* Computed total width. */ + /* Height / Y pos data. */ + int *global_max_h; /* Computed height of the tallest item in the grid. */ + int *cos_y_array; /* Computed Y coordinate of each column. */ + int *heights_array; /* Computed height of each column. */ + int *tot_h; /* Computed total height. */ +} UILayoutGridFlowOutput; + +static void ui_litem_grid_flow_compute( + ListBase *items, UILayoutGridFlowInput *parameters, UILayoutGridFlowOutput *results) +{ + uiItem *item; + int i; + + float tot_w = 0.0f, tot_h = 0.0f; + float global_avg_w = 0.0f, global_totweight_w = 0.0f; + int global_max_h = 0; + + float *avg_w = NULL, *totweight_w = NULL; + int *max_h = NULL; + + BLI_assert(parameters->tot_columns != 0 || (results->cos_x_array == NULL && results->widths_array == NULL && results->tot_w == NULL)); + BLI_assert(parameters->tot_rows != 0 || (results->cos_y_array == NULL && results->heights_array == NULL && results->tot_h == NULL)); + + if (results->tot_items) { + *results->tot_items = 0; + } + + if (items->first == NULL) { + if (results->global_avg_w) { + *results->global_avg_w = 0.0f; + } + if (results->global_max_h) { + *results->global_max_h = 0; + } + return; + } + + if (parameters->tot_columns != 0) { + avg_w = BLI_array_alloca(avg_w, parameters->tot_columns); + totweight_w = BLI_array_alloca(totweight_w, parameters->tot_columns); + memset(avg_w, 0, sizeof(*avg_w) * parameters->tot_columns); + memset(totweight_w, 0, sizeof(*totweight_w) * parameters->tot_columns); + } + if (parameters->tot_rows != 0) { + max_h = BLI_array_alloca(max_h, parameters->tot_rows); + memset(max_h, 0, sizeof(*max_h) * parameters->tot_rows); + } + + for (i = 0, item = items->first; item; item = item->next, i++) { + int item_w, item_h; + ui_item_size(item, &item_w, &item_h); + + global_avg_w += (float)(item_w * item_w); + global_totweight_w += (float)item_w; + global_max_h = max_ii(global_max_h, item_h); + + if (parameters->tot_rows != 0 && parameters->tot_columns != 0) { + const int index_col = parameters->row_major ? i % parameters->tot_columns : i / parameters->tot_rows; + const int index_row = parameters->row_major ? i / parameters->tot_columns : i % parameters->tot_rows; + + avg_w[index_col] += (float)(item_w * item_w); + totweight_w[index_col] += (float)item_w; + + max_h[index_row] = max_ii(max_h[index_row], item_h); + } + + if (results->tot_items) { + (*results->tot_items)++; + } + } + + /* Finalize computing of column average sizes */ + global_avg_w /= global_totweight_w; + if (parameters->tot_columns != 0) { + for (i = 0; i < parameters->tot_columns; i++) { + avg_w[i] /= totweight_w[i]; + tot_w += avg_w[i]; + } + if (parameters->even_columns) { + tot_w = ceilf(global_avg_w) * parameters->tot_columns; + } + } + /* Finalize computing of rows max sizes */ + if (parameters->tot_rows != 0) { + for (i = 0; i < parameters->tot_rows; i++) { + tot_h += max_h[i]; + } + if (parameters->even_rows) { + tot_h = global_max_h * parameters->tot_columns; + } + } + + /* Compute positions and sizes of all cells. */ + if (results->cos_x_array != NULL && results->widths_array != NULL) { + /* We enlarge/narrow columns evenly to match available width. */ + const float wfac = (float)(parameters->litem_w - (parameters->tot_columns - 1) * parameters->space_x) / tot_w; + + for (int col = 0; col < parameters->tot_columns; col++) { + results->cos_x_array[col] = ( + col ? + results->cos_x_array[col - 1] + results->widths_array[col - 1] + parameters->space_x : + parameters->litem_x + ); + if (parameters->even_columns) { + /* (< remaining width > - < space between remaining columns >) / < remaining columns > */ + results->widths_array[col] = ( + ((parameters->litem_w - (results->cos_x_array[col] - parameters->litem_x)) - + (parameters->tot_columns - col - 1) * parameters->space_x) / (parameters->tot_columns - col)); + } + else if (col == parameters->tot_columns - 1) { + /* Last column copes width rounding errors... */ + results->widths_array[col] = parameters->litem_w - (results->cos_x_array[col] - parameters->litem_x); + } + else { + results->widths_array[col] = (int)(avg_w[col] * wfac); + } + } + } + if (results->cos_y_array != NULL && results->heights_array != NULL) { + for (int row = 0; row < parameters->tot_rows; row++) { + if (parameters->even_rows) { + results->heights_array[row] = global_max_h; + } + else { + results->heights_array[row] = max_h[row]; + } + results->cos_y_array[row] = ( + row ? + results->cos_y_array[row - 1] - parameters->space_y - results->heights_array[row] : + parameters->litem_y - results->heights_array[row]); + } + } + + if (results->global_avg_w) { + *results->global_avg_w = global_avg_w; + } + if (results->global_max_h) { + *results->global_max_h = global_max_h; + } + if (results->tot_w) { + *results->tot_w = (int)tot_w + parameters->space_x * (parameters->tot_columns - 1); + } + if (results->tot_h) { + *results->tot_h = tot_h + parameters->space_y * (parameters->tot_rows - 1); + } +} + +static void ui_litem_estimate_grid_flow(uiLayout *litem) +{ + uiStyle *style = litem->root->style; + uiLayoutItemGridFlow *gflow = (uiLayoutItemGridFlow *)litem; + + const int space_x = style->columnspace; + const int space_y = style->buttonspacey; + + /* Estimate average needed width and height per item. */ + { + float avg_w; + int max_h; + + ui_litem_grid_flow_compute( + &litem->items, + &((UILayoutGridFlowInput) { + .row_major = gflow->row_major, + .even_columns = gflow->even_columns, + .even_rows = gflow->even_rows, + .litem_w = litem->w, + .litem_x = litem->x, + .litem_y = litem->y, + .space_x = space_x, + .space_y = space_y, + }), + &((UILayoutGridFlowOutput) { + .tot_items = &gflow->tot_items, + .global_avg_w = &avg_w, + .global_max_h = &max_h, + })); + + if (gflow->tot_items == 0) { + litem->w = litem->h = 0; + gflow->tot_columns = gflow->tot_rows = 0; + return; + } + + /* Even in varying column width case, we fix our columns number from weighted average width of items, + * a proper solving of required width would be too costly, and this should give reasonably good results + * in all resonable cases... */ + if (gflow->columns_len > 0) { + gflow->tot_columns = gflow->columns_len; + } + else { + if (avg_w == 0.0f) { + gflow->tot_columns = 1; + } + else { + gflow->tot_columns = min_ii(max_ii((int)(litem->w / avg_w), 1), gflow->tot_items); + } + } + gflow->tot_rows = (int)ceilf((float)gflow->tot_items / gflow->tot_columns); + + /* Try to tweak number of columns and rows to get better filling of last column or row, + * and apply 'modulo' value to number of columns or rows. + * Note that modulo does not prevent ending with fewer columns/rows than modulo, if mandatory + * to avoid empty column/row. */ + { + const int modulo = (gflow->columns_len < -1) ? -gflow->columns_len : 0; + const int step = modulo ? modulo : 1; + + if (gflow->row_major) { + /* Adjust number of columns to be mutiple of given modulo. */ + if (modulo && gflow->tot_columns % modulo != 0 && gflow->tot_columns > modulo) { + gflow->tot_columns = gflow->tot_columns - (gflow->tot_columns % modulo); + } + /* Find smallest number of columns conserving computed optimal number of rows. */ + for (gflow->tot_rows = (int)ceilf((float)gflow->tot_items / gflow->tot_columns); + (gflow->tot_columns - step) > 0 && + (int)ceilf((float)gflow->tot_items / (gflow->tot_columns - step)) <= gflow->tot_rows; + gflow->tot_columns -= step); + } + else { + /* Adjust number of rows to be mutiple of given modulo. */ + if (modulo && gflow->tot_rows % modulo != 0) { + gflow->tot_rows = min_ii(gflow->tot_rows + modulo - (gflow->tot_rows % modulo), gflow->tot_items); + } + /* Find smallest number of rows conserving computed optimal number of columns. */ + for (gflow->tot_columns = (int)ceilf((float)gflow->tot_items / gflow->tot_rows); + (gflow->tot_rows - step) > 0 && + (int)ceilf((float)gflow->tot_items / (gflow->tot_rows - step)) <= gflow->tot_columns; + gflow->tot_rows -= step); + } + } + + /* Set evenly-spaced axes size (quick optimization in case we have even columns and rows). */ + if (gflow->even_columns && gflow->even_rows) { + litem->w = (int)(gflow->tot_columns * avg_w) + space_x * (gflow->tot_columns - 1); + litem->h = (int)(gflow->tot_rows * max_h) + space_y * (gflow->tot_rows - 1); + return; + } + } + + /* Now that we have our final number of columns and rows, + * we can compute actual needed space for non-evenly sized axes. */ + { + int tot_w, tot_h; + + ui_litem_grid_flow_compute( + &litem->items, + &((UILayoutGridFlowInput) { + .row_major = gflow->row_major, + .even_columns = gflow->even_columns, + .even_rows = gflow->even_rows, + .litem_w = litem->w, + .litem_x = litem->x, + .litem_y = litem->y, + .space_x = space_x, + .space_y = space_y, + .tot_columns = gflow->tot_columns, + .tot_rows = gflow->tot_rows, + }), + &((UILayoutGridFlowOutput) { + .tot_w = &tot_w, + .tot_h = &tot_h, + })); + + litem->w = tot_w; + litem->h = tot_h; + } +} + +static void ui_litem_layout_grid_flow(uiLayout *litem) +{ + int i; + uiStyle *style = litem->root->style; + uiLayoutItemGridFlow *gflow = (uiLayoutItemGridFlow *)litem; + uiItem *item; + + if (gflow->tot_items == 0) { + litem->w = litem->h = 0; + return; + } + + BLI_assert(gflow->tot_columns > 0); + BLI_assert(gflow->tot_rows > 0); + + const int space_x = style->columnspace; + const int space_y = style->buttonspacey; + + int *widths = BLI_array_alloca(widths, gflow->tot_columns); + int *heights = BLI_array_alloca(heights, gflow->tot_rows); + int *cos_x = BLI_array_alloca(cos_x, gflow->tot_columns); + int *cos_y = BLI_array_alloca(cos_y, gflow->tot_rows); + + /* This time we directly compute coordinates and sizes of all cells. */ + ui_litem_grid_flow_compute( + &litem->items, + &((UILayoutGridFlowInput) { + .row_major = gflow->row_major, + .even_columns = gflow->even_columns, + .even_rows = gflow->even_rows, + .litem_w = litem->w, + .litem_x = litem->x, + .litem_y = litem->y, + .space_x = space_x, + .space_y = space_y, + .tot_columns = gflow->tot_columns, + .tot_rows = gflow->tot_rows, + }), + &((UILayoutGridFlowOutput) { + .cos_x_array = cos_x, + .cos_y_array = cos_y, + .widths_array = widths, + .heights_array = heights, + })); + + for (item = litem->items.first, i = 0; item; item = item->next, i++) { + const int col = gflow->row_major ? i % gflow->tot_columns : i / gflow->tot_rows; + const int row = gflow->row_major ? i / gflow->tot_columns : i % gflow->tot_rows; + int item_w, item_h; + ui_item_size(item, &item_w, &item_h); + + const int w = widths[col]; + const int h = heights[row]; + + item_w = (litem->alignment == UI_LAYOUT_ALIGN_EXPAND) ? w : min_ii(w, item_w); + item_h = (litem->alignment == UI_LAYOUT_ALIGN_EXPAND) ? h : min_ii(h, item_h); + + ui_item_position(item, cos_x[col], cos_y[row], item_w, item_h); + } + + litem->h = litem->y - cos_y[gflow->tot_rows - 1]; + litem->x = (cos_x[gflow->tot_columns - 1] - litem->x) + widths[gflow->tot_columns - 1]; + litem->y = litem->y - litem->h; +} + /* free layout */ static void ui_litem_estimate_absolute(uiLayout *litem) { @@ -2861,22 +3519,32 @@ static void ui_litem_layout_overlap(uiLayout *litem) litem->y = y - litem->h; } -/* layout create functions */ -uiLayout *uiLayoutRow(uiLayout *layout, bool align) +static void ui_litem_init_from_parent(uiLayout *litem, uiLayout *layout, int align) { - uiLayout *litem; - - litem = MEM_callocN(sizeof(uiLayout), "uiLayoutRow"); - litem->item.type = ITEM_LAYOUT_ROW; litem->root = layout->root; litem->align = align; + /* Children of gridflow layout shall never have "ideal big size" returned as estimated size. */ + litem->variable_size = layout->variable_size || layout->item.type == ITEM_LAYOUT_GRID_FLOW; litem->active = true; litem->enabled = true; litem->context = layout->context; - litem->space = (align) ? 0 : layout->root->style->buttonspacex; litem->redalert = layout->redalert; litem->w = layout->w; + litem->emboss = layout->emboss; + litem->item.flag = (layout->item.flag & (UI_ITEM_PROP_SEP | UI_ITEM_PROP_DECORATE)); BLI_addtail(&layout->items, litem); +} + +/* layout create functions */ +uiLayout *uiLayoutRow(uiLayout *layout, bool align) +{ + uiLayout *litem; + + litem = MEM_callocN(sizeof(uiLayout), "uiLayoutRow"); + ui_litem_init_from_parent(litem, layout, align); + + litem->item.type = ITEM_LAYOUT_ROW; + litem->space = (align) ? 0 : layout->root->style->buttonspacex; UI_block_layout_set_current(layout->root->block, litem); @@ -2888,16 +3556,10 @@ uiLayout *uiLayoutColumn(uiLayout *layout, bool align) uiLayout *litem; litem = MEM_callocN(sizeof(uiLayout), "uiLayoutColumn"); + ui_litem_init_from_parent(litem, layout, align); + litem->item.type = ITEM_LAYOUT_COLUMN; - litem->root = layout->root; - litem->align = align; - litem->active = true; - litem->enabled = true; - litem->context = layout->context; - litem->space = (litem->align) ? 0 : layout->root->style->buttonspacey; - litem->redalert = layout->redalert; - litem->w = layout->w; - BLI_addtail(&layout->items, litem); + litem->space = (align) ? 0 : layout->root->style->buttonspacey; UI_block_layout_set_current(layout->root->block, litem); @@ -2909,17 +3571,31 @@ uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, bool align) uiLayoutItemFlow *flow; flow = MEM_callocN(sizeof(uiLayoutItemFlow), "uiLayoutItemFlow"); + ui_litem_init_from_parent(&flow->litem, layout, align); + flow->litem.item.type = ITEM_LAYOUT_COLUMN_FLOW; - flow->litem.root = layout->root; - flow->litem.align = align; - flow->litem.active = true; - flow->litem.enabled = true; - flow->litem.context = layout->context; flow->litem.space = (flow->litem.align) ? 0 : layout->root->style->columnspace; - flow->litem.redalert = layout->redalert; - flow->litem.w = layout->w; flow->number = number; - BLI_addtail(&layout->items, flow); + + UI_block_layout_set_current(layout->root->block, &flow->litem); + + return &flow->litem; +} + +uiLayout *uiLayoutGridFlow( + uiLayout *layout, bool row_major, int columns_len, bool even_columns, bool even_rows, bool align) +{ + uiLayoutItemGridFlow *flow; + + flow = MEM_callocN(sizeof(uiLayoutItemGridFlow), __func__); + flow->litem.item.type = ITEM_LAYOUT_GRID_FLOW; + ui_litem_init_from_parent(&flow->litem, layout, align); + + flow->litem.space = (flow->litem.align) ? 0 : layout->root->style->columnspace; + flow->row_major = row_major; + flow->columns_len = columns_len; + flow->even_columns = even_columns; + flow->even_rows = even_rows; UI_block_layout_set_current(layout->root->block, &flow->litem); @@ -2931,15 +3607,10 @@ static uiLayoutItemBx *ui_layout_box(uiLayout *layout, int type) uiLayoutItemBx *box; box = MEM_callocN(sizeof(uiLayoutItemBx), "uiLayoutItemBx"); + ui_litem_init_from_parent(&box->litem, layout, false); + box->litem.item.type = ITEM_LAYOUT_BOX; - box->litem.root = layout->root; - box->litem.active = 1; - box->litem.enabled = 1; - box->litem.context = layout->context; box->litem.space = layout->root->style->columnspace; - box->litem.redalert = layout->redalert; - box->litem.w = layout->w; - BLI_addtail(&layout->items, box); UI_block_layout_set_current(layout->root->block, &box->litem); @@ -2967,14 +3638,9 @@ uiLayout *uiLayoutRadial(uiLayout *layout) } litem = MEM_callocN(sizeof(uiLayout), "uiLayoutRadial"); + ui_litem_init_from_parent(litem, layout, false); + litem->item.type = ITEM_LAYOUT_RADIAL; - litem->root = layout->root; - litem->active = true; - litem->enabled = true; - litem->context = layout->context; - litem->redalert = layout->redalert; - litem->w = layout->w; - BLI_addtail(&layout->root->layout->items, litem); UI_block_layout_set_current(layout->root->block, litem); @@ -3031,14 +3697,9 @@ uiLayout *uiLayoutAbsolute(uiLayout *layout, bool align) uiLayout *litem; litem = MEM_callocN(sizeof(uiLayout), "uiLayoutAbsolute"); + ui_litem_init_from_parent(litem, layout, align); + litem->item.type = ITEM_LAYOUT_ABSOLUTE; - litem->root = layout->root; - litem->align = align; - litem->active = 1; - litem->enabled = 1; - litem->context = layout->context; - litem->redalert = layout->redalert; - BLI_addtail(&layout->items, litem); UI_block_layout_set_current(layout->root->block, litem); @@ -3060,13 +3721,9 @@ uiLayout *uiLayoutOverlap(uiLayout *layout) uiLayout *litem; litem = MEM_callocN(sizeof(uiLayout), "uiLayoutOverlap"); + ui_litem_init_from_parent(litem, layout, false); + litem->item.type = ITEM_LAYOUT_OVERLAP; - litem->root = layout->root; - litem->active = true; - litem->enabled = true; - litem->context = layout->context; - litem->redalert = layout->redalert; - BLI_addtail(&layout->items, litem); UI_block_layout_set_current(layout->root->block, litem); @@ -3078,17 +3735,11 @@ uiLayout *uiLayoutSplit(uiLayout *layout, float percentage, bool align) uiLayoutItemSplit *split; split = MEM_callocN(sizeof(uiLayoutItemSplit), "uiLayoutItemSplit"); + ui_litem_init_from_parent(&split->litem, layout, align); + split->litem.item.type = ITEM_LAYOUT_SPLIT; - split->litem.root = layout->root; - split->litem.align = align; - split->litem.active = true; - split->litem.enabled = true; - split->litem.context = layout->context; split->litem.space = layout->root->style->columnspace; - split->litem.redalert = layout->redalert; - split->litem.w = layout->w; split->percentage = percentage; - BLI_addtail(&layout->items, split); UI_block_layout_set_current(layout->root->block, &split->litem); @@ -3130,6 +3781,31 @@ void uiLayoutSetScaleY(uiLayout *layout, float scale) layout->scale[1] = scale; } +void uiLayoutSetEmboss(uiLayout *layout, char emboss) +{ + layout->emboss = emboss; +} + +bool uiLayoutGetPropSep(uiLayout *layout) +{ + return (layout->item.flag & UI_ITEM_PROP_SEP) != 0; +} + +void uiLayoutSetPropSep(uiLayout *layout, bool is_sep) +{ + SET_FLAG_FROM_TEST(layout->item.flag, is_sep, UI_ITEM_PROP_SEP); +} + +bool uiLayoutGetPropDecorate(uiLayout *layout) +{ + return (layout->item.flag & UI_ITEM_PROP_DECORATE) != 0; +} + +void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep) +{ + SET_FLAG_FROM_TEST(layout->item.flag, is_sep, UI_ITEM_PROP_DECORATE); +} + bool uiLayoutGetActive(uiLayout *layout) { return layout->active; @@ -3170,6 +3846,16 @@ float uiLayoutGetScaleY(uiLayout *layout) return layout->scale[1]; } +int uiLayoutGetEmboss(uiLayout *layout) +{ + if (layout->emboss == UI_EMBOSS_UNDEFINED) { + return layout->root->block->dt; + } + else { + return layout->emboss; + } +} + /********************** Layout *******************/ static void ui_item_scale(uiLayout *litem, const float scale[2]) @@ -3178,6 +3864,11 @@ static void ui_item_scale(uiLayout *litem, const float scale[2]) int x, y, w, h; for (item = litem->items.last; item; item = item->prev) { + if (item->type != ITEM_BUTTON) { + uiLayout *subitem = (uiLayout *)item; + ui_item_scale(subitem, scale); + } + ui_item_size(item, &w, &h); ui_item_offset(item, &x, &y); @@ -3221,6 +3912,9 @@ static void ui_item_estimate(uiItem *item) case ITEM_LAYOUT_COLUMN_FLOW: ui_litem_estimate_column_flow(litem); break; + case ITEM_LAYOUT_GRID_FLOW: + ui_litem_estimate_grid_flow(litem); + break; case ITEM_LAYOUT_ROW: ui_litem_estimate_row(litem); break; @@ -3320,6 +4014,9 @@ static void ui_item_layout(uiItem *item) case ITEM_LAYOUT_COLUMN_FLOW: ui_litem_layout_column_flow(litem); break; + case ITEM_LAYOUT_GRID_FLOW: + ui_litem_layout_grid_flow(litem); + break; case ITEM_LAYOUT_ROW: ui_litem_layout_row(litem); break; @@ -3416,6 +4113,9 @@ uiLayout *UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int s layout = MEM_callocN(sizeof(uiLayout), "uiLayout"); layout->item.type = ITEM_LAYOUT_ROOT; + /* Only used when 'UI_ITEM_PROP_SEP' is set. */ + layout->item.flag = UI_ITEM_PROP_DECORATE; + layout->x = x; layout->y = y; layout->root = root; @@ -3423,10 +4123,15 @@ uiLayout *UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int s layout->active = 1; layout->enabled = 1; layout->context = NULL; + layout->emboss = UI_EMBOSS_UNDEFINED; if (type == UI_LAYOUT_MENU || type == UI_LAYOUT_PIEMENU) layout->space = 0; + if (type == UI_LAYOUT_TOOLBAR) { + block->flag |= UI_BLOCK_SHOW_SHORTCUT_ALWAYS; + } + if (dir == UI_LAYOUT_HORIZONTAL) { layout->h = size; layout->root->emh = em * UI_UNIT_Y; @@ -3483,6 +4188,10 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but) but->context = layout->context; but->context->used = true; } + + if (layout->emboss != UI_EMBOSS_UNDEFINED) { + but->dt = layout->emboss; + } } void uiLayoutSetOperatorContext(uiLayout *layout, int opcontext) @@ -3562,6 +4271,18 @@ MenuType *UI_but_menutype_get(uiBut *but) } } +/* this is a bit of a hack but best keep it in one place at least */ +PanelType *UI_but_paneltype_get(uiBut *but) +{ + if (but->menu_create_func == ui_item_paneltype_func) { + return (PanelType *)but->poin; + } + else { + return NULL; + } +} + + void UI_menutype_draw(bContext *C, MenuType *mt, struct uiLayout *layout) { Menu menu = { @@ -3583,3 +4304,64 @@ void UI_menutype_draw(bContext *C, MenuType *mt, struct uiLayout *layout) CTX_store_set(C, NULL); } } + + +static void ui_paneltype_draw_impl( + bContext *C, PanelType *pt, uiLayout *layout, bool show_header) +{ + Panel *panel = MEM_callocN(sizeof(Panel), "popover panel"); + panel->type = pt; + panel->flag = PNL_POPOVER; + + uiLayout *last_item = layout->items.last; + + /* Draw main panel. */ + if (show_header) { + uiLayout *row = uiLayoutRow(layout, false); + if (pt->draw_header) { + panel->layout = row; + pt->draw_header(C, panel); + panel->layout = NULL; + } + uiItemL(row, pt->label, ICON_NONE); + } + + panel->layout = layout; + pt->draw(C, panel); + panel->layout = NULL; + + MEM_freeN(panel); + + /* Draw child panels. */ + for (LinkData *link = pt->children.first; link; link = link->next) { + PanelType *child_pt = link->data; + + if (child_pt->poll == NULL || child_pt->poll(C, child_pt)) { + /* Add space if something was added to the layout. */ + if (last_item != layout->items.last) { + uiItemS(layout); + last_item = layout->items.last; + } + + uiLayout *col = uiLayoutColumn(layout, false); + ui_paneltype_draw_impl(C, child_pt, col, true); + } + } +} + +/** + * Used for popup panels only. + */ +void UI_paneltype_draw(bContext *C, PanelType *pt, uiLayout *layout) +{ + if (layout->context) { + CTX_store_set(C, layout->context); + } + + ui_paneltype_draw_impl(C, pt, layout, false); + + if (layout->context) { + CTX_store_set(C, NULL); + } + +} diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 1e67ecdfc90..ec10fc9d494 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -42,14 +42,20 @@ #include "BLT_lang.h" #include "BKE_context.h" +#include "BKE_idprop.h" +#include "BKE_layer.h" #include "BKE_screen.h" #include "BKE_global.h" +#include "BKE_library_override.h" #include "BKE_node.h" #include "BKE_text.h" /* for UI_OT_reports_to_text */ #include "BKE_report.h" +#include "DEG_depsgraph.h" + #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_types.h" #include "UI_interface.h" @@ -328,6 +334,217 @@ static void UI_OT_unset_property_button(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; } + +/* Note that we use different values for UI/UX than 'real' override operations, user does not care + * whether it's added or removed for the differential operation e.g. */ +enum { + UIOverride_Type_NOOP = 0, + UIOverride_Type_Replace = 1, + UIOverride_Type_Difference = 2, /* Add/subtract */ + UIOverride_Type_Factor = 3, /* Multiply */ + /* TODO: should/can we expose insert/remove ones for collections? Doubt it... */ +}; + +static EnumPropertyItem override_type_items[] = { + {UIOverride_Type_NOOP, "NOOP", 0, "NoOp", + "'No-Operation', place holder preventing automatic override to ever affect the property"}, + {UIOverride_Type_Replace, "REPLACE", 0, "Replace", "Completely replace value from linked data by local one"}, + {UIOverride_Type_Difference, "DIFFERENCE", 0, "Difference", "Store difference to linked data value"}, + {UIOverride_Type_Factor, "FACTOR", 0, "Factor", "Store factor to linked data value (useful e.g. for scale)"}, + {0, NULL, 0, NULL, NULL} +}; + + +static bool override_type_set_button_poll(bContext *C) +{ + PointerRNA ptr; + PropertyRNA *prop; + int index; + + UI_context_active_but_prop_get(C, &ptr, &prop, &index); + + const int override_status = RNA_property_static_override_status(&ptr, prop, index); + + return (ptr.data && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDABLE)); +} + +static int override_type_set_button_exec(bContext *C, wmOperator *op) +{ + PointerRNA ptr; + PropertyRNA *prop; + int index; + bool created; + const bool all = RNA_boolean_get(op->ptr, "all"); + const int op_type = RNA_enum_get(op->ptr, "type"); + + short operation; + + switch (op_type) { + case UIOverride_Type_NOOP: + operation = IDOVERRIDESTATIC_OP_NOOP; + break; + case UIOverride_Type_Replace: + operation = IDOVERRIDESTATIC_OP_REPLACE; + break; + case UIOverride_Type_Difference: + operation = IDOVERRIDESTATIC_OP_ADD; /* override code will automatically switch to subtract if needed. */ + break; + case UIOverride_Type_Factor: + operation = IDOVERRIDESTATIC_OP_MULTIPLY; + break; + default: + operation = IDOVERRIDESTATIC_OP_REPLACE; + BLI_assert(0); + break; + } + + /* try to reset the nominated setting to its default value */ + UI_context_active_but_prop_get(C, &ptr, &prop, &index); + + BLI_assert(ptr.id.data != NULL); + + if (all) { + index = -1; + } + + IDOverrideStaticPropertyOperation *opop = RNA_property_override_property_operation_get( + &ptr, prop, operation, index, true, NULL, &created); + if (!created) { + opop->operation = operation; + } + + return operator_button_property_finish(C, &ptr, prop); +} + +static int override_type_set_button_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ +#if 0 /* Disabled for now */ + return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_DEFAULT); +#else + RNA_enum_set(op->ptr, "type", IDOVERRIDESTATIC_OP_REPLACE); + return override_type_set_button_exec(C, op); +#endif +} + +static void UI_OT_override_type_set_button(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Define Override Type"; + ot->idname = "UI_OT_override_type_set_button"; + ot->description = "Create an override operation, or set the type of an existing one"; + + /* callbacks */ + ot->poll = override_type_set_button_poll; + ot->exec = override_type_set_button_exec; + ot->invoke = override_type_set_button_invoke; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array"); + ot->prop = RNA_def_enum( + ot->srna, "type", override_type_items, UIOverride_Type_Replace, + "Type", "Type of override operation"); + /* TODO: add itemf callback, not all options are available for all data types... */ +} + + +static bool override_remove_button_poll(bContext *C) +{ + PointerRNA ptr; + PropertyRNA *prop; + int index; + + UI_context_active_but_prop_get(C, &ptr, &prop, &index); + + const int override_status = RNA_property_static_override_status(&ptr, prop, index); + + return (ptr.data && ptr.id.data && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN)); +} + +static int override_remove_button_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + PointerRNA ptr, id_refptr, src; + PropertyRNA *prop; + int index; + const bool all = RNA_boolean_get(op->ptr, "all"); + + /* try to reset the nominated setting to its default value */ + UI_context_active_but_prop_get(C, &ptr, &prop, &index); + + ID *id = ptr.id.data; + IDOverrideStaticProperty *oprop = RNA_property_override_property_find(&ptr, prop); + BLI_assert(oprop != NULL); + BLI_assert(id != NULL && id->override_static != NULL); + + const bool is_template = (id->override_static->reference == NULL); + + /* We need source (i.e. linked data) to restore values of deleted overrides... + * If this is an override template, we obviously do not need to restore anything. */ + if (!is_template) { + RNA_id_pointer_create(id->override_static->reference, &id_refptr); + if (!RNA_path_resolve(&id_refptr, oprop->rna_path, &src, NULL)) { + BLI_assert(0 && "Failed to create matching source (linked data) RNA pointer"); + } + } + + if (!all && index != -1) { + bool is_strict_find; + /* Remove override operation for given item, add singular operations for the other items as needed. */ + IDOverrideStaticPropertyOperation *opop = BKE_override_static_property_operation_find( + oprop, NULL, NULL, index, index, false, &is_strict_find); + BLI_assert(opop != NULL); + if (!is_strict_find) { + /* No specific override operation, we have to get generic one, + * and create item-specific override operations for all but given index, before removing generic one. */ + for (int idx = RNA_property_array_length(&ptr, prop); idx--; ) { + if (idx != index) { + BKE_override_static_property_operation_get(oprop, opop->operation, NULL, NULL, idx, idx, true, NULL, NULL); + } + } + } + BKE_override_static_property_operation_delete(oprop, opop); + if (!is_template) { + RNA_property_copy(bmain, &ptr, &src, prop, index); + } + if (BLI_listbase_is_empty(&oprop->operations)) { + BKE_override_static_property_delete(id->override_static, oprop); + } + } + else { + /* Just remove whole generic override operation of this property. */ + BKE_override_static_property_delete(id->override_static, oprop); + if (!is_template) { + RNA_property_copy(bmain, &ptr, &src, prop, -1); + } + } + + return operator_button_property_finish(C, &ptr, prop); +} + +static void UI_OT_override_remove_button(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Override"; + ot->idname = "UI_OT_override_remove_button"; + ot->description = "Remove an override operation"; + + /* callbacks */ + ot->poll = override_remove_button_poll; + ot->exec = override_remove_button_exec; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array"); +} + + + + /* Copy To Selected Operator ------------------------ */ bool UI_context_copy_to_selected_list( @@ -484,6 +701,7 @@ bool UI_context_copy_to_selected_list( */ static bool copy_to_selected_button(bContext *C, bool all, bool poll) { + Main *bmain = CTX_data_main(C); PointerRNA ptr, lptr, idptr; PropertyRNA *prop, *lprop; bool success = false; @@ -532,7 +750,7 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll) break; } else { - if (RNA_property_copy(&lptr, &ptr, prop, (all) ? -1 : index)) { + if (RNA_property_copy(bmain, &lptr, &ptr, prop, (all) ? -1 : index)) { RNA_property_update(C, &lptr, prop); success = true; } @@ -792,6 +1010,7 @@ static int editsource_exec(bContext *C, wmOperator *op) ui_editsource_active_but_set(but); /* redraw and get active button python info */ + ED_region_do_layout(C, ar); ED_region_do_draw(C, ar); ar->do_draw = false; @@ -1127,6 +1346,8 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_copy_python_command_button); WM_operatortype_append(UI_OT_reset_default_button); WM_operatortype_append(UI_OT_unset_property_button); + WM_operatortype_append(UI_OT_override_type_set_button); + WM_operatortype_append(UI_OT_override_remove_button); WM_operatortype_append(UI_OT_copy_to_selected_button); WM_operatortype_append(UI_OT_reports_to_textblock); /* XXX: temp? */ WM_operatortype_append(UI_OT_drop_color); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 744f6b3c4d0..59fdf7e672d 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -50,9 +50,6 @@ #include "BKE_context.h" #include "BKE_screen.h" -#include "BIF_gl.h" -#include "BIF_glutil.h" - #include "BLF_api.h" #include "WM_api.h" @@ -65,6 +62,9 @@ #include "UI_interface_icons.h" #include "UI_resources.h" +#include "GPU_immediate.h" +#include "GPU_state.h" + #include "interface_intern.h" /*********************** defines and structs ************************/ @@ -111,17 +111,23 @@ typedef struct uiHandlePanelData { int startsizex, startsizey; } uiHandlePanelData; +static int get_panel_real_size_y(const Panel *pa); static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelState state); /*********************** space specific code ************************/ /* temporary code to remove all sbuts stuff from panel code */ +/* SpaceButs.align */ +typedef enum eSpaceButtons_Align { + BUT_HORIZONTAL = 0, + BUT_VERTICAL = 1, + BUT_AUTO = 2, +} eSpaceButtons_Align; + static int panel_aligned(ScrArea *sa, ARegion *ar) { - if (sa->spacetype == SPACE_BUTS && ar->regiontype == RGN_TYPE_WINDOW) { - SpaceButs *sbuts = sa->spacedata.first; - return sbuts->align; - } + if (sa->spacetype == SPACE_BUTS && ar->regiontype == RGN_TYPE_WINDOW) + return BUT_VERTICAL; else if (sa->spacetype == SPACE_USERPREF && ar->regiontype == RGN_TYPE_WINDOW) return BUT_VERTICAL; else if (sa->spacetype == SPACE_FILE && ar->regiontype == RGN_TYPE_CHANNELS) @@ -134,45 +140,78 @@ static int panel_aligned(ScrArea *sa, ARegion *ar) return 0; } -static int panels_re_align(ScrArea *sa, ARegion *ar, Panel **r_pa) +static bool panel_active_animation_changed(ListBase *lb, Panel **pa_animation, bool *no_animation) { - Panel *pa; - int active = 0; + for (Panel *pa = lb->first; pa; pa = pa->next) { + /* Detect panel active flag changes. */ + if (!(pa->type && pa->type->parent)) { + if ((pa->runtime_flag & PNL_WAS_ACTIVE) && !(pa->runtime_flag & PNL_ACTIVE)) { + return true; + } + if (!(pa->runtime_flag & PNL_WAS_ACTIVE) && (pa->runtime_flag & PNL_ACTIVE)) { + return true; + } + } + + if ((pa->runtime_flag & PNL_ACTIVE) && !(pa->flag & PNL_CLOSED)) { + if (panel_active_animation_changed(&pa->children, pa_animation, no_animation)) { + return true; + } + } + + /* Detect animation. */ + if (pa->activedata) { + uiHandlePanelData *data = pa->activedata; + if (data->state == PANEL_STATE_ANIMATION) { + *pa_animation = pa; + } + else { + /* Don't animate while handling other interaction. */ + *no_animation = true; + } + } + if ((pa->runtime_flag & PNL_ANIM_ALIGN) && !(*pa_animation)) { + *pa_animation = pa; + } + } + + return false; +} - *r_pa = NULL; +static bool panels_need_realign(ScrArea *sa, ARegion *ar, Panel **pa_animate) +{ + *pa_animate = NULL; if (sa->spacetype == SPACE_BUTS && ar->regiontype == RGN_TYPE_WINDOW) { SpaceButs *sbuts = sa->spacedata.first; - if (sbuts->align) - if (sbuts->re_align || sbuts->mainbo != sbuts->mainb) - return 1; + if (sbuts->mainbo != sbuts->mainb) { + return true; + } + } + else if (sa->spacetype == SPACE_IMAGE && ar->regiontype == RGN_TYPE_PREVIEW) { + return true; + } + else if (sa->spacetype == SPACE_FILE && ar->regiontype == RGN_TYPE_CHANNELS) { + return true; } - else if (sa->spacetype == SPACE_IMAGE && ar->regiontype == RGN_TYPE_PREVIEW) - return 1; - else if (sa->spacetype == SPACE_FILE && ar->regiontype == RGN_TYPE_CHANNELS) - return 1; - /* in case panel is added or disappears */ - for (pa = ar->panels.first; pa; pa = pa->next) { - if ((pa->runtime_flag & PNL_WAS_ACTIVE) && !(pa->runtime_flag & PNL_ACTIVE)) - return 1; - if (!(pa->runtime_flag & PNL_WAS_ACTIVE) && (pa->runtime_flag & PNL_ACTIVE)) - return 1; - if (pa->activedata) - active = 1; + /* Detect if a panel was added or removed. */ + Panel *pa_animation = NULL; + bool no_animation = false; + if (panel_active_animation_changed(&ar->panels, &pa_animation, &no_animation)) { + return true; } - /* in case we need to do an animation (size changes) */ - for (pa = ar->panels.first; pa; pa = pa->next) { - if (pa->runtime_flag & PNL_ANIM_ALIGN) { - if (!active) - *r_pa = pa; - return 1; + /* Detect panel marked for animation, if we're not already animating. */ + if (pa_animation) { + if (!no_animation) { + *pa_animate = pa_animation; } + return true; } - return 0; + return false; } /****************************** panels ******************************/ @@ -216,14 +255,14 @@ static void ui_panel_copy_offset(Panel *pa, Panel *papar) */ /* #define UI_USE_PANELTAB */ -Panel *UI_panel_find_by_type(ARegion *ar, PanelType *pt) +Panel *UI_panel_find_by_type(ListBase *lb, PanelType *pt) { Panel *pa; const char *idname = pt->idname; #ifdef UI_USE_PANELTAB const char *tabname = pt->idname; - for (pa = ar->panels.first; pa; pa = pa->next) { + for (pa = lb->first; pa; pa = pa->next) { if (STREQLEN(pa->panelname, idname, sizeof(pa->panelname))) { if (STREQLEN(pa->tabname, tabname, sizeof(pa->tabname))) { return pa; @@ -231,7 +270,7 @@ Panel *UI_panel_find_by_type(ARegion *ar, PanelType *pt) } } #else - for (pa = ar->panels.first; pa; pa = pa->next) { + for (pa = lb->first; pa; pa = pa->next) { if (STREQLEN(pa->panelname, idname, sizeof(pa->panelname))) { return pa; } @@ -244,7 +283,7 @@ Panel *UI_panel_find_by_type(ARegion *ar, PanelType *pt) /** * \note \a pa should be return value from #UI_panel_find_by_type and can be NULL. */ -Panel *UI_panel_begin(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, Panel *pa, bool *r_open) +Panel *UI_panel_begin(ScrArea *sa, ARegion *ar, ListBase *lb, uiBlock *block, PanelType *pt, Panel *pa, bool *r_open) { Panel *palast, *panext; const char *drawname = CTX_IFACE_(pt->translation_context, pt->label); @@ -276,9 +315,11 @@ Panel *UI_panel_begin(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, P pa->ofsy = 0; pa->sizex = 0; pa->sizey = 0; + pa->blocksizex = 0; + pa->blocksizey = 0; pa->runtime_flag |= PNL_NEW_ADDED; - BLI_addtail(&ar->panels, pa); + BLI_addtail(lb, pa); #ifdef UI_USE_PANELTAB BLI_strncpy(pa->tabname, tabname, sizeof(pa->tabname)); @@ -286,7 +327,7 @@ Panel *UI_panel_begin(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, P /* make new Panel tabbed? */ if (hookname) { Panel *patab; - for (patab = ar->panels.first; patab; patab = patab->next) { + for (patab = lb->first; patab; patab = patab->next) { if ((patab->runtime_flag & PNL_ACTIVE) && patab->paneltab == NULL) { if (STREQLEN(hookname, patab->panelname, sizeof(patab->panelname))) { if (STREQLEN(tabname, patab->tabname, sizeof(patab->tabname))) { @@ -309,6 +350,8 @@ Panel *UI_panel_begin(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, P /* Force update of panels' positions! */ pa->sizex = 0; pa->sizey = 0; + pa->blocksizex = 0; + pa->blocksizey = 0; } BLI_strncpy(pa->drawname, drawname, sizeof(pa->drawname)); @@ -316,14 +359,18 @@ Panel *UI_panel_begin(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, P /* if a new panel is added, we insert it right after the panel * that was last added. this way new panels are inserted in the * right place between versions */ - for (palast = ar->panels.first; palast; palast = palast->next) - if (palast->runtime_flag & PNL_LAST_ADDED) + for (palast = lb->first; palast; palast = palast->next) { + if (palast->runtime_flag & PNL_LAST_ADDED) { + BLI_remlink(lb, pa); + BLI_insertlinkafter(lb, palast, pa); break; + } + } if (newpanel) { pa->sortorder = (palast) ? palast->sortorder + 1 : 0; - for (panext = ar->panels.first; panext; panext = panext->next) + for (panext = lb->first; panext; panext = panext->next) if (panext != pa && panext->sortorder >= pa->sortorder) panext->sortorder++; } @@ -349,6 +396,19 @@ void UI_panel_end(uiBlock *block, int width, int height) { Panel *pa = block->panel; + /* Set panel size excluding children. */ + pa->blocksizex = width; + pa->blocksizey = height; + + /* Compute total panel size including children. */ + for (Panel *pachild = pa->children.first; pachild; pachild = pachild->next) { + if (pachild->runtime_flag & PNL_ACTIVE) { + width = max_ii(width, pachild->sizex); + height += get_panel_real_size_y(pachild); + } + } + + /* Update total panel size. */ if (pa->runtime_flag & PNL_NEW_ADDED) { pa->runtime_flag &= ~PNL_NEW_ADDED; pa->sizex = width; @@ -373,15 +433,13 @@ void UI_panel_end(uiBlock *block, int width, int height) static void ui_offset_panel_block(uiBlock *block) { uiStyle *style = UI_style_get_dpi(); - uiBut *but; - int ofsy; /* compute bounds and offset */ ui_block_bounds_calc(block); - ofsy = block->panel->sizey - style->panelspace; + int ofsy = block->panel->sizey - style->panelspace; - for (but = block->buttons.first; but; but = but->next) { + for (uiBut *but = block->buttons.first; but; but = but->next) { but->rect.ymin += ofsy; but->rect.ymax += ofsy; } @@ -393,81 +451,59 @@ static void ui_offset_panel_block(uiBlock *block) /**************************** drawing *******************************/ -/* extern used by previewrender */ -#if 0 /*UNUSED 2.5*/ -static void uiPanelPush(uiBlock *block) -{ - glPushMatrix(); - - if (block->panel) - glTranslatef((float)block->panel->ofsx, (float)block->panel->ofsy, 0.0); -} - -static void uiPanelPop(uiBlock *UNUSED(block)) -{ - glPopMatrix(); -} -#endif - /* triangle 'icon' for panel header */ -void UI_draw_icon_tri(float x, float y, char dir) +void UI_draw_icon_tri(float x, float y, char dir, const float color[4]) { float f3 = 0.15 * U.widget_unit; float f5 = 0.25 * U.widget_unit; float f7 = 0.35 * U.widget_unit; if (dir == 'h') { - ui_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y); + UI_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color); } else if (dir == 't') { - ui_draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3); + UI_draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3, color); } else { /* 'v' = vertical, down */ - ui_draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7); - } -} - -/* triangle 'icon' inside rect */ -static void ui_draw_tria_rect(const rctf *rect, char dir) -{ - if (dir == 'h') { - float half = 0.5f * BLI_rctf_size_y(rect); - ui_draw_anti_tria(rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half); - } - else { - float half = 0.5f * BLI_rctf_size_x(rect); - ui_draw_anti_tria(rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin); + UI_draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7, color); } } -static void ui_draw_anti_x(float x1, float y1, float x2, float y2) +static void ui_draw_anti_x(unsigned int pos, float x1, float y1, float x2, float y2) { /* set antialias line */ - glEnable(GL_LINE_SMOOTH); - glEnable(GL_BLEND); + GPU_line_smooth(true); + GPU_blend(true); + + GPU_line_width(2.0); + + immBegin(GPU_PRIM_LINES, 4); - glLineWidth(2.0); + immVertex2f(pos, x1, y1); + immVertex2f(pos, x2, y2); - fdrawline(x1, y1, x2, y2); - fdrawline(x1, y2, x2, y1); + immVertex2f(pos, x1, y2); + immVertex2f(pos, x2, y1); - glDisable(GL_LINE_SMOOTH); - glDisable(GL_BLEND); + immEnd(); + + GPU_line_smooth(false); + GPU_blend(false); } /* x 'icon' for panel header */ -static void ui_draw_x_icon(float x, float y) +static void ui_draw_x_icon(unsigned int pos, float x, float y) { - ui_draw_anti_x(x, y, x + 9.375f, y + 9.375f); + ui_draw_anti_x(pos, x, y, x + 9.375f, y + 9.375f); } #define PNL_ICON UI_UNIT_X /* could be UI_UNIT_Y too */ -static void ui_draw_panel_scalewidget(const rcti *rect) +static void ui_draw_panel_scalewidget(unsigned int pos, const rcti *rect) { float xmin, xmax, dx; float ymin, ymax, dy; @@ -480,19 +516,56 @@ static void ui_draw_panel_scalewidget(const rcti *rect) dx = 0.5f * (xmax - xmin); dy = 0.5f * (ymax - ymin); - glEnable(GL_BLEND); - glColor4ub(255, 255, 255, 50); - fdrawline(xmin, ymin, xmax, ymax); - fdrawline(xmin + dx, ymin, xmax, ymax - dy); + GPU_blend(true); + immUniformColor4ub(255, 255, 255, 50); + + immBegin(GPU_PRIM_LINES, 4); + + immVertex2f(pos, xmin, ymin); + immVertex2f(pos, xmax, ymax); + + immVertex2f(pos, xmin + dx, ymin); + immVertex2f(pos, xmax, ymax - dy); + + immEnd(); + + immUniformColor4ub(0, 0, 0, 50); + + immBegin(GPU_PRIM_LINES, 4); + + immVertex2f(pos, xmin, ymin + 1); + immVertex2f(pos, xmax, ymax + 1); - glColor4ub(0, 0, 0, 50); - fdrawline(xmin, ymin + 1, xmax, ymax + 1); - fdrawline(xmin + dx, ymin + 1, xmax, ymax - dy + 1); - glDisable(GL_BLEND); + immVertex2f(pos, xmin + dx, ymin + 1); + immVertex2f(pos, xmax, ymax - dy + 1); + + immEnd(); + + GPU_blend(false); +} + +static void immRectf_tris_color_ex( + unsigned int pos, float x1, float y1, float x2, float y2, + unsigned int col, const float color[3]) +{ + immAttrib4fv(col, color); + immVertex2f(pos, x1, y1); + immAttrib4fv(col, color); + immVertex2f(pos, x2, y1); + immAttrib4fv(col, color); + immVertex2f(pos, x2, y2); + + immAttrib4fv(col, color); + immVertex2f(pos, x1, y1); + immAttrib4fv(col, color); + immVertex2f(pos, x2, y2); + immAttrib4fv(col, color); + immVertex2f(pos, x1, y2); } -static void ui_draw_panel_dragwidget(const rctf *rect) + +static void ui_draw_panel_dragwidget(unsigned int pos, unsigned int col, const rctf *rect) { - unsigned char col_back[3], col_high[3], col_dark[3]; + float col_high[4], col_dark[4]; const int col_tint = 84; const int px = (int)U.pixelsize; @@ -507,26 +580,41 @@ static void ui_draw_panel_dragwidget(const rctf *rect) const int x_ofs = y_ofs; int i_x, i_y; - - UI_GetThemeColor3ubv(UI_GetThemeValue(TH_PANEL_SHOW_HEADER) ? TH_PANEL_HEADER : TH_PANEL_BACK, col_back); - UI_GetColorPtrShade3ubv(col_back, col_high, col_tint); - UI_GetColorPtrShade3ubv(col_back, col_dark, -col_tint); - + UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high); + UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark); /* draw multiple boxes */ + immBegin(GPU_PRIM_TRIS, 4 * 2 * (6 * 2)); for (i_x = 0; i_x < 4; i_x++) { for (i_y = 0; i_y < 2; i_y++) { const int x_co = (x_min + x_ofs) + (i_x * (box_size + box_margin)); const int y_co = (y_min + y_ofs) + (i_y * (box_size + box_margin)); - glColor3ubv(col_dark); - glRectf(x_co - box_size, y_co - px_zoom, x_co, (y_co + box_size) - px_zoom); - glColor3ubv(col_high); - glRectf(x_co - box_size, y_co, x_co, y_co + box_size); + immRectf_tris_color_ex( + pos, x_co - box_size, y_co - px_zoom, x_co, (y_co + box_size) - px_zoom, + col, col_dark); + immRectf_tris_color_ex( + pos, x_co - box_size, y_co, x_co, y_co + box_size, + col, col_high); } } + immEnd(); } +/* For button layout next to label. */ +void UI_panel_label_offset(uiBlock *block, int *x, int *y) +{ + Panel *panel = block->panel; + uiStyle *style = UI_style_get_dpi(); + const bool is_subpanel = (panel->type && panel->type->parent); + + *x = UI_UNIT_X * 1.1f; + *y = (UI_UNIT_Y * 1.1f) + style->panelspace; + + if (is_subpanel) { + *x += 5.0f / block->aspect; + } +} static void ui_draw_aligned_panel_header(uiStyle *style, uiBlock *block, const rcti *rect, char dir) { @@ -534,6 +622,9 @@ static void ui_draw_aligned_panel_header(uiStyle *style, uiBlock *block, const r rcti hrect; int pnl_icons; const char *activename = panel->drawname[0] ? panel->drawname : panel->panelname; + const bool is_subpanel = (panel->type && panel->type->parent); + uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle; + unsigned char col_title[4]; /* + 0.001f to avoid flirting with float inaccuracy */ if (panel->control & UI_PNL_CLOSE) @@ -541,22 +632,22 @@ static void ui_draw_aligned_panel_header(uiStyle *style, uiBlock *block, const r else pnl_icons = (panel->labelofs + PNL_ICON + 5) / block->aspect + 0.001f; - /* active tab */ /* draw text label */ - UI_ThemeColor(TH_TITLE); + UI_GetThemeColor3ubv(TH_TITLE, col_title); + col_title[3] = 255; hrect = *rect; if (dir == 'h') { hrect.xmin = rect->xmin + pnl_icons; hrect.ymin += 2.0f / block->aspect; - UI_fontstyle_draw(&style->paneltitle, &hrect, activename); + UI_fontstyle_draw(fontstyle, &hrect, activename, col_title); } else { /* ignore 'pnl_icons', otherwise the text gets offset horizontally * + 0.001f to avoid flirting with float inaccuracy */ hrect.xmin = rect->xmin + (PNL_ICON + 5) / block->aspect + 0.001f; - UI_fontstyle_draw_rotated(&style->paneltitle, &hrect, activename); + UI_fontstyle_draw_rotated(fontstyle, &hrect, activename, col_title); } } @@ -566,9 +657,11 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con Panel *panel = block->panel; rcti headrect; rctf itemrect; - int ofsx; + float color[4]; const bool is_closed_x = (panel->flag & PNL_CLOSEDX) ? true : false; const bool is_closed_y = (panel->flag & PNL_CLOSEDY) ? true : false; + const bool is_subpanel = (panel->type && panel->type->parent); + const bool show_drag = !is_subpanel; if (panel->paneltab) return; if (panel->type && (panel->type->flag & PNL_NO_HEADER)) return; @@ -579,38 +672,40 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con headrect.ymin = headrect.ymax; headrect.ymax = headrect.ymin + floor(PNL_HEADER / block->aspect + 0.001f); - { + rcti titlerect = headrect; + if (is_subpanel) { + titlerect.xmin += 5.0f / block->aspect; + } + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + if (!is_subpanel) { float minx = rect->xmin; float maxx = is_closed_x ? (minx + PNL_HEADER / block->aspect) : rect->xmax; float y = headrect.ymax; - glEnable(GL_BLEND); + GPU_blend(true); - if (UI_GetThemeValue(TH_PANEL_SHOW_HEADER)) { - /* draw with background color */ - UI_ThemeColor4(TH_PANEL_HEADER); - glRectf(minx, headrect.ymin + 1, maxx, y); + /* draw with background color */ + immUniformThemeColor(TH_PANEL_HEADER); + immRectf(pos, minx, headrect.ymin, maxx, y); - fdrawline(minx, y, maxx, y); - fdrawline(minx, y, maxx, y); - } - else if (!(panel->runtime_flag & PNL_FIRST)) { - /* draw embossed separator */ + immBegin(GPU_PRIM_LINES, 4); - if (is_closed_x == false) { - minx += 5.0f / block->aspect; - maxx -= 5.0f / block->aspect; - } + immVertex2f(pos, minx, y); + immVertex2f(pos, maxx, y); - glColor4f(0.0f, 0.0f, 0.0f, 0.5f); - fdrawline(minx, y, maxx, y); - glColor4f(1.0f, 1.0f, 1.0f, 0.25f); - fdrawline(minx, y - 1, maxx, y - 1); - } + immVertex2f(pos, minx, y); + immVertex2f(pos, maxx, y); + + immEnd(); - glDisable(GL_BLEND); + GPU_blend(false); } + immUnbindProgram(); + /* draw optional pin icon */ #ifdef USE_PIN_HIDDEN @@ -619,26 +714,39 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con if (show_pin) #endif { - glEnable(GL_BLEND); + GPU_blend(true); UI_icon_draw_aspect( headrect.xmax - ((PNL_ICON * 2.2f) / block->aspect), headrect.ymin + (5.0f / block->aspect), (panel->flag & PNL_PIN) ? ICON_PINNED : ICON_UNPINNED, (block->aspect / UI_DPI_FAC), 1.0f); - glDisable(GL_BLEND); + GPU_blend(false); } + /* horizontal title */ if (is_closed_x == false) { - ui_draw_aligned_panel_header(style, block, &headrect, 'h'); - - /* itemrect smaller */ - itemrect.xmax = headrect.xmax - 5.0f / block->aspect; - itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect); - itemrect.ymin = headrect.ymin; - itemrect.ymax = headrect.ymax; - - BLI_rctf_scale(&itemrect, 0.7f); - ui_draw_panel_dragwidget(&itemrect); + ui_draw_aligned_panel_header(style, block, &titlerect, 'h'); + + if (show_drag) { + uint col; + GPUVertFormat *format = immVertexFormat(); + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + /* itemrect smaller */ + itemrect.xmax = headrect.xmax - 5.0f / block->aspect; + itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect); + itemrect.ymin = headrect.ymin; + itemrect.ymax = headrect.ymax; + + BLI_rctf_scale(&itemrect, 0.7f); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + ui_draw_panel_dragwidget(pos, col, &itemrect); + immUnbindProgram(); + + /* Restore format for the following draws. */ + pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + } } /* if the panel is minimized vertically: @@ -650,6 +758,7 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con else if (is_closed_x) { /* draw vertical title */ ui_draw_aligned_panel_header(style, block, &headrect, 'v'); + pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); } /* an open panel */ else { @@ -658,55 +767,63 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con if (panel->control & UI_PNL_SOLID) UI_draw_roundbox_corner_set(UI_CNR_ALL); else UI_draw_roundbox_corner_set(UI_CNR_NONE); - UI_ThemeColorShade(TH_BACK, -120); - UI_draw_roundbox_unfilled(0.5f + rect->xmin, 0.5f + rect->ymin, 0.5f + rect->xmax, 0.5f + headrect.ymax + 1, 8); + UI_GetThemeColorShade4fv(TH_BACK, -120, color); + UI_draw_roundbox_aa(false, 0.5f + rect->xmin, 0.5f + rect->ymin, 0.5f + rect->xmax, 0.5f + headrect.ymax + 1, 8, color); } + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + GPU_blend(true); + /* panel backdrop */ - if (UI_GetThemeValue(TH_PANEL_SHOW_BACK)) { - /* draw with background color */ - glEnable(GL_BLEND); - UI_ThemeColor4(TH_PANEL_BACK); - glRecti(rect->xmin, rect->ymin, rect->xmax, rect->ymax); - } + int panel_col = is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK; + + immUniformThemeColor(panel_col); + immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); if (panel->control & UI_PNL_SCALE) - ui_draw_panel_scalewidget(rect); + ui_draw_panel_scalewidget(pos, rect); + + immUnbindProgram(); } /* draw optional close icon */ - ofsx = 6; if (panel->control & UI_PNL_CLOSE) { - UI_ThemeColor(TH_TITLE); - ui_draw_x_icon(rect->xmin + 2 + ofsx, rect->ymax + 2); - ofsx = 22; + const int ofsx = 6; + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColor3(TH_TITLE); + ui_draw_x_icon(pos, rect->xmin + 2 + ofsx, rect->ymax + 2); + immUnbindProgram(); } /* draw collapse icon */ - UI_ThemeColor(TH_TITLE); /* itemrect smaller */ - itemrect.xmin = headrect.xmin + 5.0f / block->aspect; - itemrect.xmax = itemrect.xmin + BLI_rcti_size_y(&headrect); - itemrect.ymin = headrect.ymin; - itemrect.ymax = headrect.ymax; - - BLI_rctf_scale(&itemrect, 0.35f); + itemrect.xmin = titlerect.xmin + 3.0f / block->aspect; + itemrect.xmax = itemrect.xmin + BLI_rcti_size_y(&titlerect); + itemrect.ymin = titlerect.ymin; + itemrect.ymax = titlerect.ymax; - if (is_closed_y) - ui_draw_tria_rect(&itemrect, 'h'); - else if (is_closed_x) - ui_draw_tria_rect(&itemrect, 'h'); - else - ui_draw_tria_rect(&itemrect, 'v'); + BLI_rctf_scale(&itemrect, 0.25f); - (void)ofsx; + { + float tria_color[4]; + UI_GetThemeColor3fv(TH_TITLE, tria_color); + tria_color[3] = 1.0f; + + if (is_closed_y) + ui_draw_anti_tria_rect(&itemrect, 'h', tria_color); + else if (is_closed_x) + ui_draw_anti_tria_rect(&itemrect, 'h', tria_color); + else + ui_draw_anti_tria_rect(&itemrect, 'v', tria_color); + } } /************************** panel alignment *************************/ -static int get_panel_header(Panel *pa) +static int get_panel_header(const Panel *pa) { if (pa->type && (pa->type->flag & PNL_NO_HEADER)) return 0; @@ -714,7 +831,7 @@ static int get_panel_header(Panel *pa) return PNL_HEADER; } -static int get_panel_size_y(Panel *pa) +static int get_panel_size_y(const Panel *pa) { if (pa->type && (pa->type->flag & PNL_NO_HEADER)) return pa->sizey; @@ -722,6 +839,21 @@ static int get_panel_size_y(Panel *pa) return PNL_HEADER + pa->sizey; } +static int get_panel_real_size_y(const Panel *pa) +{ + int sizey = (pa->flag & PNL_CLOSED) ? 0 : pa->sizey; + + if (pa->type && (pa->type->flag & PNL_NO_HEADER)) + return sizey; + + return PNL_HEADER + sizey; +} + +int UI_panel_size_y(const Panel *pa) +{ + return get_panel_real_size_y(pa); +} + /* this function is needed because uiBlock and Panel itself don't * change sizey or location when closed */ static int get_panel_real_ofsy(Panel *pa) @@ -794,6 +926,24 @@ static int compare_panel(const void *a1, const void *a2) return 0; } +static void align_sub_panels(Panel *pa) +{ + /* Position sub panels. */ + int ofsy = get_panel_real_ofsy(pa) + pa->sizey - pa->blocksizey; + + for (Panel *pachild = pa->children.first; pachild; pachild = pachild->next) { + if (pachild->runtime_flag & PNL_ACTIVE) { + pachild->ofsx = pa->ofsx; + pachild->ofsy = ofsy - get_panel_size_y(pachild); + ofsy -= get_panel_real_size_y(pachild); + + if (pachild->children.first) { + align_sub_panels(pachild); + } + } + } +} + /* this doesnt draw */ /* returns 1 when it did something */ static bool uiAlignPanelStep(ScrArea *sa, ARegion *ar, const float fac, const bool drag) @@ -885,10 +1035,17 @@ static bool uiAlignPanelStep(ScrArea *sa, ARegion *ar, const float fac, const bo } } - /* copy locations to tabs */ - for (pa = ar->panels.first; pa; pa = pa->next) - if (pa->paneltab && (pa->runtime_flag & PNL_ACTIVE)) - ui_panel_copy_offset(pa, pa->paneltab); + /* set locations for tabbed and sub panels */ + for (pa = ar->panels.first; pa; pa = pa->next) { + if (pa->runtime_flag & PNL_ACTIVE) { + if (pa->paneltab) { + ui_panel_copy_offset(pa, pa->paneltab); + } + if (pa->children.first) { + align_sub_panels(pa); + } + } + } /* free panelsort array */ for (ps = panelsort, a = 0; a < tot; a++, ps++) { @@ -958,20 +1115,27 @@ static void ui_do_animate(const bContext *C, Panel *panel) } } -void UI_panels_begin(const bContext *UNUSED(C), ARegion *ar) +static void panel_list_clear_active(ListBase *lb) { - Panel *pa; - /* set all panels as inactive, so that at the end we know * which ones were used */ - for (pa = ar->panels.first; pa; pa = pa->next) { - if (pa->runtime_flag & PNL_ACTIVE) + for (Panel *pa = lb->first; pa; pa = pa->next) { + if (pa->runtime_flag & PNL_ACTIVE) { pa->runtime_flag = PNL_WAS_ACTIVE; - else + } + else { pa->runtime_flag = 0; + } + + panel_list_clear_active(&pa->children); } } +void UI_panels_begin(const bContext *UNUSED(C), ARegion *ar) +{ + panel_list_clear_active(&ar->panels); +} + /* only draws blocks with panels */ void UI_panels_end(const bContext *C, ARegion *ar, int *x, int *y) { @@ -1010,8 +1174,7 @@ void UI_panels_end(const bContext *C, ARegion *ar, int *x, int *y) } /* re-align, possibly with animation */ - if (panels_re_align(sa, ar, &pa)) { - /* XXX code never gets here... PNL_ANIM_ALIGN flag is never set */ + if (panels_need_realign(sa, ar, &pa)) { if (pa) panel_activate_state(C, pa, PANEL_STATE_ANIMATION); else @@ -1038,14 +1201,16 @@ void UI_panels_draw(const bContext *C, ARegion *ar) UI_ThemeClearColor(TH_BACK); - /* draw panels, selected on top */ - for (block = ar->uiblocks.first; block; block = block->next) { + /* Draw panels, selected on top. Also in reverse order, because + * UI blocks are added in reverse order and we need child panels + * to draw on top. */ + for (block = ar->uiblocks.last; block; block = block->prev) { if (block->active && block->panel && !(block->panel->flag & PNL_SELECT)) { UI_block_draw(C, block); } } - for (block = ar->uiblocks.first; block; block = block->next) { + for (block = ar->uiblocks.last; block; block = block->prev) { if (block->active && block->panel && (block->panel->flag & PNL_SELECT)) { UI_block_draw(C, block); } @@ -1307,6 +1472,8 @@ static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, in #else const bool show_pin = UI_panel_category_is_visible(ar); #endif + const bool is_subpanel = (block->panel->type && block->panel->type->parent); + const bool show_drag = !is_subpanel; int align = panel_aligned(sa, ar), button = 0; @@ -1401,7 +1568,11 @@ static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, in else ED_region_tag_redraw(ar); } - else if (BLI_rctf_isect_x(&rect_drag, mx)) { + else if (show_drag && BLI_rctf_isect_x(&rect_drag, mx)) { + /* XXX, for now don't allow dragging in floating windows yet. */ + if (ar->alignment == RGN_ALIGN_FLOAT) { + return; + } panel_activate_state(C, block->panel, PANEL_STATE_DRAG); } else if (show_pin && BLI_rctf_isect_x(&rect_pin, mx)) { @@ -1514,12 +1685,13 @@ void UI_panel_category_clear_all(ARegion *ar) BLI_freelistN(&ar->panels_category); } -/* based on UI_draw_roundbox_gl_mode, check on making a version which allows us to skip some sides */ +/* based on UI_draw_roundbox, check on making a version which allows us to skip some sides */ static void ui_panel_category_draw_tab( - int mode, float minx, float miny, float maxx, float maxy, float rad, + bool filled, float minx, float miny, float maxx, float maxy, float rad, int roundboxtype, - const bool use_highlight, const bool use_shadow, - const unsigned char highlight_fade[3]) + bool use_highlight, bool use_shadow, + const unsigned char highlight_fade[3], + const unsigned char col[3]) { float vec[4][2] = { {0.195, 0.02}, @@ -1528,74 +1700,113 @@ static void ui_panel_category_draw_tab( {0.98, 0.805}}; int a; + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); + /* mult */ for (a = 0; a < 4; a++) { mul_v2_fl(vec[a], rad); } - glBegin(mode); + uint vert_len = 0; + if (use_highlight) { + vert_len += (roundboxtype & UI_CNR_TOP_RIGHT) ? 6 : 1; + vert_len += (roundboxtype & UI_CNR_TOP_LEFT) ? 6 : 1; + } + if (use_highlight && !use_shadow) { + vert_len++; + } + else { + vert_len += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 6 : 1; + vert_len += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 6 : 1; + } + + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); + + immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_STRIP, vert_len); /* start with corner right-top */ if (use_highlight) { if (roundboxtype & UI_CNR_TOP_RIGHT) { - glVertex2f(maxx, maxy - rad); + immAttrib3ubv(color, col); + immVertex2f(pos, maxx, maxy - rad); for (a = 0; a < 4; a++) { - glVertex2f(maxx - vec[a][1], maxy - rad + vec[a][0]); + immAttrib3ubv(color, col); + immVertex2f(pos, maxx - vec[a][1], maxy - rad + vec[a][0]); } - glVertex2f(maxx - rad, maxy); + immAttrib3ubv(color, col); + immVertex2f(pos, maxx - rad, maxy); } else { - glVertex2f(maxx, maxy); + immAttrib3ubv(color, col); + immVertex2f(pos, maxx, maxy); } /* corner left-top */ if (roundboxtype & UI_CNR_TOP_LEFT) { - glVertex2f(minx + rad, maxy); + immAttrib3ubv(color, col); + immVertex2f(pos, minx + rad, maxy); for (a = 0; a < 4; a++) { - glVertex2f(minx + rad - vec[a][0], maxy - vec[a][1]); + immAttrib3ubv(color, col); + immVertex2f(pos, minx + rad - vec[a][0], maxy - vec[a][1]); } - glVertex2f(minx, maxy - rad); + immAttrib3ubv(color, col); + immVertex2f(pos, minx, maxy - rad); } else { - glVertex2f(minx, maxy); + immAttrib3ubv(color, col); + immVertex2f(pos, minx, maxy); } } if (use_highlight && !use_shadow) { if (highlight_fade) { - glColor3ubv(highlight_fade); + immAttrib3ubv(color, highlight_fade); + } + else { + immAttrib3ubv(color, col); } - glVertex2f(minx, miny + rad); - glEnd(); + immVertex2f(pos, minx, miny + rad); + immEnd(); + immUnbindProgram(); return; } /* corner left-bottom */ if (roundboxtype & UI_CNR_BOTTOM_LEFT) { - glVertex2f(minx, miny + rad); + immAttrib3ubv(color, col); + immVertex2f(pos, minx, miny + rad); for (a = 0; a < 4; a++) { - glVertex2f(minx + vec[a][1], miny + rad - vec[a][0]); + immAttrib3ubv(color, col); + immVertex2f(pos, minx + vec[a][1], miny + rad - vec[a][0]); } - glVertex2f(minx + rad, miny); + immAttrib3ubv(color, col); + immVertex2f(pos, minx + rad, miny); } else { - glVertex2f(minx, miny); + immAttrib3ubv(color, col); + immVertex2f(pos, minx, miny); } /* corner right-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { - glVertex2f(maxx - rad, miny); + immAttrib3ubv(color, col); + immVertex2f(pos, maxx - rad, miny); for (a = 0; a < 4; a++) { - glVertex2f(maxx - rad + vec[a][0], miny + vec[a][1]); + immAttrib3ubv(color, col); + immVertex2f(pos, maxx - rad + vec[a][0], miny + vec[a][1]); } - glVertex2f(maxx, miny + rad); + immAttrib3ubv(color, col); + immVertex2f(pos, maxx, miny + rad); } else { - glVertex2f(maxx, miny); + immAttrib3ubv(color, col); + immVertex2f(pos, maxx, miny); } - glEnd(); + immEnd(); + immUnbindProgram(); } @@ -1716,23 +1927,28 @@ void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active) /* begin drawing */ - glEnable(GL_LINE_SMOOTH); + GPU_line_smooth(true); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* draw the background */ if (is_alpha) { - glEnable(GL_BLEND); - glColor4ubv(theme_col_tab_bg); + GPU_blend(true); + immUniformColor4ubv(theme_col_tab_bg); } else { - glColor3ubv(theme_col_tab_bg); + immUniformColor3ubv(theme_col_tab_bg); } - glRecti(v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax); + immRecti(pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax); if (is_alpha) { - glDisable(GL_BLEND); + GPU_blend(false); } + immUnbindProgram(); + for (pc_dyn = ar->panels_category.first; pc_dyn; pc_dyn = pc_dyn->next) { const rcti *rct = &pc_dyn->rect; const char *category_id = pc_dyn->idname; @@ -1749,37 +1965,42 @@ void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active) } #endif - glEnable(GL_BLEND); + GPU_blend(true); #ifdef USE_FLAT_INACTIVE if (is_active) #endif { - glColor3ubv(is_active ? theme_col_tab_active : theme_col_tab_inactive); ui_panel_category_draw_tab( - GL_POLYGON, rct->xmin, rct->ymin, rct->xmax, rct->ymax, - tab_curve_radius - px, roundboxtype, true, true, NULL); + true, rct->xmin, rct->ymin, rct->xmax, rct->ymax, + tab_curve_radius - px, roundboxtype, true, true, NULL, + is_active ? theme_col_tab_active : theme_col_tab_inactive); /* tab outline */ - glColor3ubv(theme_col_tab_outline); ui_panel_category_draw_tab( - GL_LINE_STRIP, rct->xmin - px, rct->ymin - px, rct->xmax - px, rct->ymax + px, - tab_curve_radius, roundboxtype, true, true, NULL); + false, rct->xmin - px, rct->ymin - px, rct->xmax - px, rct->ymax + px, + tab_curve_radius, roundboxtype, true, true, NULL, theme_col_tab_outline); + /* tab highlight (3d look) */ - glColor3ubv(is_active ? theme_col_tab_highlight : theme_col_tab_highlight_inactive); ui_panel_category_draw_tab( - GL_LINE_STRIP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, + false, rct->xmin, rct->ymin, rct->xmax, rct->ymax, tab_curve_radius, roundboxtype, true, false, - is_active ? theme_col_back : theme_col_tab_inactive); + is_active ? theme_col_back : theme_col_tab_inactive, + is_active ? theme_col_tab_highlight : theme_col_tab_highlight_inactive); } /* tab blackline */ if (!is_active) { - glColor3ubv(theme_col_tab_divider); - glRecti(v2d->mask.xmin + category_tabs_width - px, - rct->ymin - tab_v_pad, - v2d->mask.xmin + category_tabs_width, - rct->ymax + tab_v_pad); + pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, v2d->mask.xmin + category_tabs_width - px, + rct->ymin - tab_v_pad, + v2d->mask.xmin + category_tabs_width, + rct->ymax + tab_v_pad); + + immUnbindProgram(); } if (do_scaletabs) { @@ -1793,47 +2014,50 @@ void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active) /* tab titles */ /* draw white shadow to give text more depth */ - glColor3ubv(theme_col_text); + BLF_color3ubv(fontid, theme_col_text); /* main tab title */ BLF_draw(fontid, category_id_draw, category_draw_len); - glDisable(GL_BLEND); + GPU_blend(false); /* tab blackline remaining (last tab) */ + pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); if (pc_dyn->prev == NULL) { - glColor3ubv(theme_col_tab_divider); - glRecti(v2d->mask.xmin + category_tabs_width - px, - rct->ymax + px, - v2d->mask.xmin + category_tabs_width, - v2d->mask.ymax); + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, v2d->mask.xmin + category_tabs_width - px, + rct->ymax + px, + v2d->mask.xmin + category_tabs_width, + v2d->mask.ymax); } if (pc_dyn->next == NULL) { - glColor3ubv(theme_col_tab_divider); - glRecti(v2d->mask.xmin + category_tabs_width - px, - 0, - v2d->mask.xmin + category_tabs_width, - rct->ymin); + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, v2d->mask.xmin + category_tabs_width - px, + 0, + v2d->mask.xmin + category_tabs_width, + rct->ymin); } #ifdef USE_FLAT_INACTIVE /* draw line between inactive tabs */ if (is_active == false && is_active_prev == false && pc_dyn->prev) { - glColor3ubv(theme_col_tab_divider); - glRecti(v2d->mask.xmin + (category_tabs_width / 5), - rct->ymax + px, - (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5), - rct->ymax + (px * 3)); + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, v2d->mask.xmin + (category_tabs_width / 5), + rct->ymax + px, + (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5), + rct->ymax + (px * 3)); } is_active_prev = is_active; #endif + immUnbindProgram(); /* not essential, but allows events to be handled right up until the region edge [#38171] */ pc_dyn->rect.xmin = v2d->mask.xmin; } - glDisable(GL_LINE_SMOOTH); + GPU_line_smooth(false); BLF_disable(fontid, BLF_ROTATION); @@ -1904,6 +2128,11 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar, cons retval = WM_UI_HANDLER_CONTINUE; + /* Scrollbars can overlap panels now, they have handling priority. */ + if (UI_view2d_mouse_in_scrollers(ar, &ar->v2d, event->x, event->y)) { + return retval; + } + /* handle category tabs */ if (has_category_tabs) { if (event->val == KM_PRESS) { @@ -2153,3 +2382,15 @@ static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelStat ED_region_tag_redraw(ar); } + +PanelType *UI_paneltype_find(int space_id, int region_id, const char *idname) +{ + SpaceType *st = BKE_spacetype_from_id(space_id); + if (st) { + ARegionType *art = BKE_regiontype_from_id(st, region_id); + if (art) { + return BLI_findstring(&art->paneltypes, idname, offsetof(PanelType, idname)); + } + } + return NULL; +} diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index f7dbb9b14ed..66f63fef82d 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -32,6 +32,9 @@ #include "interface_intern.h" +#include "WM_api.h" +#include "WM_types.h" + /* -------------------------------------------------------------------- */ /** \name Button (uiBut) * \{ */ @@ -68,6 +71,31 @@ bool ui_but_is_toggle(const uiBut *but) ); } +#ifdef USE_UI_POPOVER_ONCE +bool ui_but_is_popover_once_compat(const uiBut *but) +{ + return ( + (but->type == UI_BTYPE_BUT) || + ui_but_is_toggle(but) + ); +} +#endif + +bool UI_but_is_tool(const uiBut *but) +{ + /* very evil! */ + if (but->optype != NULL) { + static wmOperatorType *ot = NULL; + if (ot == NULL) { + ot = WM_operatortype_find("WM_OT_tool_set_by_name", false); + } + if (but->optype == ot) { + return true; + } + } + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/interface/interface_region_hud.c b/source/blender/editors/interface/interface_region_hud.c new file mode 100644 index 00000000000..f3ff6fdf2c0 --- /dev/null +++ b/source/blender/editors/interface/interface_region_hud.c @@ -0,0 +1,339 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2008 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/interface/interface_region_hud.c + * \ingroup edinterface + * + * Floating Persistent Region + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_userdef_types.h" + +#include "BLI_string.h" +#include "BLI_rect.h" +#include "BLI_listbase.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_screen.h" +#include "BKE_main.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" + +#include "BIF_gl.h" + +#include "UI_interface.h" +#include "UI_view2d.h" + +#include "BLT_translation.h" + +#include "ED_screen.h" +#include "ED_undo.h" + +#include "interface_intern.h" +#include "GPU_framebuffer.h" + + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +static bool last_redo_poll(const bContext *C) +{ + wmOperator *op = WM_operator_last_redo(C); + if (op == NULL) { + return false; + } + bool success = false; + if (WM_operator_repeat_check(C, op) && + WM_operator_check_ui_empty(op->type) == false) + { + success = WM_operator_poll((bContext *)C, op->type); + } + return success; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Redo Panel + * \{ */ + +static bool hud_panel_operator_redo_poll(const bContext *C, PanelType *UNUSED(pt)) +{ + return last_redo_poll(C); +} + +static void hud_panel_operator_redo_draw_header(const bContext *C, Panel *pa) +{ + wmOperator *op = WM_operator_last_redo(C); + BLI_strncpy(pa->drawname, RNA_struct_ui_name(op->type->srna), sizeof(pa->drawname)); +} + +static void hud_panel_operator_redo_draw(const bContext *C, Panel *pa) +{ + wmOperator *op = WM_operator_last_redo(C); + if (op == NULL) { + return; + } + if (!WM_operator_check_ui_enabled(C, op->type->name)) { + uiLayoutSetEnabled(pa->layout, false); + } + uiLayout *col = uiLayoutColumn(pa->layout, false); + uiTemplateOperatorRedoProperties(col, C); +} + +static void hud_panels_register(ARegionType *art, int space_type, int region_type) +{ + PanelType *pt; + + pt = MEM_callocN(sizeof(PanelType), __func__); + strcpy(pt->idname, "OPERATOR_PT_redo"); + strcpy(pt->label, N_("Redo")); + strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->draw_header = hud_panel_operator_redo_draw_header; + pt->draw = hud_panel_operator_redo_draw; + pt->poll = hud_panel_operator_redo_poll; + pt->space_type = space_type; + pt->region_type = region_type; + BLI_addtail(&art->paneltypes, pt); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for Floating Region + * \{ */ + +struct HudRegionData { + short regionid; +}; + +static void hud_region_init(wmWindowManager *wm, ARegion *ar) +{ + ED_region_panels_init(wm, ar); + UI_region_handlers_add(&ar->handlers); + ar->flag |= RGN_FLAG_TEMP_REGIONDATA; +} + +static void hud_region_free(ARegion *ar) +{ + MEM_SAFE_FREE(ar->regiondata); +} + +static void hud_region_layout(const bContext *C, ARegion *ar) +{ + bool ok = false; + + { + struct HudRegionData *hrd = ar->regiondata; + if (hrd != NULL) { + ScrArea *sa = CTX_wm_area(C); + ARegion *ar_op = (hrd->regionid != -1) ? BKE_area_find_region_type(sa, hrd->regionid) : NULL; + ARegion *ar_prev = CTX_wm_region(C); + CTX_wm_region_set((bContext *)C, ar_op); + ok = last_redo_poll(C); + CTX_wm_region_set((bContext *)C, ar_prev); + } + } + + if (!ok) { + ED_region_tag_redraw(ar); + ar->flag |= RGN_FLAG_HIDDEN; + return; + } + + int size_y = ar->sizey; + + ED_region_panels_layout(C, ar); + + if (ar->panels.first && (ar->sizey != size_y)) { + View2D *v2d = &ar->v2d; + ar->winx = ar->sizex; + ar->winy = ar->sizey; + + ar->winrct.xmax = (ar->winrct.xmin + ar->winx) - 1; + ar->winrct.ymax = (ar->winrct.ymin + ar->winy) - 1; + + UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_PANELS_UI, ar->winx, ar->winy); + } + + /* restore view matrix */ + UI_view2d_view_restore(C); +} + +static void hud_region_draw(const bContext *C, ARegion *ar) +{ + UI_view2d_view_ortho(&ar->v2d); + wmOrtho2_region_pixelspace(ar); + GPU_clear_color(0, 0, 0, 0.0f); + GPU_clear(GPU_COLOR_BIT); + + if ((ar->flag & RGN_FLAG_HIDDEN) == 0) { + float color[4]; + UI_GetThemeColor4fv(TH_BUTBACK, color); + if ((U.uiflag2 & USER_REGION_OVERLAP) == 0) { + color[3] = 1.0f; + } + ui_draw_widget_back_color(UI_WTYPE_BOX, false, &(rcti){.xmax = ar->winx, .ymax = ar->winy}, color); + ED_region_panels_draw(C, ar); + } +} + +ARegionType *ED_area_type_hud(int space_type) +{ + ARegionType *art = MEM_callocN(sizeof(ARegionType), __func__); + art->regionid = RGN_TYPE_HUD; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D; + art->layout = hud_region_layout; + art->draw = hud_region_draw; + art->init = hud_region_init; + art->free = hud_region_free; + + hud_panels_register(art, space_type, art->regionid); + + art->lock = 1; /* can become flag, see BKE_spacedata_draw_locks */ + return art; +} + +static ARegion *hud_region_add(ScrArea *sa) +{ + ARegion *ar = MEM_callocN(sizeof(ARegion), "area region"); + ARegion *ar_win = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); + if (ar_win) { + BLI_insertlinkbefore(&sa->regionbase, ar_win, ar); + } + else { + BLI_addtail(&sa->regionbase, ar); + } + ar->regiontype = RGN_TYPE_HUD; + ar->alignment = RGN_ALIGN_FLOAT; + ar->overlap = true; + ar->flag |= RGN_FLAG_DYNAMIC_SIZE; + + return ar; +} + +void ED_area_type_hud_clear(wmWindowManager *wm, ScrArea *sa_keep) +{ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + if (sa != sa_keep) { + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_HUD) { + if ((ar->flag & RGN_FLAG_HIDDEN) == 0) { + ar->flag |= RGN_FLAG_HIDDEN; + ED_region_tag_redraw(ar); + ED_area_tag_redraw(sa); + } + } + } + } + } + } +} + +void ED_area_type_hud_ensure(bContext *C, ScrArea *sa) +{ + wmWindowManager *wm = CTX_wm_manager(C); + ED_area_type_hud_clear(wm, sa); + + ARegionType *art = BKE_regiontype_from_id(sa->type, RGN_TYPE_HUD); + if (art == NULL) { + return; + } + + bool init = false; + ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_HUD); + if (!last_redo_poll(C)) { + if (ar) { + ED_region_tag_redraw(ar); + ar->flag |= RGN_FLAG_HIDDEN; + } + return; + } + + if (ar == NULL) { + init = true; + ar = hud_region_add(sa); + ar->type = art; + } + + ED_region_init(ar); + ED_region_tag_redraw(ar); + + /* Reset zoom level (not well supported). */ + ar->v2d.cur = ar->v2d.tot = (rctf){.xmax = ar->winx, .ymax = ar->winy}; + ar->v2d.minzoom = 1.0f; + ar->v2d.maxzoom = 1.0f; + + /* Let 'ED_area_update_region_sizes' do the work of placing the region. + * Otherwise we could set the 'ar->winrct' & 'ar->winx/winy' here. */ + if (init) { + sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE; + } + else { + if (ar->flag & RGN_FLAG_HIDDEN) { + sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE; + } + ar->flag &= ~RGN_FLAG_HIDDEN; + } + + { + ARegion *ar_op = CTX_wm_region(C); + BLI_assert((ar_op == NULL) || (ar_op->regiontype != RGN_TYPE_HUD)); + struct HudRegionData *hrd = ar->regiondata; + if (hrd == NULL) { + hrd = MEM_callocN(sizeof(*hrd), __func__); + ar->regiondata = hrd; + } + if (ar_op) { + hrd->regionid = ar_op->regiontype; + } + else { + hrd->regionid = -1; + } + } + + /* XXX, should be handled in more general way. */ + ar->visible = !((ar->flag & RGN_FLAG_HIDDEN) || (ar->flag & RGN_FLAG_TOO_SMALL)); + + /* We shouldn't need to do this every time :S */ + /* XXX, this is evil! - it also makes the menu show on first draw. :( */ + ARegion *ar_prev = CTX_wm_region(C); + CTX_wm_region_set((bContext *)C, ar); + hud_region_layout(C, ar); + CTX_wm_region_set((bContext *)C, ar_prev); +} + +/** \} */ diff --git a/source/blender/editors/interface/interface_region_menu_pie.c b/source/blender/editors/interface/interface_region_menu_pie.c index 41001d65d82..504e1807a8f 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.c +++ b/source/blender/editors/interface/interface_region_menu_pie.c @@ -76,7 +76,7 @@ static uiBlock *ui_block_func_PIE(bContext *UNUSED(C), uiPopupBlockHandle *handl uiPieMenu *pie = arg_pie; int minwidth, width, height; - minwidth = 50; + minwidth = UI_MENU_WIDTH_MIN; block = pie->block_radial; /* in some cases we create the block before the region, @@ -202,7 +202,6 @@ void UI_pie_menu_end(bContext *C, uiPieMenu *pie) menu, WM_HANDLER_ACCEPT_DBL_CLICK); WM_event_add_mousemove(C); - menu->can_refresh = false; MEM_freeN(pie); } diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index fa7113f195e..b9222a75803 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -191,7 +191,13 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi if (pup->but) { /* minimum width to enforece */ - minwidth = BLI_rctf_size_x(&pup->but->rect); + if (pup->but->drawstr[0]) { + minwidth = BLI_rctf_size_x(&pup->but->rect); + } + else { + /* For buttons with no text, use the minimum (typically icon only). */ + minwidth = UI_MENU_WIDTH_MIN; + } /* settings (typically rna-enum-popups) show above the button, * menu's like file-menu, show below */ @@ -209,7 +215,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi } } else { - minwidth = 50; + minwidth = UI_MENU_WIDTH_MIN; direction = UI_DIR_DOWN; } @@ -284,7 +290,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi /* for a header menu we set the direction automatic */ if (!pup->slideout && flip) { ScrArea *sa = CTX_wm_area(C); - if (sa && sa->headertype == HEADERDOWN) { + if (sa && ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) { ARegion *ar = CTX_wm_region(C); if (ar && ar->regiontype == RGN_TYPE_HEADER) { UI_block_direction_set(block, UI_DIR_UP); @@ -359,7 +365,6 @@ uiPopupBlockHandle *ui_popup_menu_create( WM_event_add_mousemove(C); } - handle->can_refresh = false; MEM_freeN(pup); return handle; @@ -450,7 +455,6 @@ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) UI_popup_handlers_add(C, &window->modalhandlers, menu, 0); WM_event_add_mousemove(C); - menu->can_refresh = false; MEM_freeN(pup); } @@ -569,6 +573,7 @@ void UI_popup_block_invoke_ex(bContext *C, uiBlockCreateFunc func, void *arg, co handle = ui_popup_block_create(C, NULL, NULL, func, NULL, arg); handle->popup = true; + handle->can_refresh = true; handle->optype = (opname) ? WM_operatortype_find(opname, 0) : NULL; handle->opcontext = opcontext; @@ -591,6 +596,7 @@ void UI_popup_block_ex( handle = ui_popup_block_create(C, NULL, NULL, func, NULL, arg); handle->popup = true; handle->retvalue = 1; + handle->can_refresh = true; handle->popup_op = op; handle->popup_arg = arg; @@ -611,6 +617,7 @@ void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int handle = ui_popup_block_create(C, NULL, NULL, func, NULL, op); handle->popup = 1; handle->retvalue = 1; + handle->can_refresh = true; handle->popup_arg = op; handle->popup_func = operator_cb; @@ -627,11 +634,13 @@ void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block) /* if loading new .blend while popup is open, window will be NULL */ if (block->handle) { if (win) { + const bScreen *screen = WM_window_get_active_screen(win); + UI_popup_handlers_remove(&win->modalhandlers, block->handle); ui_popup_block_free(C, block->handle); /* In the case we have nested popups, closing one may need to redraw another, see: T48874 */ - for (ARegion *ar = win->screen->regionbase.first; ar; ar = ar->next) { + for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) { ED_region_tag_refresh_ui(ar); } } diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c new file mode 100644 index 00000000000..376e367f4da --- /dev/null +++ b/source/blender/editors/interface/interface_region_popover.c @@ -0,0 +1,393 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2008 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/interface/interface_region_popover.c + * \ingroup edinterface + * + * Pop-Over Region + * + * \note This is very close to 'interface_region_menu_popup.c' + * + * We could even merge them, however menu logic is already over-loaded. + * PopOver's have the following differences. + * + * - UI is not constrained to a list. + * - Pressing a button won't close the pop-over. + * - Different draw style (to show this is has different behavior from a menu). + * - #PanelType are used instead of #MenuType. + * - No menu flipping support. + * - No moving the menu to fit the mouse cursor. + * - No key accelerators to access menu items + * (if we add support they would work differently). + * - No arrow key navigation. + * - No menu memory. + * - No title. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_userdef_types.h" + +#include "BLI_listbase.h" + +#include "BLI_rect.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_screen.h" +#include "BKE_report.h" + +#include "ED_screen.h" + +#include "WM_api.h" +#include "WM_types.h" + + +#include "UI_interface.h" + +#include "interface_intern.h" +#include "interface_regions_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name Popup Menu with Callback or String + * \{ */ + +struct uiPopover { + uiBlock *block; + uiLayout *layout; + uiBut *but; + + /* Needed for keymap removal. */ + wmWindow *window; + wmKeyMap *keymap; + struct wmEventHandler *keymap_handler; + + uiMenuCreateFunc menu_func; + void *menu_arg; + + /* Size in pixels (ui scale applied). */ + int ui_size_x; + +#ifdef USE_UI_POPOVER_ONCE + bool is_once; +#endif +}; + +static void ui_popover_create_block(bContext *C, uiPopover *pup, int opcontext) +{ + BLI_assert(pup->ui_size_x != 0); + + uiStyle *style = UI_style_get_dpi(); + pup->block = UI_block_begin(C, NULL, __func__, UI_EMBOSS); + pup->layout = UI_block_layout( + pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, + pup->ui_size_x, 0, MENU_PADDING, style); + + uiLayoutSetOperatorContext(pup->layout, opcontext); + + if (pup->but) { + if (pup->but->context) { + uiLayoutContextCopy(pup->layout, pup->but->context); + } + } + + pup->block->flag |= UI_BLOCK_NO_FLIP; +} + +static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, void *arg_pup) +{ + uiPopover *pup = arg_pup; + + /* Create UI block and layout now if it wasn't done between begin/end. */ + if (!pup->layout) { + ui_popover_create_block(C, pup, WM_OP_INVOKE_REGION_WIN); + + if (pup->menu_func) { + pup->block->handle = handle; + pup->menu_func(C, pup->layout, pup->menu_arg); + pup->block->handle = NULL; + } + + pup->layout = NULL; + } + + /* Setup and resolve UI layout for block. */ + uiBlock *block = pup->block; + int width, height; + + UI_block_region_set(block, handle->region); + UI_block_layout_resolve(block, &width, &height); + UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER); +#ifdef USE_UI_POPOVER_ONCE + if (pup->is_once) { + UI_block_flag_enable(block, UI_BLOCK_POPOVER_ONCE); + } +#endif + UI_block_direction_set(block, UI_DIR_DOWN | UI_DIR_CENTER_X); + + const int block_margin = U.widget_unit / 2; + + if (pup->but) { + /* For a header menu we set the direction automatic. */ + block->minbounds = BLI_rctf_size_x(&pup->but->rect); + UI_block_bounds_set_normal(block, block_margin); + + /* If menu slides out of other menu, override direction. */ + bool slideout = false; //ui_block_is_menu(pup->but->block); + if (slideout) + UI_block_direction_set(block, UI_DIR_RIGHT); + + /* Store the button location for positioning the popover arrow hint. */ + if (!handle->refresh) { + float center[2] = {BLI_rctf_cent_x(&pup->but->rect), BLI_rctf_cent_y(&pup->but->rect)}; + ui_block_to_window_fl(handle->ctx_region, pup->but->block, ¢er[0], ¢er[1]); + /* These variables aren't used for popovers, we could add new variables if there is a conflict. */ + handle->prev_mx = block->mx = (int)center[0]; + handle->prev_my = block->my = (int)center[1]; + } + else { + block->mx = handle->prev_mx; + block->my = handle->prev_my; + } + + if (!slideout) { + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + + if (ar && ar->panels.first) { + /* For regions with panels, prefer to open to top so we can + * see the values of the buttons below changing. */ + UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); + } + else if (sa && ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) { + /* Prefer popover from header to be positioned into the editor. */ + if (ar && ar->regiontype == RGN_TYPE_HEADER) { + UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); + } + } + } + + /* Estimated a maximum size so we don't go offscreen for low height + * areas near the bottom of the window on refreshes. */ + handle->max_size_y = UI_UNIT_Y * 16.0f; + } + else { + /* Not attached to a button. */ + int offset[2] = {0, 0}; + UI_block_flag_enable(block, UI_BLOCK_LOOP); + UI_block_direction_set(block, block->direction); + block->minbounds = UI_MENU_WIDTH_MIN; + bool use_place_under_active = !handle->refresh; + + if (use_place_under_active) { + uiBut *but = NULL; + for (but = block->buttons.first; but; but = but->next) { + if (but->flag & (UI_SELECT | UI_SELECT_DRAW)) { + break; + } + } + + if (but) { + offset[0] = -(but->rect.xmin + 0.8f * BLI_rctf_size_x(&but->rect)); + offset[1] = -(but->rect.ymin + 0.5f * BLI_rctf_size_y(&but->rect)); + } + } + + UI_block_bounds_set_popup(block, block_margin, offset[0], offset[1]); + } + + return block; +} + +static void ui_block_free_func_POPOVER(uiPopupBlockHandle *UNUSED(handle), void *arg_pup) +{ + uiPopover *pup = arg_pup; + if (pup->keymap != NULL) { + wmWindow *window = pup->window; + WM_event_remove_keymap_handler(&window->modalhandlers, pup->keymap); + } + MEM_freeN(pup); +} + +uiPopupBlockHandle *ui_popover_panel_create( + bContext *C, ARegion *butregion, uiBut *but, + uiMenuCreateFunc menu_func, void *arg) +{ + /* Create popover, buttons are created from callback. */ + uiPopover *pup = MEM_callocN(sizeof(uiPopover), __func__); + pup->but = but; + + /* FIXME: maybe one day we want non panel popovers? */ + { + int ui_units_x = ((PanelType *)arg)->ui_units_x; + pup->ui_size_x = U.widget_unit * (ui_units_x ? ui_units_x : UI_POPOVER_WIDTH_UNITS); + } + + pup->menu_func = menu_func; + pup->menu_arg = arg; + +#ifdef USE_UI_POPOVER_ONCE + pup->is_once = true; +#endif + + /* Create popup block. */ + uiPopupBlockHandle *handle; + handle = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPOVER, pup); + handle->popup_create_vars.free_func = ui_block_free_func_POPOVER; + handle->can_refresh = true; + + /* Add handlers. If attached to a button, the button will already + * add a modal handler and pass on events. */ + if (!but) { + wmWindow *window = CTX_wm_window(C); + UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); + WM_event_add_mousemove(C); + handle->popup = true; + } + + return handle; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Standard Popover Panels + * \{ */ + +int UI_popover_panel_invoke( + bContext *C, const char *idname, + bool keep_open, ReportList *reports) +{ + uiLayout *layout; + PanelType *pt = WM_paneltype_find(idname, true); + if (pt == NULL) { + BKE_reportf(reports, RPT_ERROR, "Panel \"%s\" not found", idname); + return OPERATOR_CANCELLED; + } + + if (pt->poll && (pt->poll(C, pt) == false)) { + /* cancel but allow event to pass through, just like operators do */ + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + if (keep_open) { + ui_popover_panel_create(C, NULL, NULL, ui_item_paneltype_func, pt); + } + else { + uiPopover *pup = UI_popover_begin(C, U.widget_unit * pt->ui_units_x); + layout = UI_popover_layout(pup); + UI_paneltype_draw(C, pt, layout); + UI_popover_end(C, pup, NULL); + } + + return OPERATOR_INTERFACE; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Popup Menu API with begin & end + * \{ */ + +/** + * Only return handler, and set optional title. + */ +uiPopover *UI_popover_begin(bContext *C, int ui_size_x) +{ + uiPopover *pup = MEM_callocN(sizeof(uiPopover), "popover menu"); + if (ui_size_x == 0) { + ui_size_x = U.widget_unit * UI_POPOVER_WIDTH_UNITS; + } + pup->ui_size_x = ui_size_x; + + /* Opertor context default same as menus, change if needed. */ + ui_popover_create_block(C, pup, WM_OP_EXEC_REGION_WIN); + + /* create in advance so we can let buttons point to retval already */ + pup->block->handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle"); + + return pup; +} + +static void popover_keymap_fn(wmKeyMap *UNUSED(keymap), wmKeyMapItem *UNUSED(kmi), void *user_data) +{ + uiPopover *pup = user_data; + pup->block->handle->menuretval = UI_RETURN_OK; +} + +/* set the whole structure to work */ +void UI_popover_end(bContext *C, uiPopover *pup, wmKeyMap *keymap) +{ + wmWindow *window = CTX_wm_window(C); + /* Create popup block. No refresh support since the buttons were created + * between begin/end and we have no callback to recreate them. */ + uiPopupBlockHandle *handle; + + if (keymap) { + /* Add so we get keymaps shown in the buttons. */ + UI_block_flag_enable(pup->block, UI_BLOCK_SHOW_SHORTCUT_ALWAYS); + pup->keymap = keymap; + pup->keymap_handler = WM_event_add_keymap_handler_priority(&window->modalhandlers, keymap, 0); + WM_event_set_keymap_handler_callback(pup->keymap_handler, popover_keymap_fn, pup); + } + + handle = ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPOVER, pup); + handle->popup_create_vars.free_func = ui_block_free_func_POPOVER; + + /* Add handlers. */ + UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); + WM_event_add_mousemove(C); + handle->popup = true; + + /* Re-add so it gets priority. */ + if (keymap) { + BLI_remlink(&window->modalhandlers, pup->keymap_handler); + BLI_addhead(&window->modalhandlers, pup->keymap_handler); + } + + pup->window = window; + + /* TODO(campbell): we may want to make this configurable. + * The begin/end stype of calling popups doesn't allow to 'can_refresh' to be set. + * For now close this style of popvers when accessed. */ + UI_block_flag_disable(pup->block, UI_BLOCK_KEEP_OPEN); + + /* panels are created flipped (from event handling pov) */ + pup->block->flag ^= UI_BLOCK_IS_FLIP; +} + +uiLayout *UI_popover_layout(uiPopover *pup) +{ + return pup->layout; +} + +#ifdef USE_UI_POPOVER_ONCE +void UI_popover_once_clear(uiPopover *pup) +{ + pup->is_once = false; +} +#endif + +/** \} */ diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 656c59055af..1bbf2242c5d 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -48,7 +48,6 @@ #include "WM_api.h" #include "WM_types.h" -#include "wm_subwindow.h" #include "UI_interface.h" @@ -64,13 +63,13 @@ /** * Translate any popup regions (so we can drag them). */ -void ui_popup_translate(bContext *C, ARegion *ar, const int mdiff[2]) +void ui_popup_translate(ARegion *ar, const int mdiff[2]) { uiBlock *block; BLI_rcti_translate(&ar->winrct, UNPACK2(mdiff)); - ED_region_update_rect(C, ar); + ED_region_update_rect(ar); ED_region_tag_redraw(ar); @@ -85,32 +84,41 @@ void ui_popup_translate(bContext *C, ARegion *ar, const int mdiff[2]) } /* position block relative to but, result is in window space */ -static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block) +static void ui_popup_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block) { - uiBut *bt; - uiSafetyRct *saferct; + uiPopupBlockHandle *handle = block->handle; + + /* Compute button position in window coordinates using the source + * button region/block, to position the popup attached to it. */ rctf butrct; - /*float aspect;*/ /*UNUSED*/ - int size_x, size_y, offset_x = 0, offset_y = 0; - short dir1 = 0, dir2 = 0; - /* transform to window coordinates, using the source button region/block */ - ui_block_to_window_rctf(butregion, but->block, &butrct, &but->rect); + if (!handle->refresh) { + ui_block_to_window_rctf(butregion, but->block, &butrct, &but->rect); + + /* widget_roundbox_set has this correction too, keep in sync */ + if (but->type != UI_BTYPE_PULLDOWN) { + if (but->drawflag & UI_BUT_ALIGN_TOP) + butrct.ymax += U.pixelsize; + if (but->drawflag & UI_BUT_ALIGN_LEFT) + butrct.xmin -= U.pixelsize; + } - /* widget_roundbox_set has this correction too, keep in sync */ - if (but->type != UI_BTYPE_PULLDOWN) { - if (but->drawflag & UI_BUT_ALIGN_TOP) - butrct.ymax += U.pixelsize; - if (but->drawflag & UI_BUT_ALIGN_LEFT) - butrct.xmin -= U.pixelsize; + handle->prev_butrct = butrct; + } + else { + /* For refreshes, keep same button position so popup doesn't move. */ + butrct = handle->prev_butrct; } - /* calc block rect */ + /* Compute block size in window space, based on buttons contained in it. */ if (block->rect.xmin == 0.0f && block->rect.xmax == 0.0f) { if (block->buttons.first) { BLI_rctf_init_minmax(&block->rect); - for (bt = block->buttons.first; bt; bt = bt->next) { + for (uiBut *bt = block->buttons.first; bt; bt = bt->next) { + if (block->content_hints & BLOCK_CONTAINS_SUBMENU_BUT) { + bt->rect.xmax += UI_MENU_SUBMENU_PADDING; + } BLI_rctf_union(&block->rect, &bt->rect); } } @@ -121,34 +129,34 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, } } - /* aspect = (float)(BLI_rcti_size_x(&block->rect) + 4);*/ /*UNUSED*/ ui_block_to_window_rctf(butregion, but->block, &block->rect, &block->rect); - //block->rect.xmin -= 2.0; block->rect.ymin -= 2.0; - //block->rect.xmax += 2.0; block->rect.ymax += 2.0; + /* Compute direction relative to button, based on available space. */ + const int size_x = BLI_rctf_size_x(&block->rect) + 0.2f * UI_UNIT_X; /* 4 for shadow */ + const int size_y = BLI_rctf_size_y(&block->rect) + 0.2f * UI_UNIT_Y; + const int center_x = (block->direction & UI_DIR_CENTER_X) ? size_x / 2 : 0; + const int center_y = (block->direction & UI_DIR_CENTER_Y) ? size_y / 2 : 0; - size_x = BLI_rctf_size_x(&block->rect) + 0.2f * UI_UNIT_X; /* 4 for shadow */ - size_y = BLI_rctf_size_y(&block->rect) + 0.2f * UI_UNIT_Y; - /* aspect /= (float)size_x;*/ /*UNUSED*/ + short dir1 = 0, dir2 = 0; - { + if (!handle->refresh) { bool left = 0, right = 0, top = 0, down = 0; - // int offscreen; const int win_x = WM_window_pixels_x(window); const int win_y = WM_window_pixels_y(window); - // wm_window_get_size(window, &win_x, &win_y); - const int center_y = (block->direction & UI_DIR_CENTER_Y) ? size_y / 2 : 0; + /* Take into account maximum size so we don't have to flip on refresh. */ + const float max_size_x = max_ff(size_x, handle->max_size_x); + const float max_size_y = max_ff(size_y, handle->max_size_y); /* check if there's space at all */ - if (butrct.xmin - size_x > 0.0f) left = 1; - if (butrct.xmax + size_x < win_x) right = 1; - if (butrct.ymin - size_y + center_y > 0.0f) down = 1; - if (butrct.ymax + size_y - center_y < win_y) top = 1; + if (butrct.xmin - max_size_x + center_x > 0.0f) left = 1; + if (butrct.xmax + max_size_x - center_x < win_x) right = 1; + if (butrct.ymin - max_size_y + center_y > 0.0f) down = 1; + if (butrct.ymax + max_size_y - center_y < win_y) top = 1; if (top == 0 && down == 0) { - if (butrct.ymin - size_y < win_y - butrct.ymax - size_y) + if (butrct.ymin - max_size_y < win_y - butrct.ymax - max_size_y) top = 1; else down = 1; @@ -182,65 +190,57 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, if (dir2 == UI_DIR_DOWN && down == 0) dir2 = UI_DIR_UP; } - if (dir1 == UI_DIR_LEFT) { - offset_x = butrct.xmin - block->rect.xmax; - if (dir2 == UI_DIR_UP) offset_y = butrct.ymin - block->rect.ymin - center_y - MENU_PADDING; - else offset_y = butrct.ymax - block->rect.ymax + center_y + MENU_PADDING; - } - else if (dir1 == UI_DIR_RIGHT) { - offset_x = butrct.xmax - block->rect.xmin; - if (dir2 == UI_DIR_UP) offset_y = butrct.ymin - block->rect.ymin - center_y - MENU_PADDING; - else offset_y = butrct.ymax - block->rect.ymax + center_y + MENU_PADDING; - } - else if (dir1 == UI_DIR_UP) { - offset_y = butrct.ymax - block->rect.ymin; - if (dir2 == UI_DIR_RIGHT) offset_x = butrct.xmax - block->rect.xmax; - else offset_x = butrct.xmin - block->rect.xmin; - /* changed direction? */ - if ((dir1 & block->direction) == 0) { - UI_block_order_flip(block); - } - } - else if (dir1 == UI_DIR_DOWN) { - offset_y = butrct.ymin - block->rect.ymax; - if (dir2 == UI_DIR_RIGHT) offset_x = butrct.xmax - block->rect.xmax; - else offset_x = butrct.xmin - block->rect.xmin; - /* changed direction? */ - if ((dir1 & block->direction) == 0) { - UI_block_order_flip(block); - } - } + handle->prev_dir1 = dir1; + handle->prev_dir2 = dir2; + } + else { + /* For refreshes, keep same popup direct so popup doesn't move + * to a totally different position while editing in it. */ + dir1 = handle->prev_dir1; + dir2 = handle->prev_dir2; + } - /* and now we handle the exception; no space below or to top */ - if (top == 0 && down == 0) { - if (dir1 == UI_DIR_LEFT || dir1 == UI_DIR_RIGHT) { - /* align with bottom of screen */ - // offset_y = size_y; (not with menu scrolls) - } - } + /* Compute offset based on direction. */ + int offset_x = 0, offset_y = 0; -#if 0 /* seems redundant and causes issues with blocks inside big regions */ - /* or no space left or right */ - if (left == 0 && right == 0) { - if (dir1 == UI_DIR_UP || dir1 == UI_DIR_DOWN) { - /* align with left size of screen */ - offset_x = -block->rect.xmin + 5; - } + if (dir1 == UI_DIR_LEFT) { + offset_x = butrct.xmin - block->rect.xmax; + if (dir2 == UI_DIR_UP) offset_y = butrct.ymin - block->rect.ymin - center_y - MENU_PADDING; + else offset_y = butrct.ymax - block->rect.ymax + center_y + MENU_PADDING; + } + else if (dir1 == UI_DIR_RIGHT) { + offset_x = butrct.xmax - block->rect.xmin; + if (dir2 == UI_DIR_UP) offset_y = butrct.ymin - block->rect.ymin - center_y - MENU_PADDING; + else offset_y = butrct.ymax - block->rect.ymax + center_y + MENU_PADDING; + } + else if (dir1 == UI_DIR_UP) { + offset_y = butrct.ymax - block->rect.ymin; + if (dir2 == UI_DIR_RIGHT) offset_x = butrct.xmax - block->rect.xmax + center_x; + else offset_x = butrct.xmin - block->rect.xmin - center_x; + /* changed direction? */ + if ((dir1 & block->direction) == 0) { + /* TODO: still do */ + UI_block_order_flip(block); + } + } + else if (dir1 == UI_DIR_DOWN) { + offset_y = butrct.ymin - block->rect.ymax; + if (dir2 == UI_DIR_RIGHT) offset_x = butrct.xmax - block->rect.xmax + center_x; + else offset_x = butrct.xmin - block->rect.xmin - center_x; + /* changed direction? */ + if ((dir1 & block->direction) == 0) { + /* TODO: still do */ + UI_block_order_flip(block); } -#endif - -#if 0 - /* clamp to window bounds, could be made into an option if its ever annoying */ - if ( (offscreen = (block->rect.ymin + offset_y)) < 0) offset_y -= offscreen; /* bottom */ - else if ((offscreen = (block->rect.ymax + offset_y) - winy) > 0) offset_y -= offscreen; /* top */ - if ( (offscreen = (block->rect.xmin + offset_x)) < 0) offset_x -= offscreen; /* left */ - else if ((offscreen = (block->rect.xmax + offset_x) - winx) > 0) offset_x -= offscreen; /* right */ -#endif } - /* apply offset, buttons in window coords */ + /* Center over popovers for eg. */ + if (block->direction & UI_DIR_CENTER_X) { + offset_x += BLI_rctf_size_x(&butrct) / ((dir2 == UI_DIR_LEFT) ? 2 : - 2); + } - for (bt = block->buttons.first; bt; bt = bt->next) { + /* Apply offset, buttons in window coords. */ + for (uiBut *bt = block->buttons.first; bt; bt = bt->next) { ui_block_to_window_rctf(butregion, but->block, &bt->rect, &bt->rect); BLI_rctf_translate(&bt->rect, offset_x, offset_y); @@ -251,7 +251,7 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, BLI_rctf_translate(&block->rect, offset_x, offset_y); - /* safety calculus */ + /* Safety calculus. */ { const float midx = BLI_rctf_cent_x(&butrct); const float midy = BLI_rctf_cent_y(&butrct); @@ -281,7 +281,7 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, } /* keep a list of these, needed for pulldown menus */ - saferct = MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct"); + uiSafetyRct *saferct = MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct"); saferct->parent = butrct; saferct->safety = block->safety; BLI_freelistN(&block->saferct); @@ -295,7 +295,7 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, /** \name Menu Block Creation * \{ */ -static void ui_block_region_draw(const bContext *C, ARegion *ar) +static void ui_block_region_refresh(const bContext *C, ARegion *ar) { ScrArea *ctx_area = CTX_wm_area(C); ARegion *ctx_region = CTX_wm_region(C); @@ -309,9 +309,11 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar) ar->do_draw &= ~RGN_DRAW_REFRESH_UI; for (block = ar->uiblocks.first; block; block = block_next) { block_next = block->next; - if (block->handle->can_refresh) { - handle_ctx_area = block->handle->ctx_area; - handle_ctx_region = block->handle->ctx_region; + uiPopupBlockHandle *handle = block->handle; + + if (handle->can_refresh) { + handle_ctx_area = handle->ctx_area; + handle_ctx_region = handle->ctx_region; if (handle_ctx_area) { CTX_wm_area_set((bContext *)C, handle_ctx_area); @@ -319,13 +321,21 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar) if (handle_ctx_region) { CTX_wm_region_set((bContext *)C, handle_ctx_region); } - ui_popup_block_refresh((bContext *)C, block->handle, NULL, NULL); + + uiBut *but = handle->popup_create_vars.but; + ARegion *butregion = handle->popup_create_vars.butregion; + ui_popup_block_refresh((bContext *)C, handle, butregion, but); } } } CTX_wm_area_set((bContext *)C, ctx_area); CTX_wm_region_set((bContext *)C, ctx_region); +} + +static void ui_block_region_draw(const bContext *C, ARegion *ar) +{ + uiBlock *block; for (block = ar->uiblocks.first; block; block = block->next) UI_block_draw(C, block); @@ -335,7 +345,7 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar) * Use to refresh centered popups on screen resizing (for splash). */ static void ui_block_region_popup_window_listener( - bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn) + wmWindow *UNUSED(win), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn, const Scene *UNUSED(scene)) { switch (wmn->category) { case NC_WINDOW: @@ -455,8 +465,6 @@ uiBlock *ui_popup_block_refresh( bContext *C, uiPopupBlockHandle *handle, ARegion *butregion, uiBut *but) { - BLI_assert(handle->can_refresh == true); - const int margin = UI_POPUP_MARGIN; wmWindow *window = CTX_wm_window(C); ARegion *ar = handle->region; @@ -468,6 +476,10 @@ uiBlock *ui_popup_block_refresh( uiBlock *block_old = ar->uiblocks.first; uiBlock *block; + handle->refresh = (block_old != NULL); + + BLI_assert(!handle->refresh || handle->can_refresh); + #ifdef DEBUG wmEvent *event_back = window->eventstate; #endif @@ -513,7 +525,7 @@ uiBlock *ui_popup_block_refresh( /* if this is being created from a button */ if (but) { block->aspect = but->block->aspect; - ui_block_position(window, butregion, but, block); + ui_popup_block_position(window, butregion, but, block); handle->direction = block->direction; } else { @@ -551,7 +563,7 @@ uiBlock *ui_popup_block_refresh( block->pie_data.pie_center_spawned[0] += x_offset; block->pie_data.pie_center_spawned[1] += y_offset; - ui_block_translate(block, x_offset, y_offset); + UI_block_translate(block, x_offset, y_offset); if (U.pie_initial_timeout > 0) block->pie_data.flags |= UI_PIE_INITIAL_DIRECTION; @@ -576,6 +588,17 @@ uiBlock *ui_popup_block_refresh( else { /* clip block with window boundary */ ui_popup_block_clip(window, block); + + /* Avoid menu moving down and losing cursor focus by keeping it at + * the same height. */ + if (handle->refresh && handle->prev_block_rect.ymax > block->rect.ymax) { + float offset = handle->prev_block_rect.ymax - block->rect.ymax; + UI_block_translate(block, 0, offset); + block->rect.ymin = handle->prev_block_rect.ymin; + } + + handle->prev_block_rect = block->rect; + /* the block and buttons were positioned in window space as in 2.4x, now * these menu blocks are regions so we bring it back to region space. * additionally we add some padding for the menu shadow or rounded menus */ @@ -584,7 +607,15 @@ uiBlock *ui_popup_block_refresh( ar->winrct.ymin = block->rect.ymin - margin; ar->winrct.ymax = block->rect.ymax + UI_POPUP_MENU_TOP; - ui_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin); + UI_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin); + + /* apply scroll offset */ + if (handle->scrolloffset != 0.0f) { + for (uiBut *bt = block->buttons.first; bt; bt = bt->next) { + bt->rect.ymin += handle->scrolloffset; + bt->rect.ymax += handle->scrolloffset; + } + } } if (block_old) { @@ -597,17 +628,15 @@ uiBlock *ui_popup_block_refresh( ui_popup_block_scrolltest(block); /* adds subwindow */ - ED_region_init(C, ar); + ED_region_init(ar); /* get winmat now that we actually have the subwindow */ - wmSubWindowSet(window, ar->swinid); - - wm_subwindow_matrix_get(window, ar->swinid, block->winmat); + wmGetProjectionMatrix(block->winmat, &ar->winrct); /* notify change and redraw */ ED_region_tag_redraw(ar); - ED_region_update_rect(C, ar); + ED_region_update_rect(ar); #ifdef DEBUG window->eventstate = event_back; @@ -646,10 +675,12 @@ uiPopupBlockHandle *ui_popup_block_create( handle->popup_create_vars.create_func = create_func; handle->popup_create_vars.handle_create_func = handle_create_func; handle->popup_create_vars.arg = arg; + handle->popup_create_vars.but = but; handle->popup_create_vars.butregion = but ? butregion : NULL; copy_v2_v2_int(handle->popup_create_vars.event_xy, &window->eventstate->x); - /* caller may free vars used to create this popup, in that case this variable should be disabled. */ - handle->can_refresh = true; + + /* don't allow by default, only if popup type explicitly supports it */ + handle->can_refresh = false; /* create area region */ ar = ui_region_temp_add(CTX_wm_screen(C)); @@ -657,6 +688,7 @@ uiPopupBlockHandle *ui_popup_block_create( memset(&type, 0, sizeof(ARegionType)); type.draw = ui_block_region_draw; + type.layout = ui_block_region_refresh; type.regionid = RGN_TYPE_TEMPORARY; ar->type = &type; @@ -666,7 +698,7 @@ uiPopupBlockHandle *ui_popup_block_create( handle = block->handle; /* keep centered on window resizing */ - if ((block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) && handle->can_refresh) { + if (block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) { type.listener = ui_block_region_popup_window_listener; } @@ -675,6 +707,25 @@ uiPopupBlockHandle *ui_popup_block_create( void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle) { + /* If this popup is created from a popover which does NOT have keep-open flag set, + * then close the popover too. We could extend this to other popup types too. */ + ARegion *ar = handle->popup_create_vars.butregion; + if (ar != NULL) { + for (uiBlock *block = ar->uiblocks.first; block; block = block->next) { + if (block->handle && + (block->flag & UI_BLOCK_POPOVER) && + (block->flag & UI_BLOCK_KEEP_OPEN) == 0) + { + uiPopupBlockHandle *menu = block->handle; + menu->menuretval = UI_RETURN_OK; + } + } + } + + if (handle->popup_create_vars.free_func) { + handle->popup_create_vars.free_func(handle, handle->popup_create_vars.arg); + } + ui_popup_block_remove(C, handle); MEM_freeN(handle); diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 8d4389c73e6..d262cbc38bd 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -64,6 +64,7 @@ #include "interface_intern.h" #include "interface_regions_intern.h" +#include "GPU_state.h" #define MENU_BORDER (int)(0.3f * U.widget_unit) @@ -397,15 +398,16 @@ int ui_searchbox_autocomplete(bContext *C, ARegion *ar, uiBut *but, char *str) return match; } -static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) +static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *ar) { uiSearchboxData *data = ar->regiondata; /* pixel space */ wmOrtho2_region_pixelspace(ar); - if (data->noback == false) - ui_draw_search_back(NULL, NULL, &data->bbox); /* style not used yet */ + if (data->noback == false) { + ui_draw_widget_back(UI_WTYPE_MENU_BACK, true, &data->bbox); + } /* draw text */ if (data->items.totitem) { @@ -415,6 +417,9 @@ static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) if (data->preview) { /* draw items */ for (a = 0; a < data->items.totitem; a++) { + /* ensure icon is up-to-date */ + ui_icon_ensure_deferred(C, data->items.icons[a], data->preview); + ui_searchbox_butrect(&rect, data, a); /* widget itself */ @@ -426,15 +431,15 @@ static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) /* indicate more */ if (data->items.more) { ui_searchbox_butrect(&rect, data, data->items.maxitem - 1); - glEnable(GL_BLEND); + GPU_blend(true); UI_icon_draw(rect.xmax - 18, rect.ymin - 7, ICON_TRIA_DOWN); - glDisable(GL_BLEND); + GPU_blend(false); } if (data->items.offset) { ui_searchbox_butrect(&rect, data, 0); - glEnable(GL_BLEND); + GPU_blend(true); UI_icon_draw(rect.xmin, rect.ymax - 9, ICON_TRIA_UP); - glDisable(GL_BLEND); + GPU_blend(false); } } @@ -452,15 +457,15 @@ static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) /* indicate more */ if (data->items.more) { ui_searchbox_butrect(&rect, data, data->items.maxitem - 1); - glEnable(GL_BLEND); + GPU_blend(true); UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN); - glDisable(GL_BLEND); + GPU_blend(false); } if (data->items.offset) { ui_searchbox_butrect(&rect, data, 0); - glEnable(GL_BLEND); + GPU_blend(true); UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP); - glDisable(GL_BLEND); + GPU_blend(false); } } } @@ -624,7 +629,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but } /* adds subwindow */ - ED_region_init(C, ar); + ED_region_init(ar); /* notify change and redraw */ ED_region_tag_redraw(ar); @@ -680,8 +685,9 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe /* pixel space */ wmOrtho2_region_pixelspace(ar); - if (data->noback == false) - ui_draw_search_back(NULL, NULL, &data->bbox); /* style not used yet */ + if (data->noback == false) { + ui_draw_widget_back(UI_WTYPE_MENU_BACK, true, &data->bbox); + } /* draw text */ if (data->items.totitem) { @@ -730,15 +736,15 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe /* indicate more */ if (data->items.more) { ui_searchbox_butrect(&rect, data, data->items.maxitem - 1); - glEnable(GL_BLEND); + GPU_blend(true); UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN); - glDisable(GL_BLEND); + GPU_blend(false); } if (data->items.offset) { ui_searchbox_butrect(&rect, data, 0); - glEnable(GL_BLEND); + GPU_blend(true); UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP); - glDisable(GL_BLEND); + GPU_blend(false); } } } diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 1eec3737215..97f501b7448 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -164,6 +164,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) uiWidgetColors *theme = ui_tooltip_get_theme(); rcti bbox = data->bbox; float tip_colors[UI_TIP_LC_MAX][3]; + unsigned char drawcol[4] = {0, 0, 0, 255}; /* to store color in while drawing (alpha is always 255) */ float *main_color = tip_colors[UI_TIP_LC_MAIN]; /* the color from the theme */ float *value_color = tip_colors[UI_TIP_LC_VALUE]; @@ -174,12 +175,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) float background_color[3]; float tone_bg; - int i, multisample_enabled; - - /* disable AA, makes widgets too blurry */ - multisample_enabled = glIsEnabled(GL_MULTISAMPLE); - if (multisample_enabled) - glDisable(GL_MULTISAMPLE); + int i; wmOrtho2_region_pixelspace(ar); @@ -232,9 +228,9 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) fstyle_header.shadowalpha = 1.0f; fstyle_header.word_wrap = true; + rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_MAIN]); UI_fontstyle_set(&fstyle_header); - glColor3fv(tip_colors[UI_TIP_LC_MAIN]); - UI_fontstyle_draw(&fstyle_header, &bbox, field->text); + UI_fontstyle_draw(&fstyle_header, &bbox, field->text, drawcol); fstyle_header.shadow = 0; @@ -245,8 +241,8 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) bbox.xmin += xofs; bbox.ymax -= yofs; - glColor3fv(tip_colors[UI_TIP_LC_ACTIVE]); - UI_fontstyle_draw(&fstyle_header, &bbox, field->text_suffix); + rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_ACTIVE]); + UI_fontstyle_draw(&fstyle_header, &bbox, field->text_suffix, drawcol); /* undo offset */ bbox.xmin -= xofs; @@ -261,8 +257,8 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) UI_fontstyle_set(&fstyle_mono); /* XXX, needed because we dont have mono in 'U.uifonts' */ BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * U.pixelsize, U.dpi); - glColor3fv(tip_colors[field->format.color_id]); - UI_fontstyle_draw(&fstyle_mono, &bbox, field->text); + rgb_float_to_uchar(drawcol, tip_colors[field->format.color_id]); + UI_fontstyle_draw(&fstyle_mono, &bbox, field->text, drawcol); } else { uiFontStyle fstyle_normal = data->fstyle; @@ -270,9 +266,9 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) fstyle_normal.word_wrap = true; /* draw remaining data */ + rgb_float_to_uchar(drawcol, tip_colors[field->format.color_id]); UI_fontstyle_set(&fstyle_normal); - glColor3fv(tip_colors[field->format.color_id]); - UI_fontstyle_draw(&fstyle_normal, &bbox, field->text); + UI_fontstyle_draw(&fstyle_normal, &bbox, field->text, drawcol); } bbox.ymax -= data->lineh * field->geom.lines; @@ -284,9 +280,6 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) BLF_disable(data->fstyle.uifont_id, BLF_WORD_WRAP); BLF_disable(blf_mono_font, BLF_WORD_WRAP); - - if (multisample_enabled) - glEnable(GL_MULTISAMPLE); } static void ui_tooltip_region_free_cb(ARegion *ar) @@ -313,6 +306,63 @@ static void ui_tooltip_region_free_cb(ARegion *ar) /** \name ToolTip Creation * \{ */ +static uiTooltipData *ui_tooltip_data_from_keymap(bContext *C, wmKeyMap *keymap) +{ + char buf[512]; + + /* create tooltip data */ + uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData"); + + for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) { + wmOperatorType *ot = WM_operatortype_find(kmi->idname, true); + if (ot != NULL) { + /* Tip */ + { + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_MAIN, + .is_pad = true, + }); + field->text = BLI_strdup(ot->description[0] ? ot->description : ot->name); + } + /* Shortcut */ + { + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_NORMAL, + }); + bool found = false; + if (WM_keymap_item_to_string(kmi, false, buf, sizeof(buf))) { + found = true; + } + field->text = BLI_sprintfN(TIP_("Shortcut: %s"), found ? buf : "None"); + } + + /* Python */ + { + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_PYTHON, + }); + char *str = WM_operator_pystring_ex(C, NULL, false, false, ot, kmi->ptr); + WM_operator_pystring_abbreviate(str, 32); + field->text = BLI_sprintfN(TIP_("Python: %s"), str); + MEM_freeN(str); + } + } + } + if (data->fields_len == 0) { + MEM_freeN(data); + return NULL; + } + else { + return data; + } +} + static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) { uiStringInfo but_tip = {BUT_GET_TIP, NULL}; @@ -463,7 +513,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) WM_operator_pystring_abbreviate(str, 32); /* operator info */ - if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) { + if (U.flag & USER_TOOLTIPS_PYTHON) { uiTooltipField *field = text_field_add( data, &(uiTooltipFormat){ .style = UI_TIP_STYLE_MONO, @@ -501,7 +551,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } } - if ((U.flag & USER_TOOLTIPS_PYTHON) == 0 && !but->optype && rna_struct.strinfo) { + if ((U.flag & USER_TOOLTIPS_PYTHON) && !but->optype && rna_struct.strinfo) { { uiTooltipField *field = text_field_add( data, &(uiTooltipFormat){ @@ -565,6 +615,112 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } } +static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz) +{ + uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData"); + + /* TODO(campbell): a way for gizmos to have their own descriptions (low priority). */ + + /* Operator Actions */ + { + bool use_drag = gz->drag_part != -1 && gz->highlight_part != gz->drag_part; + + const struct { + int part; + const char *prefix; + } mpop_actions[] = { + { + .part = gz->highlight_part, + .prefix = use_drag ? TIP_("Click") : NULL, + }, { + .part = use_drag ? gz->drag_part : -1, + .prefix = use_drag ? TIP_("Drag") : NULL, + }, + }; + + for (int i = 0; i < ARRAY_SIZE(mpop_actions); i++) { + wmGizmoOpElem *mpop = (mpop_actions[i].part != -1) ? WM_gizmo_operator_get(gz, mpop_actions[i].part) : NULL; + if (mpop != NULL) { + /* Description */ + const char *info = RNA_struct_ui_description(mpop->type->srna); + if (!(info && info[0])) { + info = RNA_struct_ui_name(mpop->type->srna); + } + + if (info && info[0]) { + char *text = NULL; + if (mpop_actions[i].prefix != NULL) { + text = BLI_sprintfN("%s: %s", mpop_actions[i].prefix, info); + } + else { + text = BLI_strdup(info); + } + + if (text != NULL) { + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_HEADER, + .color_id = UI_TIP_LC_VALUE, + .is_pad = true, + }); + field->text = text; + } + } + + /* Shortcut */ + { + bool found = false; + IDProperty *prop = mpop->ptr.data; + char buf[128]; + if (WM_key_event_operator_string( + C, mpop->type->idname, WM_OP_INVOKE_DEFAULT, prop, true, + buf, ARRAY_SIZE(buf))) + { + found = true; + } + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_VALUE, + .is_pad = true, + }); + field->text = BLI_sprintfN(TIP_("Shortcut: %s"), found ? buf : "None"); + } + } + } + } + + /* Property Actions */ + if (gz->type->target_property_defs_len) { + wmGizmoProperty *gz_prop_array = WM_gizmo_target_property_array(gz); + for (int i = 0; i < gz->type->target_property_defs_len; i++) { + /* TODO(campbell): function callback descriptions. */ + wmGizmoProperty *gz_prop = &gz_prop_array[i]; + if (gz_prop->prop != NULL) { + const char *info = RNA_property_ui_description(gz_prop->prop); + if (info && info[0]) { + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_VALUE, + .is_pad = true, + }); + field->text = BLI_strdup(info); + } + } + } + } + + if (data->fields_len == 0) { + MEM_freeN(data); + return NULL; + } + else { + return data; + } +} + + static ARegion *ui_tooltip_create_with_data( bContext *C, uiTooltipData *data, const float init_position[2], @@ -711,7 +867,7 @@ static ARegion *ui_tooltip_create_with_data( } /* adds subwindow */ - ED_region_init(C, ar); + ED_region_init(ar); /* notify change and redraw */ ED_region_tag_redraw(ar); @@ -738,6 +894,28 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b } uiTooltipData *data = NULL; + /* custom tips for pre-defined operators */ + if (but->optype) { + /* TODO(campbell): we now use 'WM_OT_tool_set_by_name', this logic will be moved into the status bar. */ + if (false && STREQ(but->optype->idname, "WM_OT_tool_set")) { + char keymap[64] = ""; + RNA_string_get(but->opptr, "keymap", keymap); + if (keymap[0]) { + ScrArea *sa = CTX_wm_area(C); + /* It happens in rare cases, for tooltips originated from the toolbar. + * It is hard to reproduce, but it happens when the mouse is nowhere near the actual tool. */ + if (sa == NULL) { + return NULL; + } + wmKeyMap *km = WM_keymap_find_all(C, keymap, sa->spacetype, RGN_TYPE_WINDOW); + if (km != NULL) { + data = ui_tooltip_data_from_keymap(C, km); + } + } + } + } + /* toolsystem exception */ + if (data == NULL) { data = ui_tooltip_data_from_button(C, but); } @@ -756,6 +934,23 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b return ui_tooltip_create_with_data(C, data, init_position, aspect); } +ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz) +{ + wmWindow *win = CTX_wm_window(C); + const float aspect = 1.0f; + float init_position[2]; + + uiTooltipData *data = ui_tooltip_data_from_gizmo(C, gz); + if (data == NULL) { + return NULL; + } + + init_position[0] = win->eventstate->x; + init_position[1] = win->eventstate->y; + + return ui_tooltip_create_with_data(C, data, init_position, aspect); +} + void UI_tooltip_free(bContext *C, bScreen *sc, ARegion *ar) { ui_region_temp_remove(C, sc, ar); diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 2c3d94214d1..0decc937e19 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -61,6 +61,9 @@ ARegion *ui_region_temp_add(bScreen *sc) void ui_region_temp_remove(bContext *C, bScreen *sc, ARegion *ar) { wmWindow *win = CTX_wm_window(C); + + BLI_assert(ar->regiontype == RGN_TYPE_TEMPORARY); + BLI_assert(BLI_findindex(&sc->regionbase, ar) != -1); if (win) wm_draw_region_clear(win, ar); diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index eabc5150424..36ad516bf7f 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -149,7 +149,7 @@ static uiFont *uifont_to_blfont(int id) void UI_fontstyle_draw_ex( - const uiFontStyle *fs, const rcti *rect, const char *str, + const uiFontStyle *fs, const rcti *rect, const char *str, const unsigned char col[4], size_t len, float *r_xofs, float *r_yofs) { int xofs = 0, yofs; @@ -196,6 +196,7 @@ void UI_fontstyle_draw_ex( /* clip is very strict, so we give it some space */ BLF_clipping(fs->uifont_id, rect->xmin - 2, rect->ymin - 4, rect->xmax + 1, rect->ymax + 4); BLF_position(fs->uifont_id, rect->xmin + xofs, rect->ymin + yofs, 0.0f); + BLF_color4ubv(fs->uifont_id, col); BLF_draw(fs->uifont_id, str, len); @@ -205,17 +206,17 @@ void UI_fontstyle_draw_ex( *r_yofs = yofs; } -void UI_fontstyle_draw(const uiFontStyle *fs, const rcti *rect, const char *str) +void UI_fontstyle_draw(const uiFontStyle *fs, const rcti *rect, const char *str, const unsigned char col[4]) { float xofs, yofs; UI_fontstyle_draw_ex( - fs, rect, str, + fs, rect, str, col, BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs); } /* drawn same as above, but at 90 degree angle */ -void UI_fontstyle_draw_rotated(const uiFontStyle *fs, const rcti *rect, const char *str) +void UI_fontstyle_draw_rotated(const uiFontStyle *fs, const rcti *rect, const char *str, const unsigned char col[4]) { float height; int xofs, yofs; @@ -249,6 +250,7 @@ void UI_fontstyle_draw_rotated(const uiFontStyle *fs, const rcti *rect, const ch BLF_enable(fs->uifont_id, BLF_ROTATION); BLF_rotation(fs->uifont_id, angle); + BLF_color4ubv(fs->uifont_id, col); if (fs->shadow) { BLF_enable(fs->uifont_id, BLF_SHADOW); @@ -275,13 +277,14 @@ void UI_fontstyle_draw_rotated(const uiFontStyle *fs, const rcti *rect, const ch * * For drawing on-screen labels. */ -void UI_fontstyle_draw_simple(const uiFontStyle *fs, float x, float y, const char *str) +void UI_fontstyle_draw_simple(const uiFontStyle *fs, float x, float y, const char *str, const unsigned char col[4]) { if (fs->kerning == 1) BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); UI_fontstyle_set(fs); BLF_position(fs->uifont_id, x, y, 0.0f); + BLF_color4ubv(fs->uifont_id, col); BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); if (fs->kerning == 1) @@ -293,7 +296,7 @@ void UI_fontstyle_draw_simple(const uiFontStyle *fs, float x, float y, const cha */ void UI_fontstyle_draw_simple_backdrop( const uiFontStyle *fs, float x, float y, const char *str, - const unsigned char fg[4], const unsigned char bg[4]) + const float col_fg[4], const float col_bg[4]) { if (fs->kerning == 1) BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); @@ -307,21 +310,19 @@ void UI_fontstyle_draw_simple_backdrop( const float margin = height / 4.0f; /* backdrop */ - glColor4ubv(bg); + float color[4] = { col_bg[0], col_bg[1], col_bg[2], 0.5f }; - UI_draw_roundbox_corner_set(UI_CNR_ALL | UI_RB_ALPHA); - UI_draw_roundbox( + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_aa(true, x - margin, (y + decent) - margin, x + width + margin, (y + decent) + height + margin, - margin); - - glColor4ubv(fg); + margin, color); } - BLF_position(fs->uifont_id, x, y, 0.0f); + BLF_color4fv(fs->uifont_id, col_fg); BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); if (fs->kerning == 1) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index daee0d3af3f..2dcc18af4ea 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -56,11 +56,12 @@ #include "BKE_colorband.h" #include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_idcode.h" #include "BKE_idprop.h" +#include "BKE_layer.h" #include "BKE_library.h" +#include "BKE_library_override.h" #include "BKE_linestyle.h" #include "BKE_main.h" #include "BKE_modifier.h" @@ -69,9 +70,11 @@ #include "BKE_particle.h" #include "BKE_paint.h" #include "BKE_report.h" -#include "BKE_sca.h" #include "BKE_screen.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + #include "ED_screen.h" #include "ED_object.h" #include "ED_render.h" @@ -91,11 +94,128 @@ // #define USE_OP_RESET_BUT // we may want to make this optional, disable for now. +/* defines for templateID/TemplateSearch */ +#define TEMPLATE_SEARCH_TEXTBUT_WIDTH (UI_UNIT_X * 6) +#define TEMPLATE_SEARCH_TEXTBUT_HEIGHT UI_UNIT_Y + void UI_template_fix_linking(void) { } +/** + * Add a block button for the search menu for templateID and templateSearch. + */ +static void template_add_button_search_menu( + const bContext *C, uiLayout *layout, uiBlock *block, + PointerRNA *ptr, PropertyRNA *prop, + uiBlockCreateFunc block_func, void *block_argN, const char * const tip, + const bool use_previews, const bool editable) +{ + PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop); + ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL; + const ID *idfrom = ptr->id.data; + const StructRNA *type = active_ptr.type ? active_ptr.type : RNA_property_pointer_type(ptr, prop); + uiBut *but; + + if (use_previews) { + ARegion *region = CTX_wm_region(C); + ScrArea *area = CTX_wm_area(C); + /* XXX ugly top-bar exception */ + const bool use_big_size = (region->regiontype != RGN_TYPE_HEADER) && (area->spacetype != SPACE_TOPBAR); /* silly check, could be more generic */ + /* Ugly exception for screens here, drawing their preview in icon size looks ugly/useless */ + const bool use_preview_icon = use_big_size || (id && (GS(id->name) != ID_SCR)); + const short width = UI_UNIT_X * (use_big_size ? 6 : 1.6f); + const short height = UI_UNIT_Y * (use_big_size ? 6 : 1); + + but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, width, height, tip); + if (use_preview_icon) { + int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type); + ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); + } + else { + ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON); + UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); + } + + if ((idfrom && idfrom->lib) || !editable) + UI_but_flag_enable(but, UI_BUT_DISABLED); + if (use_big_size) { + uiLayoutRow(layout, true); + } + } + else { + but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, tip); + ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON); + if (id) { + /* default dragging of icon for id browse buttons */ + UI_but_drag_set_id(but, id); + } + UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); + + if ((idfrom && idfrom->lib) || !editable) + UI_but_flag_enable(but, UI_BUT_DISABLED); + } +} + +static uiBlock *template_common_search_menu( + const bContext *C, ARegion *region, + uiButSearchFunc search_func, void *search_arg, + uiButHandleFunc handle_func, void *active_item, + const int preview_rows, const int preview_cols) +{ + static char search[256]; + wmWindow *win = CTX_wm_window(C); + uiBlock *block; + uiBut *but; + + /* clear initial search string, then all items show */ + search[0] = 0; + + block = UI_block_begin(C, region, "_popup", UI_EMBOSS); + UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_SEARCH_MENU); + + /* preview thumbnails */ + if (preview_rows > 0 && preview_cols > 0) { + const int w = 4 * U.widget_unit * preview_cols; + const int h = 5 * U.widget_unit * preview_rows; + + /* fake button, it holds space for search items */ + uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 26, w, h, NULL, 0, 0, 0, 0, NULL); + + but = uiDefSearchBut( + block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, w, UI_UNIT_Y, + preview_rows, preview_cols, ""); + } + /* list view */ + else { + const int searchbox_width = UI_searchbox_size_x(); + const int searchbox_height = UI_searchbox_size_y(); + + /* fake button, it holds space for search items */ + uiDefBut( + block, UI_BTYPE_LABEL, 0, "", 10, 15, searchbox_width, searchbox_height, + NULL, 0, 0, 0, 0, NULL); + but = uiDefSearchBut( + block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, + searchbox_width, UI_UNIT_Y - 1, 0, 0, ""); + } + UI_but_func_search_set( + but, ui_searchbox_create_generic, search_func, + search_arg, handle_func, active_item); + + + UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); + UI_block_direction_set(block, UI_DIR_DOWN); + + /* give search-field focus */ + UI_but_focus_on_enter_event(win, but); + /* this type of search menu requires undo */ + but->flag |= UI_BUT_UNDO; + + return block; +} + /********************** Header Template *************************/ void uiTemplateHeader(uiLayout *layout, bContext *C) @@ -120,7 +240,7 @@ typedef struct TemplateID { } TemplateID; /* Search browse menu, assign */ -static void id_search_call_cb(bContext *C, void *arg_template, void *item) +static void template_ID_set_property_cb(bContext *C, void *arg_template, void *item) { TemplateID *template_ui = (TemplateID *)arg_template; @@ -231,30 +351,25 @@ static void id_search_cb_objects_from_scene(const bContext *C, void *arg_templat } BKE_main_id_flag_listbase(lb, LIB_TAG_DOIT, false); - for (Base *base = scene->base.first; base; base = base->next) { - base->object->id.tag |= LIB_TAG_DOIT; + + FOREACH_SCENE_OBJECT_BEGIN(scene, ob_iter) + { + ob_iter->id.tag |= LIB_TAG_DOIT; } + FOREACH_SCENE_OBJECT_END; id_search_cb_tagged(C, arg_template, str, items); } /* ID Search browse menu, open */ static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem) { - static char search[256]; static TemplateID template_ui; - PointerRNA idptr; - wmWindow *win = CTX_wm_window(C); - uiBlock *block; - uiBut *but; + PointerRNA active_item_ptr; void (*id_search_cb_p)(const bContext *, void *, const char *, uiSearchItems *) = id_search_cb; - /* clear initial search string, then all items show */ - search[0] = 0; /* arg_litem is malloced, can be freed by parent button */ template_ui = *((TemplateID *)arg_litem); - - /* get active id for showing first item */ - idptr = RNA_property_pointer_get(&template_ui.ptr, template_ui.prop); + active_item_ptr = RNA_property_pointer_get(&template_ui.ptr, template_ui.prop); if (template_ui.filter) { /* Currently only used for objects. */ @@ -265,47 +380,9 @@ static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem) } } - block = UI_block_begin(C, ar, "_popup", UI_EMBOSS); - UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_SEARCH_MENU); - - /* preview thumbnails */ - if (template_ui.prv_rows > 0 && template_ui.prv_cols > 0) { - int w = 4 * U.widget_unit * template_ui.prv_cols; - int h = 5 * U.widget_unit * template_ui.prv_rows; - - /* fake button, it holds space for search items */ - uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 26, w, h, NULL, 0, 0, 0, 0, NULL); - - but = uiDefSearchBut( - block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, w, UI_UNIT_Y, - template_ui.prv_rows, template_ui.prv_cols, ""); - UI_but_func_search_set( - but, ui_searchbox_create_generic, id_search_cb_p, - &template_ui, id_search_call_cb, idptr.data); - } - /* list view */ - else { - const int searchbox_width = UI_searchbox_size_x(); - const int searchbox_height = UI_searchbox_size_y(); - - /* fake button, it holds space for search items */ - uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 15, searchbox_width, searchbox_height, NULL, 0, 0, 0, 0, NULL); - but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, searchbox_width, UI_UNIT_Y - 1, 0, 0, ""); - UI_but_func_search_set( - but, ui_searchbox_create_generic, id_search_cb_p, - &template_ui, id_search_call_cb, idptr.data); - } - - - UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); - UI_block_direction_set(block, UI_DIR_DOWN); - - /* give search-field focus */ - UI_but_focus_on_enter_event(win, but); - /* this type of search menu requires undo */ - but->flag |= UI_BUT_UNDO; - - return block; + return template_common_search_menu( + C, ar, id_search_cb_p, &template_ui, template_ID_set_property_cb, active_item_ptr.data, + template_ui.prv_rows, template_ui.prv_cols); } /************************ ID Template ***************************/ @@ -313,36 +390,22 @@ static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem) /* for new/open operators */ void UI_context_active_but_prop_get_templateID( - bContext *C, - PointerRNA *r_ptr, PropertyRNA **r_prop) + bContext *C, + PointerRNA *r_ptr, PropertyRNA **r_prop) { TemplateID *template_ui; - ARegion *ar = CTX_wm_region(C); - uiBlock *block; - uiBut *but; + uiBut *but = UI_context_active_but_get(C); memset(r_ptr, 0, sizeof(*r_ptr)); *r_prop = NULL; - if (!ar) - return; - - for (block = ar->uiblocks.first; block; block = block->next) { - for (but = block->buttons.first; but; but = but->next) { - /* find the button before the active one */ - if ((but->flag & (UI_BUT_LAST_ACTIVE | UI_ACTIVE))) { - if (but->func_argN) { - template_ui = but->func_argN; - *r_ptr = template_ui->ptr; - *r_prop = template_ui->prop; - return; - } - } - } + if (but && but->func_argN) { + template_ui = but->func_argN; + *r_ptr = template_ui->ptr; + *r_prop = template_ui->prop; } } - static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) { TemplateID *template_ui = (TemplateID *)arg_litem; @@ -383,14 +446,34 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) case UI_ID_LOCAL: if (id) { Main *bmain = CTX_data_main(C); - if (id_make_local(bmain, id, false, false)) { - BKE_main_id_clear_newpoins(bmain); + if (CTX_wm_window(C)->eventstate->shift) { + ID *override_id = BKE_override_static_create_from_id(bmain, id); + if (override_id != NULL) { + BKE_main_id_clear_newpoins(bmain); + + /* Assign new pointer, takes care of updates/notifiers */ + RNA_id_pointer_create(override_id, &idptr); + } + } + else { + if (id_make_local(bmain, id, false, false)) { + BKE_main_id_clear_newpoins(bmain); - /* reassign to get get proper updates/notifiers */ - idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); - RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr); - RNA_property_update(C, &template_ui->ptr, template_ui->prop); + /* reassign to get get proper updates/notifiers */ + idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); + } } + RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr); + RNA_property_update(C, &template_ui->ptr, template_ui->prop); + } + break; + case UI_ID_OVERRIDE: + if (id && id->override_static) { + BKE_override_static_free(&id->override_static); + /* reassign to get get proper updates/notifiers */ + idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); + RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr); + RNA_property_update(C, &template_ui->ptr, template_ui->prop); } break; case UI_ID_ALONE: @@ -404,14 +487,15 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ED_object_single_user(bmain, scene, (struct Object *)id); + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); - DAG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); } else { if (id) { Main *bmain = CTX_data_main(C); id_single_user(C, id, &template_ui->ptr, template_ui->prop); - DAG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); } } } @@ -423,7 +507,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) } } -static const char *template_id_browse_tip(StructRNA *type) +static const char *template_id_browse_tip(const StructRNA *type) { if (type) { switch (RNA_type_to_ID_code(type)) { @@ -437,7 +521,7 @@ static const char *template_id_browse_tip(StructRNA *type) case ID_IM: return N_("Browse Image to be linked"); case ID_LS: return N_("Browse Line Style Data to be linked"); case ID_LT: return N_("Browse Lattice Data to be linked"); - case ID_LA: return N_("Browse Lamp Data to be linked"); + case ID_LA: return N_("Browse Light Data to be linked"); case ID_CA: return N_("Browse Camera Data to be linked"); case ID_WO: return N_("Browse World Settings to be linked"); case ID_SCR: return N_("Choose Screen layout"); @@ -455,6 +539,8 @@ static const char *template_id_browse_tip(StructRNA *type) case ID_PAL: return N_("Browse Palette Data to be linked"); case ID_PC: return N_("Browse Paint Curve Data to be linked"); case ID_CF: return N_("Browse Cache Files to be linked"); + case ID_WS: return N_("Browse Workspace to be linked"); + case ID_LP: return N_("Browse LightProbe to be linked"); } } return N_("Browse ID data to be linked"); @@ -474,6 +560,74 @@ static const char *template_id_context(StructRNA *type) } #endif +static uiBut *template_id_def_new_but( + uiBlock *block, const ID *id, const TemplateID *template_ui, StructRNA *type, + const char * const newop, const bool editable, const bool id_open, const bool use_tab_but, + int but_height) +{ + ID *idfrom = template_ui->ptr.id.data; + uiBut *but; + const int w = id ? UI_UNIT_X : id_open ? UI_UNIT_X * 3 : UI_UNIT_X * 6; + const int but_type = use_tab_but ? UI_BTYPE_TAB : UI_BTYPE_BUT; + + /* i18n markup, does nothing! */ + BLT_I18N_MSGID_MULTI_CTXT( + "New", + BLT_I18NCONTEXT_DEFAULT, + BLT_I18NCONTEXT_ID_SCENE, + BLT_I18NCONTEXT_ID_OBJECT, + BLT_I18NCONTEXT_ID_MESH, + BLT_I18NCONTEXT_ID_CURVE, + BLT_I18NCONTEXT_ID_METABALL, + BLT_I18NCONTEXT_ID_MATERIAL, + BLT_I18NCONTEXT_ID_TEXTURE, + BLT_I18NCONTEXT_ID_IMAGE, + BLT_I18NCONTEXT_ID_LATTICE, + BLT_I18NCONTEXT_ID_LAMP, + BLT_I18NCONTEXT_ID_CAMERA, + BLT_I18NCONTEXT_ID_WORLD, + BLT_I18NCONTEXT_ID_SCREEN, + BLT_I18NCONTEXT_ID_TEXT, + ); + BLT_I18N_MSGID_MULTI_CTXT( + "New", + BLT_I18NCONTEXT_ID_SPEAKER, + BLT_I18NCONTEXT_ID_SOUND, + BLT_I18NCONTEXT_ID_ARMATURE, + BLT_I18NCONTEXT_ID_ACTION, + BLT_I18NCONTEXT_ID_NODETREE, + BLT_I18NCONTEXT_ID_BRUSH, + BLT_I18NCONTEXT_ID_PARTICLESETTINGS, + BLT_I18NCONTEXT_ID_GPENCIL, + BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, + BLT_I18NCONTEXT_ID_WORKSPACE, + BLT_I18NCONTEXT_ID_LIGHTPROBE, + ); + + if (newop) { + but = uiDefIconTextButO( + block, but_type, newop, WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN, + (id) ? "" : CTX_IFACE_(template_id_context(type), "New"), 0, 0, w, but_height, NULL); + UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_ADD_NEW)); + } + else { + but = uiDefIconTextBut( + block, but_type, 0, ICON_ZOOMIN, (id) ? "" : CTX_IFACE_(template_id_context(type), "New"), + 0, 0, w, but_height, NULL, 0, 0, 0, 0, NULL); + UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_ADD_NEW)); + } + + if ((idfrom && idfrom->lib) || !editable) { + UI_but_flag_enable(but, UI_BUT_DISABLED); + } + +#ifndef WITH_INTERNATIONAL + UNUSED_VARS(type); +#endif + + return but; +} + static void template_ID( bContext *C, uiLayout *layout, TemplateID *template_ui, StructRNA *type, int flag, const char *newop, const char *openop, const char *unlinkop) @@ -484,6 +638,7 @@ static void template_ID( // ListBase *lb; // UNUSED ID *id, *idfrom; const bool editable = RNA_property_editable(&template_ui->ptr, template_ui->prop); + const bool use_previews = template_ui->preview = (flag & UI_ID_PREVIEWS) != 0; idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); id = idptr.data; @@ -496,32 +651,11 @@ static void template_ID( if (idptr.type) type = idptr.type; - if (flag & UI_ID_PREVIEWS) { - template_ui->preview = true; - - but = uiDefBlockButN( - block, id_search_menu, MEM_dupallocN(template_ui), "", 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6, - TIP_(template_id_browse_tip(type))); - ui_def_but_icon( - but, id ? ui_id_icon_get(C, id, true) : RNA_struct_ui_icon(type), - UI_HAS_ICON | UI_BUT_ICON_PREVIEW); - - if ((idfrom && idfrom->lib) || !editable) - UI_but_flag_enable(but, UI_BUT_DISABLED); - - uiLayoutRow(layout, true); - } - else if (flag & UI_ID_BROWSE) { - but = uiDefBlockButN( - block, id_search_menu, MEM_dupallocN(template_ui), "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, - TIP_(template_id_browse_tip(type))); - ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON); - /* default dragging of icon for id browse buttons */ - UI_but_drag_set_id(but, id); - UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); - - if ((idfrom && idfrom->lib) || !editable) - UI_but_flag_enable(but, UI_BUT_DISABLED); + if (flag & UI_ID_BROWSE) { + template_add_button_search_menu( + C, layout, block, &template_ui->ptr, template_ui->prop, + id_search_menu, MEM_dupallocN(template_ui), TIP_(template_id_browse_tip(type)), + use_previews, editable); } /* text button with name */ @@ -532,7 +666,7 @@ static void template_ID( //text_idbutton(id, name); name[0] = '\0'; but = uiDefButR( - block, UI_BTYPE_TEXT, 0, name, 0, 0, UI_UNIT_X * 6, UI_UNIT_Y, + block, UI_BTYPE_TEXT, 0, name, 0, 0, TEMPLATE_SEARCH_TEXTBUT_WIDTH, TEMPLATE_SEARCH_TEXTBUT_HEIGHT, &idptr, "name", -1, 0, 0, -1, -1, RNA_struct_ui_description(type)); UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_RENAME)); if (user_alert) UI_but_flag_enable(but, UI_BUT_REDALERT); @@ -545,14 +679,28 @@ static void template_ID( UI_but_flag_enable(but, UI_BUT_DISABLED); } else { + const bool disabled = ( + !id_make_local(CTX_data_main(C), id, true /* test */, false) || + (idfrom && idfrom->lib)); but = uiDefIconBut( block, UI_BTYPE_BUT, 0, ICON_LIBRARY_DATA_DIRECT, 0, 0, UI_UNIT_X, UI_UNIT_Y, - NULL, 0, 0, 0, 0, TIP_("Direct linked library data-block, click to make local")); - if (!id_make_local(CTX_data_main(C), id, true /* test */, false) || (idfrom && idfrom->lib)) + NULL, 0, 0, 0, 0, + TIP_("Direct linked library data-block, click to make local, " + "Shift + Click to create a static override")); + if (disabled) { UI_but_flag_enable(but, UI_BUT_DISABLED); + } + else { + UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_LOCAL)); + } } - - UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_LOCAL)); + } + else if (ID_IS_STATIC_OVERRIDE(id)) { + but = uiDefIconBut( + block, UI_BTYPE_BUT, 0, ICON_LIBRARY_DATA_OVERRIDE, 0, 0, UI_UNIT_X, UI_UNIT_Y, + NULL, 0, 0, 0, 0, + TIP_("Static override of linked library data-block, click to make fully local")); + UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_OVERRIDE)); } if (id->us > 1) { @@ -581,61 +729,13 @@ static void template_ID( if (user_alert) UI_but_flag_enable(but, UI_BUT_REDALERT); - if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB))) { + if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) { uiDefButR(block, UI_BTYPE_TOGGLE, 0, "F", 0, 0, UI_UNIT_X, UI_UNIT_Y, &idptr, "use_fake_user", -1, 0, 0, -1, -1, NULL); } } if (flag & UI_ID_ADD_NEW) { - int w = id ? UI_UNIT_X : (flag & UI_ID_OPEN) ? UI_UNIT_X * 3 : UI_UNIT_X * 6; - - /* i18n markup, does nothing! */ - BLT_I18N_MSGID_MULTI_CTXT( - "New", - BLT_I18NCONTEXT_DEFAULT, - BLT_I18NCONTEXT_ID_SCENE, - BLT_I18NCONTEXT_ID_OBJECT, - BLT_I18NCONTEXT_ID_MESH, - BLT_I18NCONTEXT_ID_CURVE, - BLT_I18NCONTEXT_ID_METABALL, - BLT_I18NCONTEXT_ID_MATERIAL, - BLT_I18NCONTEXT_ID_TEXTURE, - BLT_I18NCONTEXT_ID_IMAGE, - BLT_I18NCONTEXT_ID_LATTICE, - BLT_I18NCONTEXT_ID_LAMP, - BLT_I18NCONTEXT_ID_CAMERA, - BLT_I18NCONTEXT_ID_WORLD, - BLT_I18NCONTEXT_ID_SCREEN, - BLT_I18NCONTEXT_ID_TEXT, - ); - BLT_I18N_MSGID_MULTI_CTXT( - "New", - BLT_I18NCONTEXT_ID_SPEAKER, - BLT_I18NCONTEXT_ID_SOUND, - BLT_I18NCONTEXT_ID_ARMATURE, - BLT_I18NCONTEXT_ID_ACTION, - BLT_I18NCONTEXT_ID_NODETREE, - BLT_I18NCONTEXT_ID_BRUSH, - BLT_I18NCONTEXT_ID_PARTICLESETTINGS, - BLT_I18NCONTEXT_ID_GPENCIL, - BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, - ); - - if (newop) { - but = uiDefIconTextButO( - block, UI_BTYPE_BUT, newop, WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN, - (id) ? "" : CTX_IFACE_(template_id_context(type), "New"), 0, 0, w, UI_UNIT_Y, NULL); - UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_ADD_NEW)); - } - else { - but = uiDefIconTextBut( - block, UI_BTYPE_BUT, 0, ICON_ZOOMIN, (id) ? "" : CTX_IFACE_(template_id_context(type), "New"), - 0, 0, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); - UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_ADD_NEW)); - } - - if ((idfrom && idfrom->lib) || !editable) - UI_but_flag_enable(but, UI_BUT_DISABLED); + template_id_def_new_but(block, id, template_ui, type, newop, editable, flag & UI_ID_OPEN, false, UI_UNIT_X); } /* Due to space limit in UI - skip the "open" icon for packed data, and allow to unpack. @@ -708,9 +808,59 @@ static void template_ID( UI_block_align_end(block); } +static void template_ID_tabs( + bContext *C, uiLayout *layout, TemplateID *template, StructRNA *type, int flag, + const char *newop, const char *UNUSED(openop), const char *unlinkop) +{ + const ARegion *region = CTX_wm_region(C); + const PointerRNA active_ptr = RNA_property_pointer_get(&template->ptr, template->prop); + const int but_align = (region->alignment == RGN_ALIGN_TOP) ? UI_BUT_ALIGN_DOWN : UI_BUT_ALIGN_TOP; + const int but_height = UI_UNIT_Y * 1.1; + + uiBlock *block = uiLayoutGetBlock(layout); + uiStyle *style = UI_style_get_dpi(); + + + for (ID *id = template->idlb->first; id; id = id->next) { + wmOperatorType *unlink_ot = WM_operatortype_find(unlinkop, false); + const bool is_active = active_ptr.data == id; + const unsigned int but_width = ( + UI_fontstyle_string_width(&style->widgetlabel, id->name + 2) + UI_UNIT_X + + (is_active ? ICON_DEFAULT_WIDTH_SCALE : 0)); + uiButTab *tab; + + tab = (uiButTab *)uiDefButR_prop( + block, UI_BTYPE_TAB, 0, "", 0, 0, but_width, UI_UNIT_Y * 1.1, + &template->ptr, template->prop, 0, 0.0f, + sizeof(id->name) - 2, 0.0f, 0.0f, ""); + UI_but_funcN_set(&tab->but, template_ID_set_property_cb, MEM_dupallocN(template), id); + tab->but.custom_data = (void *)id; + tab->unlink_ot = unlink_ot; + + if (is_active) { + UI_but_flag_enable(&tab->but, UI_BUT_VALUE_CLEAR); + } + UI_but_drawflag_enable(&tab->but, but_align); + } + + if (flag & UI_ID_ADD_NEW) { + const bool editable = RNA_property_editable(&template->ptr, template->prop); + uiBut *but; + + if (active_ptr.type) { + type = active_ptr.type; + } + + but = template_id_def_new_but(block, active_ptr.data, template, type, newop, editable, flag & UI_ID_OPEN, true, but_height); + UI_but_drawflag_enable(but, but_align); + } +} + static void ui_template_id( - uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, - const char *openop, const char *unlinkop, int flag, int prv_rows, int prv_cols, int filter) + uiLayout *layout, bContext *C, + PointerRNA *ptr, const char *propname, + const char *newop, const char *openop, const char *unlinkop, + int flag, int prv_rows, int prv_cols, int filter, bool use_tabs) { TemplateID *template_ui; PropertyRNA *prop; @@ -751,8 +901,14 @@ static void ui_template_id( * - template_ID makes a copy of the template data and assigns it to the relevant buttons */ if (template_ui->idlb) { - uiLayoutRow(layout, true); - template_ID(C, layout, template_ui, type, flag, newop, openop, unlinkop); + if (use_tabs) { + uiLayoutRow(layout, true); + template_ID_tabs(C, layout, template_ui, type, flag, newop, openop, unlinkop); + } + else { + uiLayoutRow(layout, true); + template_ID(C, layout, template_ui, type, flag, newop, openop, unlinkop); + } } MEM_freeN(template_ui); @@ -763,15 +919,21 @@ void uiTemplateID( const char *openop, const char *unlinkop, int filter) { ui_template_id( - layout, C, ptr, propname, newop, openop, unlinkop, - UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE, 0, 0, filter); + layout, C, ptr, propname, + newop, openop, unlinkop, + UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE, + 0, 0, filter, false); } void uiTemplateIDBrowse( uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, const char *openop, const char *unlinkop, int filter) { - ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME, 0, 0, filter); + ui_template_id( + layout, C, ptr, propname, + newop, openop, unlinkop, + UI_ID_BROWSE | UI_ID_RENAME, + 0, 0, filter, false); } void uiTemplateIDPreview( @@ -779,8 +941,26 @@ void uiTemplateIDPreview( const char *openop, const char *unlinkop, int rows, int cols, int filter) { ui_template_id( - layout, C, ptr, propname, newop, openop, unlinkop, - UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE | UI_ID_PREVIEWS, rows, cols, filter); + layout, C, ptr, propname, + newop, openop, unlinkop, + UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE | UI_ID_PREVIEWS, + rows, cols, filter, false); +} + +/** + * Version of #uiTemplateID using tabs. + */ +void uiTemplateIDTabs( + uiLayout *layout, bContext *C, + PointerRNA *ptr, const char *propname, + const char *newop, const char *openop, const char *unlinkop, + int filter) +{ + ui_template_id( + layout, C, ptr, propname, + newop, openop, unlinkop, + UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE, + 0, 0, filter, true); } /************************ ID Chooser Template ***************************/ @@ -842,6 +1022,209 @@ void uiTemplateAnyID( uiItemFullR(sub, ptr, propID, 0, 0, 0, "", ICON_NONE); } +/********************* Search Template ********************/ + +typedef struct TemplateSearch { + uiRNACollectionSearch search_data; + + bool use_previews; + int preview_rows, preview_cols; +} TemplateSearch; + +static void template_search_handle_cb(bContext *C, void *arg_template, void *item) +{ + TemplateSearch *template_search = arg_template; + uiRNACollectionSearch *coll_search = &template_search->search_data; + StructRNA *type = RNA_property_pointer_type(&coll_search->target_ptr, coll_search->target_prop); + PointerRNA item_ptr; + + RNA_pointer_create(NULL, type, item, &item_ptr); + RNA_property_pointer_set(&coll_search->target_ptr, coll_search->target_prop, item_ptr); + RNA_property_update(C, &coll_search->target_ptr, coll_search->target_prop); +} + +static uiBlock *template_search_menu(bContext *C, ARegion *region, void *arg_template) +{ + static TemplateSearch template_search; + PointerRNA active_ptr; + + /* arg_template is malloced, can be freed by parent button */ + template_search = *((TemplateSearch *)arg_template); + active_ptr = RNA_property_pointer_get( + &template_search.search_data.target_ptr, + template_search.search_data.target_prop); + + return template_common_search_menu( + C, region, ui_rna_collection_search_cb, &template_search, + template_search_handle_cb, active_ptr.data, + template_search.preview_rows, template_search.preview_cols); +} + +static void template_search_add_button_searchmenu( + const bContext *C, uiLayout *layout, uiBlock *block, + TemplateSearch *template_search, const bool editable) +{ + const char *ui_description = RNA_property_ui_description(template_search->search_data.target_prop); + + template_add_button_search_menu( + C, layout, block, + &template_search->search_data.target_ptr, template_search->search_data.target_prop, + template_search_menu, MEM_dupallocN(template_search), ui_description, + template_search->use_previews, editable); +} + +static void template_search_add_button_name( + uiBlock *block, PointerRNA *active_ptr, const StructRNA *type) +{ + uiDefAutoButR( + block, active_ptr, RNA_struct_name_property(type), 0, "", ICON_NONE, + 0, 0, TEMPLATE_SEARCH_TEXTBUT_WIDTH, TEMPLATE_SEARCH_TEXTBUT_HEIGHT); +} + +static void template_search_add_button_operator( + uiBlock *block, const char * const operator_name, + const int opcontext, const int icon, const bool editable) +{ + if (!operator_name) { + return; + } + + uiBut *but = uiDefIconButO( + block, UI_BTYPE_BUT, operator_name, opcontext, icon, + 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL); + + if (!editable) { + UI_but_drawflag_enable(but, UI_BUT_DISABLED); + } +} + +static void template_search_buttons( + const bContext *C, uiLayout *layout, TemplateSearch *template_search, + const char *newop, const char *unlinkop) +{ + uiBlock *block = uiLayoutGetBlock(layout); + uiRNACollectionSearch *search_data = &template_search->search_data; + StructRNA *type = RNA_property_pointer_type(&search_data->target_ptr, search_data->target_prop); + const bool editable = RNA_property_editable(&search_data->target_ptr, search_data->target_prop); + PointerRNA active_ptr = RNA_property_pointer_get(&search_data->target_ptr, search_data->target_prop); + + if (active_ptr.type) { + /* can only get correct type when there is an active item */ + type = active_ptr.type; + } + + uiLayoutRow(layout, true); + UI_block_align_begin(block); + + template_search_add_button_searchmenu(C, layout, block, template_search, editable); + template_search_add_button_name(block, &active_ptr, type); + template_search_add_button_operator(block, newop, WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN, editable); + template_search_add_button_operator(block, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, editable); + + UI_block_align_end(block); +} + +static PropertyRNA *template_search_get_searchprop( + PointerRNA *targetptr, PropertyRNA *targetprop, + PointerRNA *searchptr, const char * const searchpropname) +{ + PropertyRNA *searchprop; + + if (searchptr && !searchptr->data) { + searchptr = NULL; + } + + if (!searchptr && !searchpropname) { + /* both NULL means we don't use a custom rna collection to search in */ + } + else if (!searchptr && searchpropname) { + RNA_warning("searchpropname defined (%s) but searchptr is missing", searchpropname); + } + else if (searchptr && !searchpropname) { + RNA_warning("searchptr defined (%s) but searchpropname is missing", RNA_struct_identifier(searchptr->type)); + } + else if (!(searchprop = RNA_struct_find_property(searchptr, searchpropname))) { + RNA_warning("search collection property not found: %s.%s", + RNA_struct_identifier(searchptr->type), searchpropname); + } + else if (RNA_property_type(searchprop) != PROP_COLLECTION) { + RNA_warning("search collection property is not a collection type: %s.%s", + RNA_struct_identifier(searchptr->type), searchpropname); + } + /* check if searchprop has same type as targetprop */ + else if (RNA_property_pointer_type(searchptr, searchprop) != RNA_property_pointer_type(targetptr, targetprop)) { + RNA_warning("search collection items from %s.%s are not of type %s", + RNA_struct_identifier(searchptr->type), searchpropname, + RNA_struct_identifier(RNA_property_pointer_type(targetptr, targetprop))); + } + else { + return searchprop; + } + + return NULL; +} + +static TemplateSearch *template_search_setup( + PointerRNA *ptr, const char * const propname, + PointerRNA *searchptr, const char * const searchpropname) +{ + TemplateSearch *template_search; + PropertyRNA *prop, *searchprop; + + prop = RNA_struct_find_property(ptr, propname); + + if (!prop || RNA_property_type(prop) != PROP_POINTER) { + RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname); + return NULL; + } + searchprop = template_search_get_searchprop(ptr, prop, searchptr, searchpropname); + + template_search = MEM_callocN(sizeof(*template_search), __func__); + template_search->search_data.target_ptr = *ptr; + template_search->search_data.target_prop = prop; + template_search->search_data.search_ptr = *searchptr; + template_search->search_data.search_prop = searchprop; + + return template_search; +} + +/** + * Search menu to pick an item from a collection. + * A version of uiTemplateID that works for non-ID types. + */ +void uiTemplateSearch( + uiLayout *layout, bContext *C, + PointerRNA *ptr, const char *propname, + PointerRNA *searchptr, const char *searchpropname, + const char *newop, const char *unlinkop) +{ + TemplateSearch *template_search = template_search_setup(ptr, propname, searchptr, searchpropname); + if (template_search != NULL) { + template_search_buttons(C, layout, template_search, newop, unlinkop); + MEM_freeN(template_search); + } +} + +void uiTemplateSearchPreview( + uiLayout *layout, bContext *C, + PointerRNA *ptr, const char *propname, + PointerRNA *searchptr, const char *searchpropname, + const char *newop, const char *unlinkop, + const int rows, const int cols) +{ + TemplateSearch *template_search = template_search_setup(ptr, propname, searchptr, searchpropname); + + if (template_search != NULL) { + template_search->use_previews = true; + template_search->preview_rows = rows; + template_search->preview_cols = cols; + + template_search_buttons(C, layout, template_search, newop, unlinkop); + + MEM_freeN(template_search); + } +} + /********************* RNA Path Builder Template ********************/ /* ---------- */ @@ -896,7 +1279,7 @@ static void modifiers_convertToReal(bContext *C, void *ob_v, void *md_v) ob->partype = PAROBJECT; WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); ED_undo_push(C, "Modifier convert to real"); } @@ -979,8 +1362,7 @@ static uiLayout *draw_modifier( UI_block_emboss_set(block, UI_EMBOSS); /* modifier name */ - md->scene = scene; - if (mti->isDisabled && mti->isDisabled(md, 0)) { + if (mti->isDisabled && mti->isDisabled(scene, md, 0)) { uiLayoutSetRedAlert(row, true); } uiItemR(row, &ptr, "name", 0, "", ICON_NONE); @@ -1041,8 +1423,7 @@ static uiLayout *draw_modifier( UI_block_emboss_set(block, UI_EMBOSS_NONE); /* When Modifier is a simulation, show button to switch to context rather than the delete button. */ if (modifier_can_delete(md) && - (!modifier_is_simulation(md) || - STREQ(scene->r.engine, RE_engine_id_BLENDER_GAME))) + !modifier_is_simulation(md)) { uiItemO(row, "", ICON_X, "OBJECT_OT_modifier_remove"); } @@ -1080,13 +1461,15 @@ static uiLayout *draw_modifier( } else { uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT); - uiItemEnumO(row, "OBJECT_OT_modifier_apply", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"), - 0, "apply_as", MODIFIER_APPLY_DATA); + uiItemEnumO( + row, "OBJECT_OT_modifier_apply", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"), + 0, "apply_as", MODIFIER_APPLY_DATA); if (modifier_isSameTopology(md) && !modifier_isNonGeometrical(md)) { - uiItemEnumO(row, "OBJECT_OT_modifier_apply", - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply as Shape Key"), - 0, "apply_as", MODIFIER_APPLY_SHAPE); + uiItemEnumO( + row, "OBJECT_OT_modifier_apply", + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply as Shape Key"), + 0, "apply_as", MODIFIER_APPLY_SHAPE); } } @@ -1156,6 +1539,74 @@ uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr) return NULL; } + +/************************ Redo Buttons Template *************************/ + +static bool template_operator_redo_property_buts_poll(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +{ + return (RNA_property_tags(prop) & OP_PROP_TAG_ADVANCED) == 0; +} + +static void template_operator_redo_property_buts_draw( + const bContext *C, wmOperator *op, + uiLayout *layout, int layout_flags, + bool *r_has_advanced) +{ + if (op->type->flag & OPTYPE_MACRO) { + for (wmOperator *macro_op = op->macro.first; macro_op; macro_op = macro_op->next) { + template_operator_redo_property_buts_draw(C, macro_op, layout, layout_flags, r_has_advanced); + } + } + else { + /* Might want to make label_align adjustable somehow. */ + eAutoPropButsReturn return_info = uiTemplateOperatorPropertyButs( + C, layout, op, r_has_advanced ? template_operator_redo_property_buts_poll : NULL, + UI_BUT_LABEL_ALIGN_NONE, layout_flags); + if (return_info & UI_PROP_BUTS_ANY_FAILED_CHECK) { + if (r_has_advanced) { + *r_has_advanced = true; + } + } + } +} + +void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C) +{ + wmOperator *op = WM_operator_last_redo(C); + uiBlock *block = uiLayoutGetBlock(layout); + + if (op == NULL) { + return; + } + + /* Disable for now, doesn't fit well in popover. */ +#if 0 + /* Repeat button with operator name as text. */ + uiItemFullO(layout, "SCREEN_OT_repeat_last", RNA_struct_ui_name(op->type->srna), + ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, NULL); +#endif + + if (WM_operator_repeat_check(C, op)) { + int layout_flags = 0; + if (block->panel == NULL) { + layout_flags = UI_TEMPLATE_OP_PROPS_SHOW_TITLE; + } +#if 0 + bool has_advanced = false; +#endif + + UI_block_func_set(block, ED_undo_operator_repeat_cb, op, NULL); + template_operator_redo_property_buts_draw(C, op, layout, layout_flags, NULL /* &has_advanced */ ); + UI_block_func_set(block, NULL, NULL, NULL); /* may want to reset to old state instead of NULLing all */ + +#if 0 + if (has_advanced) { + uiItemO(layout, IFACE_("More..."), ICON_NONE, "SCREEN_OT_redo_last"); + } +#endif + } +} + /************************ Constraint Template *************************/ #include "DNA_constraint_types.h" @@ -1179,7 +1630,7 @@ static void do_constraint_panels(bContext *C, void *ob_pt, int event) Main *bmain = CTX_data_main(C); if (ob->pose) BKE_pose_tag_recalc(bmain, ob->pose); /* checks & sorts pose channels */ - DAG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); break; } #endif @@ -1193,8 +1644,8 @@ static void do_constraint_panels(bContext *C, void *ob_pt, int event) * object_test_constraints(ob); * if (ob->pose) BKE_pose_update_constraint_flags(ob->pose); */ - if (ob->type == OB_ARMATURE) DAG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB); - else DAG_id_tag_update(&ob->id, OB_RECALC_OB); + if (ob->type == OB_ARMATURE) DEG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB); + else DEG_id_tag_update(&ob->id, OB_RECALC_OB); WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob); } @@ -1305,7 +1756,7 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con) /* enabled */ UI_block_emboss_set(block, UI_EMBOSS_NONE); uiItemR(row, &ptr, "mute", 0, "", - (con->flag & CONSTRAINT_OFF) ? ICON_RESTRICT_VIEW_ON : ICON_RESTRICT_VIEW_OFF); + (con->flag & CONSTRAINT_OFF) ? ICON_HIDE_ON : ICON_HIDE_OFF); UI_block_emboss_set(block, UI_EMBOSS); uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT); @@ -1415,7 +1866,7 @@ void uiTemplatePreview( char _preview_id[UI_MAX_NAME_STR]; if (id && !ELEM(GS(id->name), ID_MA, ID_TE, ID_WO, ID_LA, ID_LS)) { - RNA_warning("Expected ID of type material, texture, lamp, world or line style"); + RNA_warning("Expected ID of type material, texture, light, world or line style"); return; } @@ -1506,7 +1957,7 @@ void uiTemplatePreview( pr_texture, 10, TEX_PR_OTHER, 0, 0, ""); } else if (GS(parent->name) == ID_LA) { - uiDefButS(block, UI_BTYPE_ROW, B_MATPRV, IFACE_("Lamp"), 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, + uiDefButS(block, UI_BTYPE_ROW, B_MATPRV, IFACE_("Light"), 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, pr_texture, 10, TEX_PR_OTHER, 0, 0, ""); } else if (GS(parent->name) == ID_WO) { @@ -1746,6 +2197,19 @@ void uiTemplateColorRamp(uiLayout *layout, PointerRNA *ptr, const char *propname MEM_freeN(cb); } +/********************* Icon Template ************************/ +/** + * \param icon_scale: Scale of the icon, 1x == button height. + */ +void uiTemplateIcon(uiLayout *layout, int icon_value, float icon_scale) +{ + uiBlock *block; + uiBut *but; + + block = uiLayoutAbsoluteBlock(layout); + but = uiDefIconBut(block, UI_BTYPE_LABEL, 0, ICON_X, 0, 0, UI_UNIT_X * icon_scale, UI_UNIT_Y * icon_scale, NULL, 0.0, 0.0, 0.0, 0.0, ""); + ui_def_but_icon(but, icon_value, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); +} /********************* Icon viewer Template ************************/ typedef struct IconViewMenuArgs { @@ -1832,13 +2296,20 @@ void uiTemplateIconView(uiLayout *layout, PointerRNA *ptr, const char *propname, value = RNA_property_enum_get(ptr, prop); RNA_enum_icon_from_value(items, value, &icon); - cb_args = MEM_callocN(sizeof(IconViewMenuArgs), __func__); - cb_args->ptr = *ptr; - cb_args->prop = prop; - cb_args->show_labels = show_labels; - cb_args->icon_scale = icon_scale; - but = uiDefBlockButN(block, ui_icon_view_menu_cb, cb_args, "", 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6, ""); + if (RNA_property_editable(ptr, prop)) { + cb_args = MEM_callocN(sizeof(IconViewMenuArgs), __func__); + cb_args->ptr = *ptr; + cb_args->prop = prop; + cb_args->show_labels = show_labels; + cb_args->icon_scale = icon_scale; + + but = uiDefBlockButN(block, ui_icon_view_menu_cb, cb_args, "", 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6, ""); + } + else { + but = uiDefIconBut(block, UI_BTYPE_LABEL, 0, ICON_X, 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6, NULL, 0.0, 0.0, 0.0, 0.0, ""); + } + ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); @@ -2687,78 +3158,6 @@ void uiTemplateLayers( } } -void uiTemplateGameStates( - uiLayout *layout, PointerRNA *ptr, const char *propname, - PointerRNA *used_ptr, const char *used_propname, int active_state) -{ - uiLayout *uRow, *uCol; - PropertyRNA *prop, *used_prop = NULL; - int groups, cols, states; - int group, col, state, row; - int cols_per_group = 5; - Object *ob = (Object *)ptr->id.data; - - prop = RNA_struct_find_property(ptr, propname); - if (!prop) { - RNA_warning("states property not found: %s.%s", RNA_struct_identifier(ptr->type), propname); - return; - } - - /* the number of states determines the way we group them - * - we want 2 rows only (for now) - * - the number of columns (cols) is the total number of buttons per row - * the 'remainder' is added to this, as it will be ok to have first row slightly wider if need be - * - for now, only split into groups if group will have at least 5 items - */ - states = RNA_property_array_length(ptr, prop); - cols = (states / 2) + (states % 2); - groups = ((cols / 2) < cols_per_group) ? (1) : (cols / cols_per_group); - - if (used_ptr && used_propname) { - used_prop = RNA_struct_find_property(used_ptr, used_propname); - if (!used_prop) { - RNA_warning("used layers property not found: %s.%s", RNA_struct_identifier(ptr->type), used_propname); - return; - } - - if (RNA_property_array_length(used_ptr, used_prop) < states) - used_prop = NULL; - } - - /* layers are laid out going across rows, with the columns being divided into groups */ - - for (group = 0; group < groups; group++) { - uCol = uiLayoutColumn(layout, true); - - for (row = 0; row < 2; row++) { - uiBlock *block; - uiBut *but; - - uRow = uiLayoutRow(uCol, true); - block = uiLayoutGetBlock(uRow); - state = groups * cols_per_group * row + cols_per_group * group; - - /* add layers as toggle buts */ - for (col = 0; (col < cols_per_group) && (state < states); col++, state++) { - int icon = 0; - int butlay = 1 << state; - - if (active_state & butlay) - icon = ICON_LAYER_ACTIVE; - else if (used_prop && RNA_property_boolean_get_index(used_ptr, used_prop, state)) - icon = ICON_LAYER_USED; - - but = uiDefIconButR_prop( - block, UI_BTYPE_ICON_TOGGLE, 0, icon, 0, 0, UI_UNIT_X / 2, UI_UNIT_Y / 2, ptr, prop, - state, 0, 0, -1, -1, sca_state_name_get(ob, state)); - UI_but_func_set(but, handle_layer_buttons, but, SET_INT_IN_POINTER(state)); - but->type = UI_BTYPE_TOGGLE; - } - } - } -} - - /************************* List Template **************************/ static void uilist_draw_item_default( struct uiList *ui_list, struct bContext *UNUSED(C), struct uiLayout *layout, @@ -3553,11 +3952,14 @@ static void ui_layout_operator_buts__reset_cb(bContext *UNUSED(C), void *op_pt, * Draw Operator property buttons for redoing execution with different settings. * This function does not initialize the layout, functions can be called on the layout before and after. */ -void uiTemplateOperatorPropertyButs( +eAutoPropButsReturn uiTemplateOperatorPropertyButs( const bContext *C, uiLayout *layout, wmOperator *op, bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *), - const char label_align, const short flag) + const eButLabelAlign label_align, const short flag) { + uiBlock *block = uiLayoutGetBlock(layout); + eAutoPropButsReturn return_info = 0; + if (!op->properties) { IDPropertyTemplate val = {0}; op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); @@ -3570,14 +3972,14 @@ void uiTemplateOperatorPropertyButs( /* poll() on this operator may still fail, at the moment there is no nice feedback when this happens * just fails silently */ if (!WM_operator_repeat_check(C, op)) { - UI_block_lock_set(uiLayoutGetBlock(layout), true, "Operator can't' redo"); + UI_block_lock_set(block, true, "Operator can't' redo"); /* XXX, could give some nicer feedback or not show redo panel at all? */ uiItemL(layout, IFACE_("* Redo Unsupported *"), ICON_NONE); } else { /* useful for macros where only one of the steps can't be re-done */ - UI_block_lock_clear(uiLayoutGetBlock(layout)); + UI_block_lock_clear(block); } /* menu */ @@ -3586,7 +3988,7 @@ void uiTemplateOperatorPropertyButs( PointerRNA op_ptr; uiLayout *row; - uiLayoutGetBlock(layout)->ui_operator = op; + block->ui_operator = op; row = uiLayoutRow(layout, true); uiItemM(row, "WM_MT_operator_presets", NULL, ICON_NONE); @@ -3605,19 +4007,21 @@ void uiTemplateOperatorPropertyButs( op->type->ui((bContext *)C, op); op->layout = NULL; - /* UI_LAYOUT_OP_SHOW_EMPTY ignored */ + /* UI_LAYOUT_OP_SHOW_EMPTY ignored. return_info is ignored too. We could + * allow ot.ui callback to return this, but not needed right now. */ } else { wmWindowManager *wm = CTX_wm_manager(C); PointerRNA ptr; - int empty; RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + uiLayoutSetPropSep(layout, true); + /* main draw call */ - empty = uiDefAutoButsRNA(layout, &ptr, check_prop, label_align) == 0; + return_info = uiDefAutoButsRNA(layout, &ptr, check_prop, label_align, (flag & UI_TEMPLATE_OP_PROPS_COMPACT)); - if (empty && (flag & UI_TEMPLATE_OP_PROPS_SHOW_EMPTY)) { + if ((return_info & UI_PROP_BUTS_NONE_ADDED) && (flag & UI_TEMPLATE_OP_PROPS_SHOW_EMPTY)) { uiItemL(layout, IFACE_("No Properties"), ICON_NONE); } } @@ -3627,7 +4031,6 @@ void uiTemplateOperatorPropertyButs( * but this is not so important if this button is drawn in those cases * (which isn't all that likely anyway) - campbell */ if (op->properties->len) { - uiBlock *block; uiBut *but; uiLayout *col; /* needed to avoid alignment errors with previous buttons */ @@ -3640,8 +4043,9 @@ void uiTemplateOperatorPropertyButs( #endif /* set various special settings for buttons */ - { - uiBlock *block = uiLayoutGetBlock(layout); + + /* Only do this if we're not refreshing an existing UI. */ + if (block->oldblock == NULL) { const bool is_popup = (block->flag & UI_BLOCK_KEEP_OPEN) != 0; uiBut *but; @@ -3661,6 +4065,8 @@ void uiTemplateOperatorPropertyButs( } } } + + return return_info; } /************************* Running Jobs Template **************************/ @@ -3900,18 +4306,18 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C) /* make a box around the report to make it stand out */ UI_block_align_begin(block); - but = uiDefBut(block, UI_BTYPE_ROUNDBOX, 0, "", 0, 0, UI_UNIT_X + 10, UI_UNIT_Y, NULL, 0.0f, 0.0f, 0, 0, ""); + but = uiDefBut(block, UI_BTYPE_ROUNDBOX, 0, "", 0, 0, UI_UNIT_X + 5, UI_UNIT_Y, NULL, 0.0f, 0.0f, 0, 0, ""); /* set the report's bg color in but->col - UI_BTYPE_ROUNDBOX feature */ rgb_float_to_uchar(but->col, rti->col); but->col[3] = 255; - but = uiDefBut(block, UI_BTYPE_ROUNDBOX, 0, "", UI_UNIT_X + 10, 0, UI_UNIT_X + width, UI_UNIT_Y, + but = uiDefBut(block, UI_BTYPE_ROUNDBOX, 0, "", UI_UNIT_X + 5, 0, UI_UNIT_X + width, UI_UNIT_Y, NULL, 0.0f, 0.0f, 0, 0, ""); - but->col[0] = but->col[1] = but->col[2] = unit_float_to_uchar_clamp(rti->grayscale); - but->col[3] = 255; UI_block_align_end(block); + UI_GetThemeColorShade3ubv(TH_BACK, 20, but->col); + but->col[3] = 255; /* icon and report message on top */ icon = UI_icon_from_report_type(report->type); @@ -3929,10 +4335,50 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C) UI_block_emboss_set(block, UI_EMBOSS); - uiDefBut(block, UI_BTYPE_LABEL, 0, report->message, UI_UNIT_X + 10, 0, UI_UNIT_X + width, UI_UNIT_Y, + uiDefBut(block, UI_BTYPE_LABEL, 0, report->message, UI_UNIT_X + 5, 0, UI_UNIT_X + width, UI_UNIT_Y, NULL, 0.0f, 0.0f, 0, 0, ""); } + +void uiTemplateInputStatus(uiLayout *layout, struct bContext *C) +{ + wmWindow *win = CTX_wm_window(C); + WorkSpace *workspace = CTX_wm_workspace(C); + + /* Workspace status text has priority. */ + if (workspace->status_text) { + uiItemL(layout, workspace->status_text, ICON_NONE); + return; + } + + if (WM_window_modal_keymap_status_draw(C, win, layout)) { + return; + } + + /* Otherwise should cursor keymap status. */ + for (int i = 0; i < 3; i++) { + uiLayout *box = uiLayoutRow(layout, false); + uiLayout *col = uiLayoutColumn(box, false); + uiLayout *row = uiLayoutRow(col, true); + uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT); + + const char *msg = WM_window_cursor_keymap_status_get(win, i, 0); + const char *msg_drag = WM_window_cursor_keymap_status_get(win, i, 1); + + if (msg || (msg_drag == NULL)) { + uiItemL(row, msg ? msg : "", (ICON_MOUSE_LMB + i)); + } + + if (msg_drag) { + uiItemL(row, msg_drag, (ICON_MOUSE_LMB_DRAG + i)); + } + + /* Use trick with empty string to keep icons in same position. */ + row = uiLayoutRow(col, false); + uiItemL(row, " ", ICON_NONE); + } +} + /********************************* Keymap *************************************/ static void keymap_item_modified(bContext *UNUSED(C), void *kmi_p, void *UNUSED(unused)) @@ -3995,6 +4441,7 @@ void uiTemplateKeymapItemProperties(uiLayout *layout, PointerRNA *ptr) if (propptr.data) { uiBut *but = uiLayoutGetBlock(layout)->buttons.last; + WM_operator_properties_sanitize(&propptr, false); template_keymap_item_properties(layout, NULL, &propptr); /* attach callbacks to compensate for missing properties update, diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 1aa6f045266..d080397c488 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -157,66 +157,159 @@ uiBut *uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int ind * \a check_prop callback filters functions to avoid drawing certain properties, * in cases where PROP_HIDDEN flag can't be used for a property. */ -int uiDefAutoButsRNA( +eAutoPropButsReturn uiDefAutoButsRNA( uiLayout *layout, PointerRNA *ptr, bool (*check_prop)(PointerRNA *, PropertyRNA *), - const char label_align) + const eButLabelAlign label_align, const bool compact) { + eAutoPropButsReturn return_info = UI_PROP_BUTS_NONE_ADDED; uiLayout *split, *col; int flag; const char *name; - int tot = 0; - - assert(ELEM(label_align, '\0', 'H', 'V')); RNA_STRUCT_BEGIN (ptr, prop) { flag = RNA_property_flag(prop); - if (flag & PROP_HIDDEN || (check_prop && check_prop(ptr, prop) == 0)) + + if (flag & PROP_HIDDEN) { + continue; + } + if (check_prop && check_prop(ptr, prop) == 0) { + return_info |= UI_PROP_BUTS_ANY_FAILED_CHECK; continue; + } - if (label_align != '\0') { - PropertyType type = RNA_property_type(prop); - const bool is_boolean = (type == PROP_BOOLEAN && !RNA_property_array_check(prop)); + switch (label_align) { + case UI_BUT_LABEL_ALIGN_COLUMN: + case UI_BUT_LABEL_ALIGN_SPLIT_COLUMN: + { + PropertyType type = RNA_property_type(prop); + const bool is_boolean = (type == PROP_BOOLEAN && !RNA_property_array_check(prop)); - name = RNA_property_ui_name(prop); + name = RNA_property_ui_name(prop); - if (label_align == 'V') { - col = uiLayoutColumn(layout, true); + if (label_align == UI_BUT_LABEL_ALIGN_COLUMN) { + col = uiLayoutColumn(layout, true); - if (!is_boolean) - uiItemL(col, name, ICON_NONE); - } - else { /* (label_align == 'H') */ - BLI_assert(label_align == 'H'); - split = uiLayoutSplit(layout, 0.5f, false); + if (!is_boolean) + uiItemL(col, name, ICON_NONE); + } + else { + BLI_assert(label_align == UI_BUT_LABEL_ALIGN_SPLIT_COLUMN); + split = uiLayoutSplit(layout, 0.5f, false); + + col = uiLayoutColumn(split, false); + uiItemL(col, (is_boolean) ? "" : name, ICON_NONE); + col = uiLayoutColumn(split, false); + } + + /* may meed to add more cases here. + * don't override enum flag names */ - col = uiLayoutColumn(split, false); - uiItemL(col, (is_boolean) ? "" : name, ICON_NONE); - col = uiLayoutColumn(split, false); + /* name is shown above, empty name for button below */ + name = (flag & PROP_ENUM_FLAG || is_boolean) ? NULL : ""; + + break; } + case UI_BUT_LABEL_ALIGN_NONE: + default: + col = layout; + name = NULL; /* no smart label alignment, show default name with button */ + break; + } + + uiItemFullR(col, ptr, prop, -1, 0, compact ? UI_ITEM_R_COMPACT : 0, name, ICON_NONE); + return_info &= ~UI_PROP_BUTS_NONE_ADDED; + } + RNA_STRUCT_END; - /* may meed to add more cases here. - * don't override enum flag names */ + return return_info; +} + +/* *** RNA collection search menu *** */ + +typedef struct CollItemSearch { + struct CollItemSearch *next, *prev; + void *data; + char *name; + int index; + int iconid; +} CollItemSearch; + +static int sort_search_items_list(const void *a, const void *b) +{ + const CollItemSearch *cis1 = a; + const CollItemSearch *cis2 = b; - /* name is shown above, empty name for button below */ - name = (flag & PROP_ENUM_FLAG || is_boolean) ? NULL : ""; + if (BLI_strcasecmp(cis1->name, cis2->name) > 0) + return 1; + else + return 0; +} + +void ui_rna_collection_search_cb(const struct bContext *C, void *arg, const char *str, uiSearchItems *items) +{ + uiRNACollectionSearch *data = arg; + char *name; + int i = 0, iconid = 0, flag = RNA_property_flag(data->target_prop); + ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list"); + CollItemSearch *cis; + const bool skip_filter = !(data->but_changed && *data->but_changed); + + /* build a temporary list of relevant items first */ + RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) + { + + if (flag & PROP_ID_SELF_CHECK) + if (itemptr.data == data->target_ptr.id.data) + continue; + + /* use filter */ + if (RNA_property_type(data->target_prop) == PROP_POINTER) { + if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) + continue; } - else { - col = layout; - name = NULL; /* no smart label alignment, show default name with button */ + + name = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); /* could use the string length here */ + iconid = 0; + if (itemptr.type && RNA_struct_is_ID(itemptr.type)) { + iconid = ui_id_icon_get(C, itemptr.data, false); + } + + if (name) { + if (skip_filter || BLI_strcasestr(name, str)) { + cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch"); + cis->data = itemptr.data; + cis->name = MEM_dupallocN(name); + cis->index = i; + cis->iconid = iconid; + BLI_addtail(items_list, cis); + } + MEM_freeN(name); } - uiItemFullR(col, ptr, prop, -1, 0, 0, name, ICON_NONE); - tot++; + i++; + } + RNA_PROP_END; + + BLI_listbase_sort(items_list, sort_search_items_list); + + /* add search items from temporary list */ + for (cis = items_list->first; cis; cis = cis->next) { + if (UI_search_item_add(items, cis->name, cis->data, cis->iconid) == false) { + break; + } } - RNA_STRUCT_END; - return tot; + for (cis = items_list->first; cis; cis = cis->next) { + MEM_freeN(cis->name); + } + BLI_freelistN(items_list); + MEM_freeN(items_list); } -/***************************** ID Utilities *******************************/ +/***************************** ID Utilities *******************************/ int UI_icon_from_id(ID *id) { Object *ob; diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index a7de70629d1..52e6e237a58 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -43,13 +43,9 @@ #include "BLI_utildefines.h" #include "BKE_context.h" -#include "BKE_curve.h" #include "RNA_access.h" -#include "BIF_gl.h" -#include "BIF_glutil.h" - #include "BLF_api.h" #include "UI_interface.h" @@ -58,6 +54,12 @@ #include "interface_intern.h" #include "GPU_basic_shader.h" +#include "GPU_batch.h" +#include "GPU_batch_presets.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" #ifdef WITH_INPUT_IME # include "WM_types.h" @@ -72,13 +74,19 @@ enum { /* Show that holding the button opens a menu. */ UI_STATE_HOLD_ACTION = UI_BUT_UPDATE_DELAY, UI_STATE_TEXT_INPUT = UI_BUT_UNDO, + UI_STATE_ACTIVE_LEFT = UI_BUT_VALUE_CLEAR, + UI_STATE_ACTIVE_RIGHT = UI_BUT_TEXTEDIT_UPDATE, - UI_STATE_FLAGS_ALL = (UI_STATE_HOLD_ACTION | UI_STATE_TEXT_INPUT), + UI_STATE_FLAGS_ALL = (UI_STATE_HOLD_ACTION | + UI_STATE_TEXT_INPUT | + UI_STATE_ACTIVE_LEFT | + UI_STATE_ACTIVE_RIGHT), }; /* Prevent accidental use. */ #define UI_BUT_UPDATE_DELAY ((void)0) #define UI_BUT_UNDO ((void)0) + /* ************** widget base functions ************** */ /** * - in: roundbox codes for corner types and radius @@ -98,6 +106,8 @@ enum { typedef struct uiWidgetTrias { unsigned int tot; + int type; + float size, center[2]; float vec[16][2]; const unsigned int (*index)[3]; @@ -105,21 +115,24 @@ typedef struct uiWidgetTrias { } uiWidgetTrias; /* max as used by round_box__edges */ +/* Make sure to change widget_base_vert.glsl accordingly. */ #define WIDGET_CURVE_RESOLU 9 #define WIDGET_SIZE_MAX (WIDGET_CURVE_RESOLU * 4) typedef struct uiWidgetBase { - + /* TODO remove these completely */ int totvert, halfwayvert; float outer_v[WIDGET_SIZE_MAX][2]; float inner_v[WIDGET_SIZE_MAX][2]; float inner_uv[WIDGET_SIZE_MAX][2]; - bool draw_inner, draw_outline, draw_emboss, draw_shadedir; + bool draw_inner, draw_outline, draw_emboss; uiWidgetTrias tria1; uiWidgetTrias tria2; + /* Widget shader parameters, must match the shader layout. */ + uiWidgetBaseParameters uniform_params; } uiWidgetBase; /** uiWidgetType: for time being only for visual appearance, @@ -150,13 +163,15 @@ static const float cornervec[WIDGET_CURVE_RESOLU][2] = { {0.924, 0.617}, {0.98, 0.805}, {1.0, 1.0} }; -#define WIDGET_AA_JITTER 8 -static const float jit[WIDGET_AA_JITTER][2] = { + +const float ui_pixel_jitter[UI_PIXEL_AA_JITTER][2] = { { 0.468813, -0.481430}, {-0.155755, -0.352820}, { 0.219306, -0.238501}, {-0.393286, -0.110949}, {-0.024699, 0.013908}, { 0.343805, 0.147431}, {-0.272855, 0.269918}, { 0.095909, 0.388710} }; +#define WIDGET_AA_JITTER UI_PIXEL_AA_JITTER +#define jit ui_pixel_jitter /* -------------------------------------------------------------------- */ /** \name Shape Preset Data @@ -195,8 +210,8 @@ static const uint g_shape_preset_checkmark_face[4][3] = { {3, 2, 4}, {3, 4, 5}, {1, 0, 3}, {0, 2, 3} }; -#define OY -0.2 -#define SC 0.35 +#define OY (-0.2 / 2) +#define SC (0.35 * 2) static const float g_shape_preset_hold_action_vert[6][2] = { {-0.5 + SC, 1.0 + OY}, {0.5, 1.0 + OY}, {0.5, 0.0 + OY + SC}, }; @@ -206,53 +221,364 @@ static const uint g_shape_preset_hold_action_face[2][3] = {{2, 0, 1}, {3, 5, 4}} /** \} */ +/* **************** Batch creations ****************** */ +/** + * In order to speed up UI drawing we create some batches that are then + * modified by specialized shaders to draw certain elements really fast. + * TODO: find a better place. Maybe it's own file? + **/ + +/* offset in triavec[] in shader per type */ +static const int tria_ofs[ROUNDBOX_TRIA_MAX] = { + [ROUNDBOX_TRIA_NONE] = 0, + [ROUNDBOX_TRIA_ARROWS] = 0, + [ROUNDBOX_TRIA_SCROLL] = 12, + [ROUNDBOX_TRIA_MENU] = 28, + [ROUNDBOX_TRIA_CHECK] = 34, + [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 40, +}; +static const int tria_vcount[ROUNDBOX_TRIA_MAX] = { + [ROUNDBOX_TRIA_NONE] = 0, + [ROUNDBOX_TRIA_ARROWS] = 6, + [ROUNDBOX_TRIA_SCROLL] = 16, + [ROUNDBOX_TRIA_MENU] = 6, + [ROUNDBOX_TRIA_CHECK] = 6, + [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 3, +}; + +static struct { + GPUBatch *roundbox_widget[ROUNDBOX_TRIA_MAX]; + + GPUBatch *roundbox_simple; + GPUBatch *roundbox_simple_aa; + GPUBatch *roundbox_simple_outline; + GPUBatch *roundbox_shadow; + + GPUVertFormat format; + uint vflag_id; +} g_ui_batch_cache = {{0}}; + +static GPUVertFormat *vflag_format(void) +{ + if (g_ui_batch_cache.format.attr_len == 0) { + GPUVertFormat *format = &g_ui_batch_cache.format; + g_ui_batch_cache.vflag_id = GPU_vertformat_attr_add(format, "vflag", GPU_COMP_U32, 1, GPU_FETCH_INT); + } + return &g_ui_batch_cache.format; +} + +#define INNER 0 +#define OUTLINE 1 +#define EMBOSS 2 +#define NO_AA WIDGET_AA_JITTER + +static void set_roundbox_vertex_data( + GPUVertBufRaw *vflag_step, uint32_t d) +{ + uint32_t *data = GPU_vertbuf_raw_step(vflag_step); + *data = d; +} + +static uint32_t set_roundbox_vertex( + GPUVertBufRaw *vflag_step, + int corner_id, int corner_v, int jit_v, bool inner, bool emboss, int color) +{ + uint32_t *data = GPU_vertbuf_raw_step(vflag_step); + *data = corner_id; + *data |= corner_v << 2; + *data |= jit_v << 6; + *data |= color << 12; + *data |= (inner) ? (1 << 10) : 0; /* is inner vert */ + *data |= (emboss) ? (1 << 11) : 0; /* is emboss vert */ + return *data; +} + +static uint32_t set_tria_vertex( + GPUVertBufRaw *vflag_step, + int tria_type, int tria_v, int tria_id, int jit_v) +{ + uint32_t *data = GPU_vertbuf_raw_step(vflag_step); + if (ELEM(tria_type, ROUNDBOX_TRIA_ARROWS)) { + tria_v += tria_id * tria_vcount[ROUNDBOX_TRIA_ARROWS]; + } + *data = tria_ofs[tria_type] + tria_v; + *data |= jit_v << 6; + *data |= (tria_id == 0) ? (1 << 10) : 0; /* is first tria */ + *data |= 1 << 14; /* is tria vert */ + return *data; +} + +static void roundbox_batch_add_tria(GPUVertBufRaw *vflag_step, int tria, uint32_t last_data) +{ + const int tria_num = ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, ROUNDBOX_TRIA_MENU) ? 1 : 2; + /* for each tria */ + for (int t = 0; t < tria_num; ++t) { + for (int j = 0; j < WIDGET_AA_JITTER; j++) { + /* restart */ + set_roundbox_vertex_data(vflag_step, last_data); + set_tria_vertex(vflag_step, tria, 0, t, j); + for (int v = 0; v < tria_vcount[tria]; v++) { + last_data = set_tria_vertex(vflag_step, tria, v, t, j); + } + } + } +} + +GPUBatch *ui_batch_roundbox_widget_get(int tria) +{ + if (g_ui_batch_cache.roundbox_widget[tria] == NULL) { + uint32_t last_data; + GPUVertBufRaw vflag_step; + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format()); + int vcount = WIDGET_SIZE_MAX; /* inner */ + vcount += 2; /* restart */ + vcount += ((WIDGET_SIZE_MAX + 1) * 2) * WIDGET_AA_JITTER; /* outline (edges) */ + vcount += 2; /* restart */ + vcount += ((WIDGET_CURVE_RESOLU * 2) * 2) * WIDGET_AA_JITTER; /* emboss */ + if (tria) { + vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria1 */ + if (!ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, ROUNDBOX_TRIA_MENU)) { + vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria2 */ + } + } + GPU_vertbuf_data_alloc(vbo, vcount); + GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step); + /* Inner */ + for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) { + for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU -1; a2 >= 0; a1++, a2--) { + last_data = set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER); + last_data = set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER); + } + } + /* restart */ + set_roundbox_vertex_data(&vflag_step, last_data); + set_roundbox_vertex(&vflag_step, 0, 0, 0, true, false, OUTLINE); + /* Outlines */ + for (int j = 0; j < WIDGET_AA_JITTER; j++) { + for (int c = 0; c < 4; c++) { + for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) { + set_roundbox_vertex(&vflag_step, c, a, j, true, false, OUTLINE); + set_roundbox_vertex(&vflag_step, c, a, j, false, false, OUTLINE); + } + } + /* Close the loop. */ + set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, OUTLINE); + last_data = set_roundbox_vertex(&vflag_step, 0, 0, j, false, false, OUTLINE); + } + /* restart */ + set_roundbox_vertex_data(&vflag_step, last_data); + set_roundbox_vertex(&vflag_step, 0, 0, 0, false, false, EMBOSS); + /* Emboss */ + bool rev = false; /* go back and forth : avoid degenerate triangle (but beware of backface cull) */ + for (int j = 0; j < WIDGET_AA_JITTER; j++, rev = !rev) { + for (int c = (rev) ? 1 : 0; (rev) ? c >= 0 : c < 2; (rev) ? c-- : c++) { + int sta = (rev) ? WIDGET_CURVE_RESOLU - 1 : 0; + int end = WIDGET_CURVE_RESOLU; + for (int a = sta; (rev) ? a >= 0 : a < end; (rev) ? a-- : a++) { + set_roundbox_vertex(&vflag_step, c, a, j, false, false, EMBOSS); + last_data = set_roundbox_vertex(&vflag_step, c, a, j, false, true, EMBOSS); + } + } + } + if (tria) { + roundbox_batch_add_tria(&vflag_step, tria, last_data); + } + g_ui_batch_cache.roundbox_widget[tria] = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); + gpu_batch_presets_register(g_ui_batch_cache.roundbox_widget[tria]); + } + return g_ui_batch_cache.roundbox_widget[tria]; +} + +GPUBatch *ui_batch_roundbox_get(bool filled, bool antialiased) +{ + GPUBatch **batch = NULL; + + if (filled) { + if (antialiased) + batch = &g_ui_batch_cache.roundbox_simple_aa; + else + batch = &g_ui_batch_cache.roundbox_simple; + } + else { + if (antialiased) + BLI_assert(0); /* Use GL_LINE_SMOOTH instead!!: */ + else + batch = &g_ui_batch_cache.roundbox_simple_outline; + } + + if (*batch == NULL) { + uint32_t last_data; + GPUVertBufRaw vflag_step; + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format()); + int vcount = WIDGET_SIZE_MAX; + vcount += (filled) ? 2 : 0; + vcount *= (antialiased) ? WIDGET_AA_JITTER : 1; + GPU_vertbuf_data_alloc(vbo, vcount); + GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step); + + if (filled) { + for (int j = 0; j < WIDGET_AA_JITTER; j++) { + if (!antialiased) { + j = NO_AA; + } + /* restart */ + set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, INNER); + for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) { + for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU -1; a2 >= 0; a1++, a2--) { + last_data = set_roundbox_vertex(&vflag_step, c1, a1, j, true, false, INNER); + last_data = set_roundbox_vertex(&vflag_step, c2, a2, j, true, false, INNER); + } + } + /* restart */ + set_roundbox_vertex_data(&vflag_step, last_data); + if (!antialiased) { + break; + } + } + *batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); + } + else { + for (int j = 0; j < WIDGET_AA_JITTER; j++) { + if (!antialiased) { + j = NO_AA; + } + for (int c = 0; c < 4; c++) { + for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) { + set_roundbox_vertex(&vflag_step, c, a, j, true, false, INNER); + } + } + if (!antialiased) { + break; + } + } + *batch = GPU_batch_create_ex(GPU_PRIM_LINE_LOOP, vbo, NULL, GPU_BATCH_OWNS_VBO); + } + + gpu_batch_presets_register(*batch); + } + return *batch; +} + +GPUBatch *ui_batch_roundbox_shadow_get(void) +{ + if (g_ui_batch_cache.roundbox_shadow == NULL) { + uint32_t last_data; + GPUVertBufRaw vflag_step; + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format()); + int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX; + GPU_vertbuf_data_alloc(vbo, vcount); + GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step); + + for (int c = 0; c < 4; c++) { + for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) { + set_roundbox_vertex(&vflag_step, c, a, NO_AA, true, false, INNER); + set_roundbox_vertex(&vflag_step, c, a, NO_AA, false, false, INNER); + } + } + /* close loop */ + last_data = set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, true, false, INNER); + last_data = set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, false, false, INNER); + /* restart */ + set_roundbox_vertex_data(&vflag_step, last_data); + set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, true, false, INNER); + /* filled */ + for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) { + for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU -1; a2 >= 0; a1++, a2--) { + set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER); + set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER); + } + } + g_ui_batch_cache.roundbox_shadow = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); + gpu_batch_presets_register(g_ui_batch_cache.roundbox_shadow); + } + return g_ui_batch_cache.roundbox_shadow; +} + +#undef INNER +#undef OUTLINE +#undef EMBOSS +#undef NO_AA + /* ************************************************* */ -void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3) +void UI_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3, + const float color[4]) { float tri_arr[3][2] = {{x1, y1}, {x2, y2}, {x3, y3}}; - float color[4]; - int j; + float draw_color[4]; + + copy_v4_v4(draw_color, color); + /* Note: This won't give back the original color. */ + draw_color[3] *= 1.0f / WIDGET_AA_JITTER; - glEnable(GL_BLEND); - glGetFloatv(GL_CURRENT_COLOR, color); - color[3] *= 0.125f; - glColor4fv(color); + GPU_blend(true); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, tri_arr); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4fv(draw_color); + immBegin(GPU_PRIM_TRIS, 3 * WIDGET_AA_JITTER); /* for each AA step */ - for (j = 0; j < WIDGET_AA_JITTER; j++) { - glTranslate2fv(jit[j]); - glDrawArrays(GL_TRIANGLES, 0, 3); - glTranslatef(-jit[j][0], -jit[j][1], 0.0f); + for (int j = 0; j < WIDGET_AA_JITTER; j++) { + immVertex2f(pos, tri_arr[0][0] + jit[j][0], tri_arr[0][1] + jit[j][1]); + immVertex2f(pos, tri_arr[1][0] + jit[j][0], tri_arr[1][1] + jit[j][1]); + immVertex2f(pos, tri_arr[2][0] + jit[j][0], tri_arr[2][1] + jit[j][1]); } - glDisableClientState(GL_VERTEX_ARRAY); - glDisable(GL_BLEND); + immEnd(); + + immUnbindProgram(); + + GPU_blend(false); } -void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy, float rad, bool use_alpha) +/* triangle 'icon' inside rect */ +void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]) { - float color[4]; - int j; - - glEnable(GL_BLEND); - glGetFloatv(GL_CURRENT_COLOR, color); - if (use_alpha) { - color[3] = 0.5f; + if (dir == 'h') { + float half = 0.5f * BLI_rctf_size_y(rect); + UI_draw_anti_tria(rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color); } - color[3] *= 0.125f; - glColor4fv(color); + else { + float half = 0.5f * BLI_rctf_size_x(rect); + UI_draw_anti_tria(rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color); + } +} + + +void UI_draw_anti_fan(float tri_array[][2], unsigned int length, const float color[4]) +{ + float draw_color[4]; + + copy_v4_v4(draw_color, color); + draw_color[3] *= 2.0f / WIDGET_AA_JITTER; + + GPU_blend(true); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4fv(draw_color); - for (j = 0; j < WIDGET_AA_JITTER; j++) { - glTranslate2fv(jit[j]); - UI_draw_roundbox_gl_mode(mode, minx, miny, maxx, maxy, rad); - glTranslatef(-jit[j][0], -jit[j][1], 0.0f); + /* for each AA step */ + for (int j = 0; j < WIDGET_AA_JITTER; j++) { + immBegin(GPU_PRIM_TRI_FAN, length); + immVertex2f(pos, tri_array[0][0], tri_array[0][1]); + immVertex2f(pos, tri_array[1][0], tri_array[1][1]); + + /* We jitter only the middle of the fan, the extremes are pinned. */ + for (int i = 2; i < length - 1; i++) { + immVertex2f(pos, tri_array[i][0] + jit[j][0], tri_array[i][1] + jit[j][1]); + } + + immVertex2f(pos, tri_array[length - 1][0], tri_array[length - 1][1]); + immEnd(); } - glDisable(GL_BLEND); + immUnbindProgram(); + + GPU_blend(false); } static void widget_init(uiWidgetBase *wtb) @@ -260,11 +586,16 @@ static void widget_init(uiWidgetBase *wtb) wtb->totvert = wtb->halfwayvert = 0; wtb->tria1.tot = 0; wtb->tria2.tot = 0; + wtb->tria1.type = ROUNDBOX_TRIA_NONE; + wtb->tria1.size = 0; + wtb->tria2.size = 0; wtb->draw_inner = true; wtb->draw_outline = true; wtb->draw_emboss = true; - wtb->draw_shadedir = true; + + wtb->uniform_params.shade_dir = 1.0f; + wtb->uniform_params.alpha_discard = 1.0f; } /* helper call, makes shadow rect, with 'sun' above menu, so only shadow to left/right/bottom */ @@ -375,6 +706,17 @@ static void round_box__edges(uiWidgetBase *wt, int roundboxalign, const rcti *re if (2.0f * (radi + 1.0f) > minsize) radi = 0.5f * minsize - U.pixelsize; + wt->uniform_params.rad = rad; + wt->uniform_params.radi = radi; + wt->uniform_params.facxi = facxi; + wt->uniform_params.facyi = facyi; + wt->uniform_params.round_corners[0] = (roundboxalign & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f; + wt->uniform_params.round_corners[1] = (roundboxalign & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f; + wt->uniform_params.round_corners[2] = (roundboxalign & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f; + wt->uniform_params.round_corners[3] = (roundboxalign & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f; + BLI_rctf_rcti_copy(&wt->uniform_params.rect, rect); + BLI_rctf_init(&wt->uniform_params.recti, minxi, maxxi, minyi, maxyi); + /* mult */ for (a = 0; a < WIDGET_CURVE_RESOLU; a++) { veci[a][0] = radi * cornervec[a][0]; @@ -519,23 +861,30 @@ static void shape_preset_init_trias_ex( float centx, centy, sizex, sizey, minsize; int a, i1 = 0, i2 = 1; - minsize = min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)); + if (where == 'r' || where == 'l') { + minsize = BLI_rcti_size_y(rect); + } + else { + minsize = BLI_rcti_size_x(rect); + } /* center position and size */ centx = (float)rect->xmin + 0.4f * minsize; centy = (float)rect->ymin + 0.5f * minsize; - sizex = sizey = -0.5f * triasize * minsize; + tria->size = sizex = sizey = -0.5f * triasize * minsize; if (where == 'r') { centx = (float)rect->xmax - 0.4f * minsize; sizex = -sizex; } else if (where == 't') { + centx = (float)rect->xmin + 0.5f * minsize; centy = (float)rect->ymax - 0.5f * minsize; sizey = -sizey; i2 = 0; i1 = 1; } else if (where == 'b') { + centx = (float)rect->xmin + 0.5f * minsize; sizex = -sizex; i2 = 0; i1 = 1; } @@ -545,12 +894,16 @@ static void shape_preset_init_trias_ex( tria->vec[a][1] = sizey * verts[a][i2] + centy; } + tria->center[0] = centx; + tria->center[1] = centy; + tria->tot = tris_tot; tria->index = tris; } static void shape_preset_init_number_arrows(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) { + tria->type = ROUNDBOX_TRIA_ARROWS; shape_preset_init_trias_ex( tria, rect, triasize, where, g_shape_preset_number_arrow_vert, ARRAY_SIZE(g_shape_preset_number_arrow_vert), @@ -559,6 +912,12 @@ static void shape_preset_init_number_arrows(uiWidgetTrias *tria, const rcti *rec static void shape_preset_init_hold_action(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) { + tria->type = ROUNDBOX_TRIA_HOLD_ACTION_ARROW; + /* With the current changes to use batches for widget drawing, the code + * below is doing almost nothing effectively. 'where' doesn't work either, + * shader is currently hardcoded to work for the button triangle pointing + * at the lower right. The same limitation applies to other trias as well. + * XXX Should be addressed. */ shape_preset_init_trias_ex( tria, rect, triasize, where, g_shape_preset_hold_action_vert, ARRAY_SIZE(g_shape_preset_hold_action_vert), @@ -567,29 +926,37 @@ static void shape_preset_init_hold_action(uiWidgetTrias *tria, const rcti *rect, static void shape_preset_init_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) { + tria->type = ROUNDBOX_TRIA_SCROLL; shape_preset_init_trias_ex( tria, rect, triasize, where, g_shape_preset_scroll_circle_vert, ARRAY_SIZE(g_shape_preset_scroll_circle_vert), g_shape_preset_scroll_circle_face, ARRAY_SIZE(g_shape_preset_scroll_circle_face)); } -static void shape_preset_draw_trias(uiWidgetTrias *tria) +static void widget_draw_vertex_buffer(unsigned int pos, unsigned int col, int mode, + const float quads_pos[WIDGET_SIZE_MAX][2], + const unsigned char quads_col[WIDGET_SIZE_MAX][4], + unsigned int totvert) { - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, tria->vec); - glDrawElements(GL_TRIANGLES, tria->tot * 3, GL_UNSIGNED_INT, tria->index); - glDisableClientState(GL_VERTEX_ARRAY); + immBegin(mode, totvert); + for (int i = 0; i < totvert; ++i) { + if (quads_col) + immAttrib4ubv(col, quads_col[i]); + immVertex2fv(pos, quads_pos[i]); + } + immEnd(); } static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *rect) { float centx, centy, size; int a; + tria->type = ROUNDBOX_TRIA_MENU; /* center position and size */ - centx = rect->xmax - 0.32f * BLI_rcti_size_y(rect); - centy = rect->ymin + 0.50f * BLI_rcti_size_y(rect); - size = 0.4f * BLI_rcti_size_y(rect); + tria->center[0] = centx = rect->xmax - 0.32f * BLI_rcti_size_y(rect); + tria->center[1] = centy = rect->ymin + 0.50f * BLI_rcti_size_y(rect); + tria->size = size = 0.4f * BLI_rcti_size_y(rect); for (a = 0; a < 6; a++) { tria->vec[a][0] = size * g_shape_preset_menu_arrow_vert[a][0] + centx; @@ -604,11 +971,12 @@ static void shape_preset_trias_from_rect_checkmark(uiWidgetTrias *tria, const rc { float centx, centy, size; int a; + tria->type = ROUNDBOX_TRIA_CHECK; /* center position and size */ - centx = rect->xmin + 0.5f * BLI_rcti_size_y(rect); - centy = rect->ymin + 0.5f * BLI_rcti_size_y(rect); - size = 0.5f * BLI_rcti_size_y(rect); + tria->center[0] = centx = rect->xmin + 0.5f * BLI_rcti_size_y(rect); + tria->center[1] = centy = rect->ymin + 0.5f * BLI_rcti_size_y(rect); + tria->size = size = 0.5f * BLI_rcti_size_y(rect); for (a = 0; a < 6; a++) { tria->vec[a][0] = size * g_shape_preset_checkmark_vert[a][0] + centx; @@ -658,187 +1026,213 @@ static void widget_verts_to_triangle_strip(uiWidgetBase *wtb, const int totvert, copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[0]); } -static void widget_verts_to_triangle_strip_open(uiWidgetBase *wtb, const int totvert, float triangle_strip[WIDGET_SIZE_MAX * 2][2]) -{ - int a; - for (a = 0; a < totvert; a++) { - triangle_strip[a * 2][0] = wtb->outer_v[a][0]; - triangle_strip[a * 2][1] = wtb->outer_v[a][1]; - triangle_strip[a * 2 + 1][0] = wtb->outer_v[a][0]; - triangle_strip[a * 2 + 1][1] = wtb->outer_v[a][1] - 1.0f; - } -} - -static void widgetbase_outline(uiWidgetBase *wtb) +static void widgetbase_outline(uiWidgetBase *wtb, unsigned int pos) { float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */ widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, triangle_strip); - glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->totvert * 2 + 2); - glDisableClientState(GL_VERTEX_ARRAY); + widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2); } -static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) +static void widgetbase_set_uniform_alpha_discard( + uiWidgetBase *wtb, + const bool alpha_check, + const float discard_factor) { - int j, a; + if (alpha_check) { + wtb->uniform_params.alpha_discard = -discard_factor; + } + else { + wtb->uniform_params.alpha_discard = discard_factor; + } +} - glEnable(GL_BLEND); +static void widgetbase_set_uniform_alpha_check( + uiWidgetBase *wtb, + const bool alpha_check) +{ + const float discard_factor = fabs(wtb->uniform_params.alpha_discard); + widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor); +} - /* backdrop non AA */ - if (wtb->draw_inner) { - BLI_assert(wtb->totvert != 0); - if (wcol->shaded == 0) { - if (wcol->alpha_check) { - float inner_v_half[WIDGET_SIZE_MAX][2]; - float x_mid = 0.0f; /* used for dumb clamping of values */ +static void widgetbase_set_uniform_discard_factor( + uiWidgetBase *wtb, + const float discard_factor) +{ + bool alpha_check = wtb->uniform_params.alpha_discard < 0.0f; + widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor); +} - /* dark checkers */ - glColor4ub(UI_ALPHA_CHECKER_DARK, UI_ALPHA_CHECKER_DARK, UI_ALPHA_CHECKER_DARK, 255); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v); - glDrawArrays(GL_POLYGON, 0, wtb->totvert); +static void widgetbase_set_uniform_colors_ubv( + uiWidgetBase *wtb, + const unsigned char *col1, const unsigned char *col2, + const unsigned char *outline, + const unsigned char *emboss, + const unsigned char *tria, + const bool alpha_check) +{ + widgetbase_set_uniform_alpha_check(wtb, alpha_check); + rgba_float_args_set_ch(wtb->uniform_params.color_inner1, col1[0], col1[1], col1[2], col1[3]); + rgba_float_args_set_ch(wtb->uniform_params.color_inner2, col2[0], col2[1], col2[2], col2[3]); + rgba_float_args_set_ch(wtb->uniform_params.color_outline, outline[0], outline[1], outline[2], outline[3]); + rgba_float_args_set_ch(wtb->uniform_params.color_emboss, emboss[0], emboss[1], emboss[2], emboss[3]); + rgba_float_args_set_ch(wtb->uniform_params.color_tria, tria[0], tria[1], tria[2], tria[3]); +} - /* light checkers */ - GPU_basic_shader_bind(GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - glColor4ub(UI_ALPHA_CHECKER_LIGHT, UI_ALPHA_CHECKER_LIGHT, UI_ALPHA_CHECKER_LIGHT, 255); - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_CHECKER_8PX); +/* keep in sync with shader */ +#define MAX_WIDGET_BASE_BATCH 6 +#define MAX_WIDGET_PARAMETERS 11 - glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v); - glDrawArrays(GL_POLYGON, 0, wtb->totvert); +struct { + GPUBatch *batch; /* Batch type */ + uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]; + int count; + bool enabled; +} g_widget_base_batch = {0}; - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); +void UI_widgetbase_draw_cache_flush(void) +{ + float checker_params[3] = {UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f}; - /* alpha fill */ - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (g_widget_base_batch.count == 0) + return; - glColor4ubv((unsigned char *)wcol->inner); + GPUBatch *batch = g_widget_base_batch.batch; + if (g_widget_base_batch.count == 1) { + /* draw single */ + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); + GPU_batch_uniform_4fv_array(batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)g_widget_base_batch.params); + GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params); + GPU_batch_draw(batch); + } + else { + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE_INST); + GPU_batch_uniform_4fv_array(batch, "parameters", MAX_WIDGET_PARAMETERS * MAX_WIDGET_BASE_BATCH, + (float *)g_widget_base_batch.params); + GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params); + GPU_matrix_bind(batch->interface); + GPU_batch_draw_range_ex(batch, 0, g_widget_base_batch.count, true); + GPU_batch_program_use_end(batch); + } + g_widget_base_batch.count = 0; +} - for (a = 0; a < wtb->totvert; a++) { - x_mid += wtb->inner_v[a][0]; - } - x_mid /= wtb->totvert; +void UI_widgetbase_draw_cache_begin(void) +{ + BLI_assert(g_widget_base_batch.enabled == false); + g_widget_base_batch.enabled = true; +} - glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v); - glDrawArrays(GL_POLYGON, 0, wtb->totvert); +void UI_widgetbase_draw_cache_end(void) +{ + BLI_assert(g_widget_base_batch.enabled == true); + g_widget_base_batch.enabled = false; - /* 1/2 solid color */ - glColor4ub(wcol->inner[0], wcol->inner[1], wcol->inner[2], 255); + GPU_blend(true); - for (a = 0; a < wtb->totvert; a++) { - inner_v_half[a][0] = MIN2(wtb->inner_v[a][0], x_mid); - inner_v_half[a][1] = wtb->inner_v[a][1]; - } + UI_widgetbase_draw_cache_flush(); - glVertexPointer(2, GL_FLOAT, 0, inner_v_half); - glDrawArrays(GL_POLYGON, 0, wtb->totvert); - glDisableClientState(GL_VERTEX_ARRAY); - } - else { - /* simple fill */ - glColor4ubv((unsigned char *)wcol->inner); + GPU_blend(false); +} - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v); - glDrawArrays(GL_POLYGON, 0, wtb->totvert); - glDisableClientState(GL_VERTEX_ARRAY); - } +static void draw_widgetbase_batch(GPUBatch *batch, uiWidgetBase *wtb) +{ + wtb->uniform_params.tria1_size = wtb->tria1.size; + wtb->uniform_params.tria2_size = wtb->tria2.size; + copy_v2_v2(wtb->uniform_params.tria1_center, wtb->tria1.center); + copy_v2_v2(wtb->uniform_params.tria2_center, wtb->tria2.center); + + if (g_widget_base_batch.enabled) { + if (g_widget_base_batch.batch == NULL) { + g_widget_base_batch.batch = ui_batch_roundbox_widget_get(ROUNDBOX_TRIA_ARROWS); } - else { - char col1[4], col2[4]; - unsigned char col_array[WIDGET_SIZE_MAX * 4]; - unsigned char *col_pt = col_array; - - shadecolors4(col1, col2, wcol->inner, wcol->shadetop, wcol->shadedown); - - for (a = 0; a < wtb->totvert; a++, col_pt += 4) { - round_box_shade_col4_r(col_pt, col1, col2, wtb->inner_uv[a][wtb->draw_shadedir ? 1 : 0]); - } - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, col_array); - glDrawArrays(GL_POLYGON, 0, wtb->totvert); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); + /* draw multi */ + if (batch != g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE] && + batch != g_widget_base_batch.batch) + { + /* issue previous calls before changing batch type. */ + UI_widgetbase_draw_cache_flush(); + g_widget_base_batch.batch = batch; } - } - /* for each AA step */ - if (wtb->draw_outline) { - BLI_assert(wtb->totvert != 0); - float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */ - float triangle_strip_emboss[WIDGET_SIZE_MAX * 2][2]; /* only for emboss */ - - const unsigned char tcol[4] = { - wcol->outline[0], - wcol->outline[1], - wcol->outline[2], - wcol->outline[3] / WIDGET_AA_JITTER, - }; - unsigned char emboss[4]; + /* No need to change batch if tria is not visible. Just scale it to 0. */ + if (batch == g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE]) { + wtb->uniform_params.tria1_size = wtb->uniform_params.tria2_size = 0; + } - widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip); + g_widget_base_batch.params[g_widget_base_batch.count] = wtb->uniform_params; + g_widget_base_batch.count++; - if (wtb->draw_emboss) { - widget_verts_to_triangle_strip_open(wtb, wtb->halfwayvert, triangle_strip_emboss); - UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss); + if (g_widget_base_batch.count == MAX_WIDGET_BASE_BATCH) { + UI_widgetbase_draw_cache_flush(); } + } + else { + float checker_params[3] = {UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f}; + /* draw single */ + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); + GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&wtb->uniform_params); + GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params); + GPU_batch_draw(batch); + } +} - glEnableClientState(GL_VERTEX_ARRAY); - - for (j = 0; j < WIDGET_AA_JITTER; j++) { - glTranslate2fv(jit[j]); +static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol) +{ + unsigned char inner_col1[4] = {0}; + unsigned char inner_col2[4] = {0}; + unsigned char emboss_col[4] = {0}; + unsigned char outline_col[4] = {0}; + unsigned char tria_col[4] = {0}; + /* For color widget. */ + bool alpha_check = (wcol->alpha_check && (wcol->shaded == 0)); - /* outline */ - glColor4ubv(tcol); + GPU_blend(true); - glVertexPointer(2, GL_FLOAT, 0, triangle_strip); - glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->totvert * 2 + 2); + /* backdrop non AA */ + if (wtb->draw_inner) { + if (wcol->shaded == 0) { + /* simple fill */ + inner_col1[0] = inner_col2[0] = (unsigned char)wcol->inner[0]; + inner_col1[1] = inner_col2[1] = (unsigned char)wcol->inner[1]; + inner_col1[2] = inner_col2[2] = (unsigned char)wcol->inner[2]; + inner_col1[3] = inner_col2[3] = (unsigned char)wcol->inner[3]; + } + else { + /* gradient fill */ + shadecolors4((char *)inner_col1, (char *)inner_col2, wcol->inner, wcol->shadetop, wcol->shadedown); + } + } - /* emboss bottom shadow */ - if (wtb->draw_emboss) { - if (emboss[3]) { - glColor4ubv(emboss); - glVertexPointer(2, GL_FLOAT, 0, triangle_strip_emboss); - glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->halfwayvert * 2); - } - } + if (wtb->draw_outline) { + outline_col[0] = wcol->outline[0]; + outline_col[1] = wcol->outline[1]; + outline_col[2] = wcol->outline[2]; + outline_col[3] = wcol->outline[3] / WIDGET_AA_JITTER; - glTranslatef(-jit[j][0], -jit[j][1], 0.0f); + /* emboss bottom shadow */ + if (wtb->draw_emboss) { + UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss_col); } + } - glDisableClientState(GL_VERTEX_ARRAY); + if (wtb->tria1.type != ROUNDBOX_TRIA_NONE) { + tria_col[0] = wcol->item[0]; + tria_col[1] = wcol->item[1]; + tria_col[2] = wcol->item[2]; + tria_col[3] = (unsigned char)((float)wcol->item[3] / WIDGET_AA_JITTER); } - /* decoration */ - if (wtb->tria1.tot || wtb->tria2.tot) { - const unsigned char tcol[4] = { - wcol->item[0], - wcol->item[1], - wcol->item[2], - (unsigned char)((float)wcol->item[3] / WIDGET_AA_JITTER), - }; - glColor4ubv(tcol); - - /* for each AA step */ - for (j = 0; j < WIDGET_AA_JITTER; j++) { - glTranslate2fv(jit[j]); - - if (wtb->tria1.tot) { - shape_preset_draw_trias(&wtb->tria1); - } - if (wtb->tria2.tot) { - shape_preset_draw_trias(&wtb->tria2); - } + /* Draw everything in one drawcall */ + if (inner_col1[3] || inner_col2[3] || outline_col[3] || emboss_col[3] || tria_col[3] || alpha_check) { + widgetbase_set_uniform_colors_ubv(wtb, inner_col1, inner_col2, outline_col, emboss_col, tria_col, alpha_check); - glTranslatef(-jit[j][0], -jit[j][1], 0.0f); - } + GPUBatch *roundbox_batch = ui_batch_roundbox_widget_get(wtb->tria1.type); + draw_widgetbase_batch(roundbox_batch, wtb); } - glDisable(GL_BLEND); + GPU_blend(false); } /* *********************** text/icon ************************************** */ @@ -876,16 +1270,16 @@ static int ui_but_draw_menu_icon(const uiBut *but) /* icons have been standardized... and this call draws in untransformed coordinates */ static void widget_draw_icon_ex( - const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, const bool show_menu_icon, - const int icon_size) + const uiBut *but, BIFIconID icon, float alpha, + const rcti *rect, const int icon_size) { float xs = 0.0f, ys = 0.0f; float aspect, height; if (but->flag & UI_BUT_ICON_PREVIEW) { - glEnable(GL_BLEND); + GPU_blend(true); widget_draw_preview(icon, alpha, rect); - glDisable(GL_BLEND); + GPU_blend(false); return; } @@ -913,14 +1307,14 @@ static void widget_draw_icon_ex( } } - glEnable(GL_BLEND); + GPU_blend(true); if (icon && icon != ICON_BLANK1) { float ofs = 1.0f / aspect; if (but->drawflag & UI_BUT_ICON_LEFT) { /* special case - icon_only pie buttons */ - if (ui_block_is_pie_menu(but->block) && but->type != UI_BTYPE_MENU && but->str && but->str[0] == '\0') + if (ui_block_is_pie_menu(but->block) && !ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_POPOVER) && but->str && but->str[0] == '\0') xs = rect->xmin + 2.0f * ofs; else if (but->dt == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) xs = rect->xmin + 2.0f * ofs; @@ -943,24 +1337,44 @@ static void widget_draw_icon_ex( float rgb[3] = {1.25f, 1.25f, 1.25f}; UI_icon_draw_aspect_color(xs, ys, icon, aspect, rgb); } - else + else if ((but->flag & (UI_ACTIVE | UI_SELECT | UI_SELECT_DRAW)) || !UI_but_is_tool(but)) { UI_icon_draw_aspect(xs, ys, icon, aspect, alpha); + } + else { + const bTheme *btheme = UI_GetTheme(); + UI_icon_draw_desaturate(xs, ys, icon, aspect, alpha, 1.0 - btheme->tui.icon_saturation); + } } - if (show_menu_icon) { - xs = rect->xmax - UI_DPI_ICON_SIZE - aspect; - ys = (rect->ymin + rect->ymax - height) / 2.0f; - - UI_icon_draw_aspect(xs, ys, ICON_RIGHTARROW_THIN, aspect, alpha); - } - - glDisable(GL_BLEND); + GPU_blend(false); } static void widget_draw_icon( - const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, const bool show_menu_icon) + const uiBut *but, BIFIconID icon, float alpha, const rcti *rect) { - widget_draw_icon_ex(but, icon, alpha, rect, show_menu_icon, ICON_DEFAULT_HEIGHT); + widget_draw_icon_ex(but, icon, alpha, rect, ICON_DEFAULT_HEIGHT); +} + +static void widget_draw_submenu_tria(const uiBut *but, const rcti *rect, const uiWidgetColors *wcol) +{ + const float aspect = but->block->aspect / UI_DPI_FAC; + const int tria_height = (int)(ICON_DEFAULT_HEIGHT / aspect); + const int tria_width = (int)(ICON_DEFAULT_WIDTH / aspect) - 2 * U.pixelsize; + const int xs = rect->xmax - tria_width; + const int ys = (rect->ymin + rect->ymax - tria_height) / 2.0f; + float col[4]; + rctf tria_rect; + + rgba_uchar_to_float(col, (const uchar *)wcol->text); + col[3] *= 0.8f; + + BLI_rctf_init(&tria_rect, xs, xs + tria_width, ys, ys + tria_height); + BLI_rctf_scale(&tria_rect, 0.4f); + + GPU_blend(true); + UI_widgetbase_draw_cache_flush(); + GPU_blend(false); + ui_draw_anti_tria_rect(&tria_rect, 'h', col); } static void ui_text_clip_give_prev_off(uiBut *but, const char *str) @@ -1120,6 +1534,8 @@ float UI_text_clip_middle_ex( BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); } + BLI_assert(strwidth <= okwidth); + return strwidth; } @@ -1129,7 +1545,7 @@ float UI_text_clip_middle_ex( static void ui_text_clip_middle(uiFontStyle *fstyle, uiBut *but, const rcti *rect) { /* No margin for labels! */ - const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f); + const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU, UI_BTYPE_POPOVER) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f); const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0); const size_t max_len = sizeof(but->drawstr); const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f; @@ -1145,7 +1561,7 @@ static void ui_text_clip_middle(uiFontStyle *fstyle, uiBut *but, const rcti *rec static void ui_text_clip_middle_protect_right(uiFontStyle *fstyle, uiBut *but, const rcti *rect, const char rsep) { /* No margin for labels! */ - const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f); + const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU, UI_BTYPE_POPOVER) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f); const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0); const size_t max_len = sizeof(but->drawstr); const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f; @@ -1298,6 +1714,7 @@ static void widget_draw_text_ime_underline( int ofs_x, width; int rect_x = BLI_rcti_size_x(rect); int sel_start = ime_data->sel_start, sel_end = ime_data->sel_end; + float fcol[4]; if (drawstr[0] != 0) { if (but->pos >= but->ofs) { @@ -1311,8 +1728,8 @@ static void widget_draw_text_ime_underline( fstyle->uifont_id, drawstr + but->ofs, ime_data->composite_len + but->pos - but->ofs); - glColor4ubv((unsigned char *)wcol->text); - UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 1); + rgba_uchar_to_float(fcol, wcol->text); + UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 1, fcol); /* draw the thick line */ if (sel_start != -1 && sel_end != -1) { @@ -1330,7 +1747,7 @@ static void widget_draw_text_ime_underline( fstyle->uifont_id, drawstr + but->ofs, sel_end + sel_start - but->ofs); - UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 2); + UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 2, fcol); } } } @@ -1405,6 +1822,10 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b int selsta_draw, selwidth_draw; if (drawstr[0] != 0) { + /* We are drawing on top of widget bases. Flush cache. */ + GPU_blend(true); + UI_widgetbase_draw_cache_flush(); + GPU_blend(false); if (but->selsta >= but->ofs) { selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selsta - but->ofs); @@ -1415,11 +1836,16 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b selwidth_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selend - but->ofs); - glColor4ubv((unsigned char *)wcol->item); - glRecti(rect->xmin + selsta_draw, - rect->ymin + 2, - min_ii(rect->xmin + selwidth_draw, rect->xmax - 2), - rect->ymax - 2); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4ubv((unsigned char *)wcol->item); + immRecti(pos, rect->xmin + selsta_draw, + rect->ymin + 2, + min_ii(rect->xmin + selwidth_draw, rect->xmax - 2), + rect->ymax - 2); + + immUnbindProgram(); } } @@ -1441,14 +1867,23 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b else { t = 0; } + /* We are drawing on top of widget bases. Flush cache. */ + GPU_blend(true); + UI_widgetbase_draw_cache_flush(); + GPU_blend(false); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - glColor3f(0.2, 0.6, 0.9); + immUniformColor3f(0.2f, 0.6f, 0.9f); tx = rect->xmin + t + 2; ty = rect->ymin + 2; /* draw cursor */ - glRecti(rect->xmin + t, ty, tx, rect->ymax - 2); + immRecti(pos, rect->xmin + t, ty, tx, rect->ymax - 2); + + immUnbindProgram(); } #ifdef WITH_INPUT_IME @@ -1473,7 +1908,7 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b #endif /* cut string in 2 parts - only for menu entries */ - if ((but->block->flag & UI_BLOCK_LOOP) && + if ((but->drawflag & UI_BUT_HAS_SHORTCUT) && (but->editstr == NULL)) { if (but->flag & UI_BUT_HAS_SEP_CHAR) { @@ -1509,44 +1944,46 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b } #endif - glColor4ubv((unsigned char *)wcol->text); - if (!use_right_only) { /* for underline drawing */ float font_xofs, font_yofs; - UI_fontstyle_draw_ex( - fstyle, rect, drawstr + but->ofs, - drawstr_left_len - but->ofs, &font_xofs, &font_yofs); + int drawlen = (drawstr_left_len == INT_MAX) ? strlen(drawstr + but->ofs) : (drawstr_left_len - but->ofs); - if (but->menu_key != '\0') { - char fixedbuf[128]; - const char *str; + if (drawlen > 0) { + UI_fontstyle_draw_ex(fstyle, rect, drawstr + but->ofs, (unsigned char *)wcol->text, + drawlen, &font_xofs, &font_yofs); - BLI_strncpy(fixedbuf, drawstr + but->ofs, min_ii(sizeof(fixedbuf), drawstr_left_len)); + if (but->menu_key != '\0') { + char fixedbuf[128]; + const char *str; - str = strchr(fixedbuf, but->menu_key - 32); /* upper case */ - if (str == NULL) - str = strchr(fixedbuf, but->menu_key); + BLI_strncpy(fixedbuf, drawstr + but->ofs, min_ii(sizeof(fixedbuf), drawlen)); - if (str) { - int ul_index = -1; - float ul_advance; + str = strchr(fixedbuf, but->menu_key - 32); /* upper case */ + if (str == NULL) + str = strchr(fixedbuf, but->menu_key); - ul_index = (int)(str - fixedbuf); + if (str) { + int ul_index = -1; + float ul_advance; - if (fstyle->kerning == 1) { - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } + ul_index = (int)(str - fixedbuf); + + if (fstyle->kerning == 1) { + BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); + } - fixedbuf[ul_index] = '\0'; - ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index); + fixedbuf[ul_index] = '\0'; + ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index); - BLF_position(fstyle->uifont_id, rect->xmin + font_xofs + ul_advance, rect->ymin + font_yofs, 0.0f); - BLF_draw(fstyle->uifont_id, "_", 2); + BLF_position(fstyle->uifont_id, rect->xmin + font_xofs + ul_advance, rect->ymin + font_yofs, 0.0f); + BLF_color4ubv(fstyle->uifont_id, (unsigned char *)wcol->text); + BLF_draw(fstyle->uifont_id, "_", 2); - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); + if (fstyle->kerning == 1) { + BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); + } } } } @@ -1554,9 +1991,15 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b /* part text right aligned */ if (drawstr_right) { + char col[4]; + copy_v4_v4_char(col, wcol->text); + if (but->drawflag & UI_BUT_HAS_SHORTCUT) { + col[3] *= 0.5f; + } + fstyle->align = UI_STYLE_TEXT_RIGHT; rect->xmax -= UI_TEXT_CLIP_MARGIN; - UI_fontstyle_draw(fstyle, rect, drawstr_right); + UI_fontstyle_draw(fstyle, rect, drawstr_right, (const uchar *)col); } } @@ -1571,10 +2014,10 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB ui_but_text_password_hide(password_str, but, false); /* check for button text label */ - if (but->type == UI_BTYPE_MENU && (but->flag & UI_BUT_NODE_LINK)) { + if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_POPOVER) && (but->flag & UI_BUT_NODE_LINK)) { rcti temp = *rect; temp.xmin = rect->xmax - BLI_rcti_size_y(rect) - 1; - widget_draw_icon(but, ICON_LAYER_USED, alpha, &temp, false); + widget_draw_icon(but, ICON_LAYER_USED, alpha, &temp); rect->xmax = temp.xmin; } @@ -1596,9 +2039,9 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB /* draw icon in rect above the space reserved for the label */ rect->ymin += text_size; - glEnable(GL_BLEND); + GPU_blend(true); widget_draw_preview(icon, alpha, rect); - glDisable(GL_BLEND); + GPU_blend(false); /* offset rect to draw label in */ rect->ymin -= text_size; @@ -1609,25 +2052,52 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB } /* Icons on the left with optional text label on the right */ else if (but->flag & UI_HAS_ICON || show_menu_icon) { + const bool is_tool = UI_but_is_tool(but); + + /* XXX add way to draw icons at a different size! + * Use small icons for popup. */ +#ifdef USE_UI_TOOLBAR_HACK + const float aspect_orig = but->block->aspect; + if (is_tool && (but->block->flag & UI_BLOCK_POPOVER)) { + but->block->aspect *= 2.0f; + } +#endif + const BIFIconID icon = (but->flag & UI_HAS_ICON) ? but->icon + but->iconadd : ICON_NONE; - const float icon_size = ICON_DEFAULT_WIDTH_SCALE; + int icon_size_init = is_tool ? ICON_DEFAULT_HEIGHT_TOOLBAR : ICON_DEFAULT_HEIGHT; + const float icon_size = icon_size_init / (but->block->aspect / UI_DPI_FAC); + +#ifdef USE_UI_TOOLBAR_HACK + if (is_tool) { + /* pass (even if its a menu toolbar) */ + but->drawflag |= UI_BUT_TEXT_LEFT; + but->drawflag |= UI_BUT_ICON_LEFT; + } +#endif /* menu item - add some more padding so menus don't feel cramped. it must * be part of the button so that this area is still clickable */ - if (ui_block_is_pie_menu(but->block)) { + if (is_tool) { + /* pass (even if its a menu toolbar) */ + } + else if (ui_block_is_pie_menu(but->block)) { if (but->dt == UI_EMBOSS_RADIAL) rect->xmin += 0.3f * U.widget_unit; } else if (ui_block_is_menu(but->block)) rect->xmin += 0.3f * U.widget_unit; - widget_draw_icon(but, icon, alpha, rect, show_menu_icon); - - rect->xmin += icon_size; - /* without this menu keybindings will overlap the arrow icon [#38083] */ + widget_draw_icon(but, icon, alpha, rect); if (show_menu_icon) { - rect->xmax -= icon_size / 2.0f; + BLI_assert(but->block->content_hints & BLOCK_CONTAINS_SUBMENU_BUT); + widget_draw_submenu_tria(but, rect, wcol); } + +#ifdef USE_UI_TOOLBAR_HACK + but->block->aspect = aspect_orig; +#endif + + rect->xmin += icon_size; } if (but->editstr || (but->drawflag & UI_BUT_TEXT_LEFT)) { @@ -1637,6 +2107,12 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB rect->xmax -= (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect; } + /* Menu contains sub-menu items with triangle icon on their right. Shortcut + * strings should be drawn with some padding to the right then. */ + if (ui_block_is_menu(but->block) && (but->block->content_hints & BLOCK_CONTAINS_SUBMENU_BUT)) { + rect->xmax -= UI_MENU_SUBMENU_PADDING; + } + /* extra icons, e.g. 'x' icon to clear text or icon for eyedropper */ if (extra_icon_type != UI_BUT_ICONEXTRA_NONE) { rcti temp = *rect; @@ -1644,10 +2120,10 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB temp.xmin = temp.xmax - (BLI_rcti_size_y(rect) * 1.08f); if (extra_icon_type == UI_BUT_ICONEXTRA_CLEAR) { - widget_draw_icon(but, ICON_PANEL_CLOSE, alpha, &temp, false); + widget_draw_icon(but, ICON_PANEL_CLOSE, alpha, &temp); } else if (extra_icon_type == UI_BUT_ICONEXTRA_EYEDROPPER) { - widget_draw_icon(but, ICON_EYEDROPPER, alpha, &temp, false); + widget_draw_icon(but, ICON_EYEDROPPER, alpha, &temp); } else { BLI_assert(0); @@ -1680,6 +2156,9 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB widget_draw_text(fstyle, wcol, but, rect); ui_but_text_password_hide(password_str, but, true); + + /* if a widget uses font shadow it has to be deactivated now */ + BLF_disable(fstyle->uifont_id, BLF_SHADOW); } #undef UI_TEXT_CLIP_MARGIN @@ -1687,297 +2166,6 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB /* *********************** widget types ************************************* */ -static struct uiWidgetStateColors wcol_state_colors = { - {115, 190, 76, 255}, - {90, 166, 51, 255}, - {240, 235, 100, 255}, - {215, 211, 75, 255}, - {180, 0, 255, 255}, - {153, 0, 230, 255}, - 0.5f, 0.0f -}; - -static struct uiWidgetColors wcol_num = { - {25, 25, 25, 255}, - {180, 180, 180, 255}, - {153, 153, 153, 255}, - {90, 90, 90, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 1, - -20, 0 -}; - -static struct uiWidgetColors wcol_numslider = { - {25, 25, 25, 255}, - {180, 180, 180, 255}, - {153, 153, 153, 255}, - {128, 128, 128, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 1, - -20, 0 -}; - -static struct uiWidgetColors wcol_text = { - {25, 25, 25, 255}, - {153, 153, 153, 255}, - {153, 153, 153, 255}, - {90, 90, 90, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 1, - 0, 25 -}; - -static struct uiWidgetColors wcol_option = { - {0, 0, 0, 255}, - {70, 70, 70, 255}, - {70, 70, 70, 255}, - {255, 255, 255, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 1, - 15, -15 -}; - -/* button that shows popup */ -static struct uiWidgetColors wcol_menu = { - {0, 0, 0, 255}, - {70, 70, 70, 255}, - {70, 70, 70, 255}, - {255, 255, 255, 255}, - - {255, 255, 255, 255}, - {204, 204, 204, 255}, - - 1, - 15, -15 -}; - -/* button that starts pulldown */ -static struct uiWidgetColors wcol_pulldown = { - {0, 0, 0, 255}, - {63, 63, 63, 255}, - {86, 128, 194, 255}, - {255, 255, 255, 255}, - - {0, 0, 0, 255}, - {0, 0, 0, 255}, - - 0, - 25, -20 -}; - -/* button inside menu */ -static struct uiWidgetColors wcol_menu_item = { - {0, 0, 0, 255}, - {0, 0, 0, 0}, - {86, 128, 194, 255}, - {172, 172, 172, 128}, - - {255, 255, 255, 255}, - {0, 0, 0, 255}, - - 1, - 38, 0 -}; - -/* backdrop menu + title text color */ -static struct uiWidgetColors wcol_menu_back = { - {0, 0, 0, 255}, - {25, 25, 25, 230}, - {45, 45, 45, 230}, - {100, 100, 100, 255}, - - {160, 160, 160, 255}, - {255, 255, 255, 255}, - - 0, - 25, -20 -}; - -/* pie menus */ -static struct uiWidgetColors wcol_pie_menu = { - {10, 10, 10, 200}, - {25, 25, 25, 230}, - {140, 140, 140, 255}, - {45, 45, 45, 230}, - - {160, 160, 160, 255}, - {255, 255, 255, 255}, - - 1, - 10, -10 -}; - - -/* tooltip color */ -static struct uiWidgetColors wcol_tooltip = { - {0, 0, 0, 255}, - {25, 25, 25, 230}, - {45, 45, 45, 230}, - {100, 100, 100, 255}, - - {255, 255, 255, 255}, - {255, 255, 255, 255}, - - 0, - 25, -20 -}; - -static struct uiWidgetColors wcol_radio = { - {0, 0, 0, 255}, - {70, 70, 70, 255}, - {86, 128, 194, 255}, - {255, 255, 255, 255}, - - {255, 255, 255, 255}, - {0, 0, 0, 255}, - - 1, - 15, -15 -}; - -static struct uiWidgetColors wcol_regular = { - {25, 25, 25, 255}, - {153, 153, 153, 255}, - {100, 100, 100, 255}, - {25, 25, 25, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 0, - 0, 0 -}; - -static struct uiWidgetColors wcol_tool = { - {25, 25, 25, 255}, - {153, 153, 153, 255}, - {100, 100, 100, 255}, - {25, 25, 25, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 1, - 15, -15 -}; - -static struct uiWidgetColors wcol_box = { - {25, 25, 25, 255}, - {128, 128, 128, 255}, - {100, 100, 100, 255}, - {25, 25, 25, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 0, - 0, 0 -}; - -static struct uiWidgetColors wcol_toggle = { - {25, 25, 25, 255}, - {153, 153, 153, 255}, - {100, 100, 100, 255}, - {25, 25, 25, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 0, - 0, 0 -}; - -static struct uiWidgetColors wcol_scroll = { - {50, 50, 50, 180}, - {80, 80, 80, 180}, - {100, 100, 100, 180}, - {128, 128, 128, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 1, - 5, -5 -}; - -static struct uiWidgetColors wcol_progress = { - {0, 0, 0, 255}, - {190, 190, 190, 255}, - {100, 100, 100, 180}, - {128, 128, 128, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 0, - 0, 0 -}; - -static struct uiWidgetColors wcol_list_item = { - {0, 0, 0, 255}, - {0, 0, 0, 0}, - {86, 128, 194, 255}, - {90, 90, 90, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 0, - 0, 0 -}; - -/* free wcol struct to play with */ -static struct uiWidgetColors wcol_tmp = { - {0, 0, 0, 255}, - {128, 128, 128, 255}, - {100, 100, 100, 255}, - {25, 25, 25, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 0, - 0, 0 -}; - - -/* called for theme init (new theme) and versions */ -void ui_widget_color_init(ThemeUI *tui) -{ - tui->wcol_regular = wcol_regular; - tui->wcol_tool = wcol_tool; - tui->wcol_text = wcol_text; - tui->wcol_radio = wcol_radio; - tui->wcol_option = wcol_option; - tui->wcol_toggle = wcol_toggle; - tui->wcol_num = wcol_num; - tui->wcol_numslider = wcol_numslider; - tui->wcol_menu = wcol_menu; - tui->wcol_pulldown = wcol_pulldown; - tui->wcol_menu_back = wcol_menu_back; - tui->wcol_pie_menu = wcol_pie_menu; - tui->wcol_tooltip = wcol_tooltip; - tui->wcol_menu_item = wcol_menu_item; - tui->wcol_box = wcol_box; - tui->wcol_scroll = wcol_scroll; - tui->wcol_list_item = wcol_list_item; - tui->wcol_progress = wcol_progress; - - tui->wcol_state = wcol_state_colors; -} - /* ************ button callbacks, state ***************** */ static void widget_state_blend(char cp[3], const char cpstate[3], const float fac) @@ -2006,6 +2194,13 @@ static void ui_widget_color_disabled(uiWidgetType *wt) wt->wcol_theme = &wcol_theme_s; } +static void widget_active_color(char cp[3]) +{ + cp[0] = cp[0] >= 240 ? 255 : cp[0] + 15; + cp[1] = cp[1] >= 240 ? 255 : cp[1] + 15; + cp[2] = cp[2] >= 240 ? 255 : cp[2] + 15; +} + /* copy colors from theme, and set changes in it based on state */ static void widget_state(uiWidgetType *wt, int state) { @@ -2032,6 +2227,8 @@ static void widget_state(uiWidgetType *wt, int state) widget_state_blend(wt->wcol.inner, wcol_state->inner_anim_sel, wcol_state->blend); else if (state & UI_BUT_DRIVEN) widget_state_blend(wt->wcol.inner, wcol_state->inner_driven_sel, wcol_state->blend); + else if (state & UI_BUT_OVERRIDEN) + widget_state_blend(wt->wcol.inner, wcol_state->inner_overridden_sel, wcol_state->blend); copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); @@ -2045,11 +2242,11 @@ static void widget_state(uiWidgetType *wt, int state) widget_state_blend(wt->wcol.inner, wcol_state->inner_anim, wcol_state->blend); else if (state & UI_BUT_DRIVEN) widget_state_blend(wt->wcol.inner, wcol_state->inner_driven, wcol_state->blend); + else if (state & UI_BUT_OVERRIDEN) + widget_state_blend(wt->wcol.inner, wcol_state->inner_overridden, wcol_state->blend); if (state & UI_ACTIVE) { /* mouse over? */ - wt->wcol.inner[0] = wt->wcol.inner[0] >= 240 ? 255 : wt->wcol.inner[0] + 15; - wt->wcol.inner[1] = wt->wcol.inner[1] >= 240 ? 255 : wt->wcol.inner[1] + 15; - wt->wcol.inner[2] = wt->wcol.inner[2] >= 240 ? 255 : wt->wcol.inner[2] + 15; + widget_active_color(wt->wcol.inner); } } @@ -2090,6 +2287,8 @@ static void widget_state_numslider(uiWidgetType *wt, int state) widget_state_blend(wt->wcol.item, wcol_state->inner_anim_sel, blend); else if (state & UI_BUT_DRIVEN) widget_state_blend(wt->wcol.item, wcol_state->inner_driven_sel, blend); + else if (state & UI_BUT_OVERRIDEN) + widget_state_blend(wt->wcol.item, wcol_state->inner_overridden_sel, blend); if (state & UI_SELECT) SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); @@ -2101,6 +2300,8 @@ static void widget_state_numslider(uiWidgetType *wt, int state) widget_state_blend(wt->wcol.item, wcol_state->inner_anim, blend); else if (state & UI_BUT_DRIVEN) widget_state_blend(wt->wcol.item, wcol_state->inner_driven, blend); + else if (state & UI_BUT_OVERRIDEN) + widget_state_blend(wt->wcol.item, wcol_state->inner_overridden, blend); } } @@ -2126,15 +2327,9 @@ static void widget_state_nothing(uiWidgetType *wt, int UNUSED(state)) } /* special case, button that calls pulldown */ -static void widget_state_pulldown(uiWidgetType *wt, int state) +static void widget_state_pulldown(uiWidgetType *wt, int UNUSED(state)) { wt->wcol = *(wt->wcol_theme); - - copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel); - copy_v3_v3_char(wt->wcol.outline, wt->wcol.inner); - - if (state & UI_ACTIVE) - copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); } /* special case, pie menu items */ @@ -2150,18 +2345,22 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, int state) copy_v4_v4_char(wt->wcol.inner, wt->wcol.item); wt->wcol.inner[3] = 64; } - /* regular disabled */ - else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { - widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f); - } - /* regular active */ - else if (state & UI_SELECT) { - copy_v4_v4_char(wt->wcol.outline, wt->wcol.inner_sel); - copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); - } - else if (state & UI_ACTIVE) { - copy_v4_v4_char(wt->wcol.inner, wt->wcol.item); - copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); + else { + /* regular active */ + if (state & (UI_SELECT | UI_ACTIVE)) { + copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); + } + else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + /* regular disabled */ + widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f); + } + + if (state & UI_SELECT) { + copy_v4_v4_char(wt->wcol.outline, wt->wcol.inner_sel); + } + else if (state & UI_ACTIVE) { + copy_v4_v4_char(wt->wcol.inner, wt->wcol.item); + } } } @@ -2178,14 +2377,19 @@ static void widget_state_menu_item(uiWidgetType *wt, int state) copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel); wt->wcol.inner[3] = 64; } - /* regular disabled */ - else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { - widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f); - } - /* regular active */ - else if (state & UI_ACTIVE) { - copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel); - copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); + else { + /* regular active */ + if (state & UI_ACTIVE) { + copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); + } + else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + /* regular disabled */ + widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f); + } + + if (state & UI_ACTIVE) { + copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel); + } } } @@ -2219,22 +2423,23 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r /* we draw a number of increasing size alpha quad strips */ alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout; - glEnableClientState(GL_VERTEX_ARRAY); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); for (step = 1; step <= (int)radout; step++) { float expfac = sqrtf(step / radout); round_box_shadow_edges(wtb.outer_v, &rect1, radin, UI_CNR_ALL, (float)step); - glColor4f(0.0f, 0.0f, 0.0f, alphastep * (1.0f - expfac)); + immUniformColor4f(0.0f, 0.0f, 0.0f, alphastep * (1.0f - expfac)); widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip); - glVertexPointer(2, GL_FLOAT, 0, triangle_strip); - glDrawArrays(GL_TRIANGLE_STRIP, 0, totvert * 2); /* add + 2 for getting a complete soft rect. Now it skips top edge to allow transparent menus */ + widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, totvert * 2); } - glDisableClientState(GL_VERTEX_ARRAY); + immUnbindProgram(); } static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int direction) @@ -2258,33 +2463,33 @@ static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int dir rect->ymax += 0.1f * U.widget_unit; } - glEnable(GL_BLEND); - widget_softshadow(rect, roundboxalign, 0.25f * U.widget_unit); + GPU_blend(true); + widget_softshadow(rect, roundboxalign, wcol->roundness * U.widget_unit); - round_box_edges(&wtb, roundboxalign, rect, 0.25f * U.widget_unit); + round_box_edges(&wtb, roundboxalign, rect, wcol->roundness * U.widget_unit); wtb.draw_emboss = false; widgetbase_draw(&wtb, wcol); - glDisable(GL_BLEND); + GPU_blend(false); } - static void ui_hsv_cursor(float x, float y) { - glPushMatrix(); - glTranslatef(x, y, 0.0f); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - glColor3f(1.0f, 1.0f, 1.0f); - glutil_draw_filled_arc(0.0f, M_PI * 2.0, 3.0f * U.pixelsize, 8); + immUniformColor3f(1.0f, 1.0f, 1.0f); + imm_draw_circle_fill_2d(pos, x, y, 3.0f * U.pixelsize, 8); - glEnable(GL_BLEND); - glEnable(GL_LINE_SMOOTH); - glColor3f(0.0f, 0.0f, 0.0f); - glutil_draw_lined_arc(0.0f, M_PI * 2.0, 3.0f * U.pixelsize, 12); - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); + GPU_blend(true); + GPU_line_smooth(true); + immUniformColor3f(0.0f, 0.0f, 0.0f); + imm_draw_circle_wire_2d(pos, x, y, 3.0f * U.pixelsize, 12); + GPU_blend(false); + GPU_line_smooth(false); - glPopMatrix(); + immUnbindProgram(); } void ui_hsvcircle_vals_from_pos( @@ -2325,18 +2530,17 @@ void ui_hsvcircle_pos_from_vals(uiBut *but, const rcti *rect, float *hsv, float static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti *rect) { + /* TODO(merwin): reimplement as shader for pixel-perfect colors */ + const int tot = 64; const float radstep = 2.0f * (float)M_PI / (float)tot; const float centx = BLI_rcti_cent_x_fl(rect); const float centy = BLI_rcti_cent_y_fl(rect); float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f; - /* gouraud triangle fan */ ColorPicker *cpicker = but->custom_data; const float *hsv_ptr = cpicker->color_data; - float xpos, ypos, ang = 0.0f; float rgb[3], hsvo[3], hsv[3], col[3], colcent[3]; - int a; bool color_profile = ui_but_is_colorpicker_display_space(but); /* color */ @@ -2367,11 +2571,18 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti * ui_color_picker_to_rgb(0.0f, 0.0f, hsv[2], colcent, colcent + 1, colcent + 2); - glBegin(GL_TRIANGLE_FAN); - glColor3fv(colcent); - glVertex2f(centx, centy); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); - for (a = 0; a <= tot; a++, ang += radstep) { + immBegin(GPU_PRIM_TRI_FAN, tot + 2); + immAttrib3fv(color, colcent); + immVertex2f(pos, centx, centy); + + float ang = 0.0f; + for (int a = 0; a <= tot; a++, ang += radstep) { float si = sinf(ang); float co = cosf(ang); @@ -2379,25 +2590,32 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti * ui_color_picker_to_rgb_v(hsv, col); - glColor3fv(col); - glVertex2f(centx + co * radius, centy + si * radius); + immAttrib3fv(color, col); + immVertex2f(pos, centx + co * radius, centy + si * radius); } - glEnd(); + immEnd(); + immUnbindProgram(); /* fully rounded outline */ - glPushMatrix(); - glTranslatef(centx, centy, 0.0f); - glEnable(GL_BLEND); - glEnable(GL_LINE_SMOOTH); - glColor3ubv((unsigned char *)wcol->outline); - glutil_draw_lined_arc(0.0f, M_PI * 2.0, radius, tot + 1); - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); - glPopMatrix(); + format = immVertexFormat(); + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + GPU_blend(true); + GPU_line_smooth(true); + + immUniformColor3ubv((unsigned char *)wcol->outline); + imm_draw_circle_wire_2d(pos, centx, centy, radius, tot); + + immUnbindProgram(); + + GPU_blend(false); + GPU_line_smooth(false); /* cursor */ + float xpos, ypos; ui_hsvcircle_pos_from_vals(but, rect, hsvo, &xpos, &ypos); - ui_hsv_cursor(xpos, ypos); } @@ -2407,7 +2625,8 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti * void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, const float alpha) { /* allows for 4 steps (red->yellow) */ - const float color_step = 1.0f / 48.0f; + const int steps = 48; + const float color_step = 1.0f / steps; int a; float h = hsv[0], s = hsv[1], v = hsv[2]; float dx, dy, sx1, sx2, sy; @@ -2463,7 +2682,12 @@ void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, cons } /* old below */ + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); + immBegin(GPU_PRIM_TRIS, steps * 3 * 6); for (dx = 0.0f; dx < 0.999f; dx += color_step) { /* 0.999 = prevent float inaccuracy for steps */ const float dx_next = dx + color_step; @@ -2521,22 +2745,29 @@ void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, cons sy = rect->ymin; dy = (float)BLI_rcti_size_y(rect) / 3.0f; - glBegin(GL_QUADS); for (a = 0; a < 3; a++, sy += dy) { - glColor4f(col0[a][0], col0[a][1], col0[a][2], alpha); - glVertex2f(sx1, sy); + immAttrib4f(col, col0[a][0], col0[a][1], col0[a][2], alpha); + immVertex2f(pos, sx1, sy); + + immAttrib4f(col, col1[a][0], col1[a][1], col1[a][2], alpha); + immVertex2f(pos, sx2, sy); + + immAttrib4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha); + immVertex2f(pos, sx2, sy + dy); - glColor4f(col1[a][0], col1[a][1], col1[a][2], alpha); - glVertex2f(sx2, sy); + immAttrib4f(col, col0[a][0], col0[a][1], col0[a][2], alpha); + immVertex2f(pos, sx1, sy); - glColor4f(col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha); - glVertex2f(sx2, sy + dy); + immAttrib4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha); + immVertex2f(pos, sx2, sy + dy); - glColor4f(col0[a + 1][0], col0[a + 1][1], col0[a + 1][2], alpha); - glVertex2f(sx1, sy + dy); + immAttrib4f(col, col0[a + 1][0], col0[a + 1][1], col0[a + 1][2], alpha); + immVertex2f(pos, sx1, sy + dy); } - glEnd(); } + immEnd(); + + immUnbindProgram(); } bool ui_but_is_colorpicker_display_space(uiBut *but) @@ -2612,15 +2843,20 @@ static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect) ui_hsv_cursor(x, y); /* outline */ - glColor3ub(0, 0, 0); - fdrawbox((rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax)); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3ub(0, 0, 0); + imm_draw_box_wire_2d(pos, (rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax)); + immUnbindProgram(); } /* vertical 'value' slider, using new widget code */ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect) { + bTheme *btheme = UI_GetTheme(); + uiWidgetColors *wcol = &btheme->tui.wcol_numslider; uiWidgetBase wtb; - const float rad = 0.5f * BLI_rcti_size_x(rect); + const float rad = wcol->roundness * BLI_rcti_size_x(rect); float x, y; float rgb[3], hsv[3], v; bool color_profile = but->block->color_profile; @@ -2654,13 +2890,21 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect) round_box_edges(&wtb, UI_CNR_ALL, rect, rad); /* setup temp colors */ - wcol_tmp.outline[0] = wcol_tmp.outline[1] = wcol_tmp.outline[2] = 0; - wcol_tmp.inner[0] = wcol_tmp.inner[1] = wcol_tmp.inner[2] = 128; - wcol_tmp.shadetop = 127; - wcol_tmp.shadedown = -128; - wcol_tmp.shaded = 1; - - widgetbase_draw(&wtb, &wcol_tmp); + widgetbase_draw( + &wtb, + &((uiWidgetColors){ + .outline = {0, 0, 0, 255}, + .inner = {128, 128, 128, 255}, + .shadetop = 127, + .shadedown = -128, + .shaded = 1, + }) + ); + + /* We are drawing on top of widget bases. Flush cache. */ + GPU_blend(true); + UI_widgetbase_draw_cache_flush(); + GPU_blend(false); /* cursor */ x = rect->xmin + 0.5f * BLI_rcti_size_x(rect); @@ -2670,6 +2914,20 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect) ui_hsv_cursor(x, y); } +/* Generic round-box drawing. */ +static void ui_draw_roundbox(const rcti *rect, const float rad, const uiWidgetColors *wcol) +{ + uiWidgetBase wtb; + widget_init(&wtb); + round_box_edges(&wtb, UI_CNR_ALL, rect, rad); + widgetbase_draw(&wtb, wcol); + + /* We are drawing on top of widget bases. Flush cache. */ + GPU_blend(true); + UI_widgetbase_draw_cache_flush(); + GPU_blend(false); +} + /* ************ separator, for menus etc ***************** */ static void ui_draw_separator(const rcti *rect, uiWidgetColors *wcol) @@ -2682,19 +2940,29 @@ static void ui_draw_separator(const rcti *rect, uiWidgetColors *wcol) 30 }; - glEnable(GL_BLEND); - glColor4ubv(col); - glLineWidth(1.0f); - sdrawline(rect->xmin, y, rect->xmax, y); - glDisable(GL_BLEND); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + GPU_blend(true); + immUniformColor4ubv(col); + GPU_line_width(1.0f); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, rect->xmin, y); + immVertex2f(pos, rect->xmax, y); + immEnd(); + + GPU_blend(false); + + immUnbindProgram(); } /* ************ button callbacks, draw ***************** */ static void widget_numbut_draw(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, bool emboss) { uiWidgetBase wtb; - const float rad = 0.5f * BLI_rcti_size_y(rect); - float textofs = rad * 0.85f; + const float rad = wcol->roundness * BLI_rcti_size_y(rect); + const int handle_width = min_ii(BLI_rcti_size_x(rect) / 3, BLI_rcti_size_y(rect) * 0.7f); if (state & UI_SELECT) SWAP(short, wcol->shadetop, wcol->shadedown); @@ -2710,14 +2978,80 @@ static void widget_numbut_draw(uiWidgetColors *wcol, rcti *rect, int state, int } /* decoration */ - if (!(state & UI_STATE_TEXT_INPUT)) { - shape_preset_init_number_arrows(&wtb.tria1, rect, 0.6f, 'l'); - shape_preset_init_number_arrows(&wtb.tria2, rect, 0.6f, 'r'); - } + if ((state & UI_ACTIVE) && !(state & UI_STATE_TEXT_INPUT)) { + uiWidgetColors wcol_zone; + uiWidgetBase wtb_zone; + rcti rect_zone; + int roundboxalign_zone; + + /* left arrow zone */ + widget_init(&wtb_zone); + wtb_zone.draw_outline = false; + wtb_zone.draw_emboss = false; + + wcol_zone = *wcol; + copy_v3_v3_char(wcol_zone.item, wcol->text); + if (state & UI_STATE_ACTIVE_LEFT) { + widget_active_color(wcol_zone.inner); + } - widgetbase_draw(&wtb, wcol); + rect_zone = *rect; + rect_zone.xmax = rect->xmin + handle_width + U.pixelsize; + roundboxalign_zone = roundboxalign & ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT); + round_box_edges(&wtb_zone, roundboxalign_zone, &rect_zone, rad); + + shape_preset_init_number_arrows(&wtb_zone.tria1, &rect_zone, 0.6f, 'l'); + widgetbase_draw(&wtb_zone, &wcol_zone); + + /* right arrow zone */ + widget_init(&wtb_zone); + wtb_zone.draw_outline = false; + wtb_zone.draw_emboss = false; + wtb_zone.tria1.type = ROUNDBOX_TRIA_ARROWS; + + wcol_zone = *wcol; + copy_v3_v3_char(wcol_zone.item, wcol->text); + if (state & UI_STATE_ACTIVE_RIGHT) { + widget_active_color(wcol_zone.inner); + } + + rect_zone = *rect; + rect_zone.xmin = rect->xmax - handle_width - U.pixelsize; + roundboxalign_zone = roundboxalign & ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT); + round_box_edges(&wtb_zone, roundboxalign_zone, &rect_zone, rad); + + shape_preset_init_number_arrows(&wtb_zone.tria2, &rect_zone, 0.6f, 'r'); + widgetbase_draw(&wtb_zone, &wcol_zone); + + /* middle highlight zone */ + widget_init(&wtb_zone); + wtb_zone.draw_outline = false; + wtb_zone.draw_emboss = false; + + wcol_zone = *wcol; + copy_v3_v3_char(wcol_zone.item, wcol->text); + if (!(state & (UI_STATE_ACTIVE_LEFT | UI_STATE_ACTIVE_RIGHT))) { + widget_active_color(wcol_zone.inner); + } + + rect_zone = *rect; + rect_zone.xmin = rect->xmin + handle_width - U.pixelsize; + rect_zone.xmax = rect->xmax - handle_width + U.pixelsize; + round_box_edges(&wtb_zone, 0, &rect_zone, 0); + widgetbase_draw(&wtb_zone, &wcol_zone); + + /* outline */ + wtb.draw_inner = false; + widgetbase_draw(&wtb, wcol); + } + else { + /* inner and outline */ + widgetbase_draw(&wtb, wcol); + } if (!(state & UI_STATE_TEXT_INPUT)) { + const float textofs = 0.425f * BLI_rcti_size_y(rect); + /* text space */ rect->xmin += textofs; rect->xmax -= textofs; @@ -2737,53 +3071,6 @@ static void widget_numbut_embossn(uiBut *UNUSED(but), uiWidgetColors *wcol, rcti widget_numbut_draw(wcol, rect, state, roundboxalign, true); } -bool ui_link_bezier_points(const rcti *rect, float coord_array[][2], int resol) -{ - float dist, vec[4][2]; - - vec[0][0] = rect->xmin; - vec[0][1] = rect->ymin; - vec[3][0] = rect->xmax; - vec[3][1] = rect->ymax; - - dist = 0.5f * fabsf(vec[0][0] - vec[3][0]); - - vec[1][0] = vec[0][0] + dist; - vec[1][1] = vec[0][1]; - - vec[2][0] = vec[3][0] - dist; - vec[2][1] = vec[3][1]; - - BKE_curve_forward_diff_bezier(vec[0][0], vec[1][0], vec[2][0], vec[3][0], &coord_array[0][0], resol, sizeof(float[2])); - BKE_curve_forward_diff_bezier(vec[0][1], vec[1][1], vec[2][1], vec[3][1], &coord_array[0][1], resol, sizeof(float[2])); - - /* TODO: why return anything if always true? */ - return true; -} - -#define LINK_RESOL 24 -void ui_draw_link_bezier(const rcti *rect) -{ - float coord_array[LINK_RESOL + 1][2]; - - if (ui_link_bezier_points(rect, coord_array, LINK_RESOL)) { -#if 0 /* unused */ - /* we can reuse the dist variable here to increment the GL curve eval amount*/ - const float dist = 1.0f / (float)LINK_RESOL; -#endif - glEnable(GL_BLEND); - glEnable(GL_LINE_SMOOTH); - - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, coord_array); - glDrawArrays(GL_LINE_STRIP, 0, LINK_RESOL + 1); - glDisableClientState(GL_VERTEX_ARRAY); - - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); - } -} - /* function in use for buttons and for view2d sliders */ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state) { @@ -2798,11 +3085,11 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s horizontal = (BLI_rcti_size_x(rect) > BLI_rcti_size_y(rect)); if (horizontal) - rad = 0.5f * BLI_rcti_size_y(rect); + rad = wcol->roundness * BLI_rcti_size_y(rect); else - rad = 0.5f * BLI_rcti_size_x(rect); + rad = wcol->roundness * BLI_rcti_size_x(rect); - wtb.draw_shadedir = (horizontal) ? true : false; + wtb.uniform_params.shade_dir = (horizontal) ? 1.0f : 0.0; /* draw back part, colors swapped and shading inverted */ if (horizontal) @@ -2934,7 +3221,7 @@ static void widget_progressbar(uiBut *but, uiWidgetColors *wcol, rcti *rect, int /* round corners */ float value = but->a1; - float offs = 0.25f * BLI_rcti_size_y(&rect_prog); + float offs = wcol->roundness * BLI_rcti_size_y(&rect_prog); float w = value * BLI_rcti_size_x(&rect_prog); /* ensure minimium size */ @@ -2957,96 +3244,85 @@ static void widget_progressbar(uiBut *but, uiWidgetColors *wcol, rcti *rect, int rect->xmax += (BLI_rcti_size_x(&rect_prog) / 2); } -static void widget_link(uiBut *but, uiWidgetColors *UNUSED(wcol), rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) -{ - - if (but->flag & UI_SELECT) { - rcti rectlink; - - UI_ThemeColor(TH_TEXT_HI); - - rectlink.xmin = BLI_rcti_cent_x(rect); - rectlink.ymin = BLI_rcti_cent_y(rect); - rectlink.xmax = but->linkto[0]; - rectlink.ymax = but->linkto[1]; - - ui_draw_link_bezier(&rectlink); - } -} - static void widget_numslider(uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign) { uiWidgetBase wtb, wtb1; rcti rect1; - double value; - float offs, toffs, fac = 0; + float offs, toffs; char outline[3]; widget_init(&wtb); widget_init(&wtb1); - /* backdrop first */ - - /* fully rounded */ - offs = 0.5f * BLI_rcti_size_y(rect); + /* Backdrop first. */ + offs = wcol->roundness * BLI_rcti_size_y(rect); toffs = offs * 0.75f; round_box_edges(&wtb, roundboxalign, rect, offs); wtb.draw_outline = false; widgetbase_draw(&wtb, wcol); - /* draw left/right parts only when not in text editing */ + /* Draw slider part only when not in text editing. */ if (!(state & UI_STATE_TEXT_INPUT)) { - int roundboxalign_slider; + int roundboxalign_slider = roundboxalign; - /* slider part */ copy_v3_v3_char(outline, wcol->outline); copy_v3_v3_char(wcol->outline, wcol->item); copy_v3_v3_char(wcol->inner, wcol->item); - if (!(state & UI_SELECT)) + if (!(state & UI_SELECT)) { SWAP(short, wcol->shadetop, wcol->shadedown); + } rect1 = *rect; + float factor, factor_ui; + float factor_discard = 1.0f; /* No discard. */ + float value = (float)ui_but_value_get(but); - value = ui_but_value_get(but); - if ((but->softmax - but->softmin) > 0) { - fac = ((float)value - but->softmin) * (BLI_rcti_size_x(&rect1) - offs) / (but->softmax - but->softmin); + if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) { + factor = value / but->softmax; + } + else { + factor = (value - but->softmin) / (but->softmax - but->softmin); } - /* left part of slider, always rounded */ - rect1.xmax = rect1.xmin + ceil(offs + U.pixelsize); - round_box_edges(&wtb1, roundboxalign & ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT), &rect1, offs); - wtb1.draw_outline = false; - widgetbase_draw(&wtb1, wcol); - - /* right part of slider, interpolate roundness */ - rect1.xmax = rect1.xmin + fac + offs; - rect1.xmin += floor(offs - U.pixelsize); + float width = (float)BLI_rcti_size_x(rect); + factor_ui = factor * width; - if (rect1.xmax + offs > rect->xmax) { - roundboxalign_slider = roundboxalign & ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT); - offs *= (rect1.xmax + offs - rect->xmax) / offs; + if (factor_ui <= offs) { + /* Left part only. */ + roundboxalign_slider &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT); + rect1.xmax = rect1.xmin + offs; + factor_discard = factor_ui / offs; + } + else if (factor_ui <= width - offs) { + /* Left part + middle part. */ + roundboxalign_slider &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT); + rect1.xmax = rect1.xmin + factor_ui; } else { - roundboxalign_slider = 0; - offs = 0.0f; + /* Left part + middle part + right part. */ + factor_discard = factor; } - round_box_edges(&wtb1, roundboxalign_slider, &rect1, offs); + round_box_edges(&wtb1, roundboxalign_slider, &rect1, offs); + wtb1.draw_outline = false; + widgetbase_set_uniform_discard_factor(&wtb1, factor_discard); widgetbase_draw(&wtb1, wcol); + copy_v3_v3_char(wcol->outline, outline); - if (!(state & UI_SELECT)) + if (!(state & UI_SELECT)) { SWAP(short, wcol->shadetop, wcol->shadedown); + } } - /* outline */ + /* Outline. */ wtb.draw_outline = true; wtb.draw_inner = false; widgetbase_draw(&wtb, wcol); - /* add space at either side of the button so text aligns with numbuttons (which have arrow icons) */ + /* Add space at either side of the button so text aligns with numbuttons (which have arrow icons). */ if (!(state & UI_STATE_TEXT_INPUT)) { rect->xmax -= toffs; rect->xmin += toffs; @@ -3077,13 +3353,12 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat widget_init(&wtb); - /* half rounded */ - rad = 0.25f * U.widget_unit; + rad = wcol->roundness * U.widget_unit; round_box_edges(&wtb, roundboxalign, rect, rad); ui_but_v3_get(but, col); - if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT)) { + if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDEN | UI_BUT_REDALERT)) { /* draw based on state - color for keyed etc */ widgetbase_draw(&wtb, wcol); @@ -3114,7 +3389,6 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat } widgetbase_draw(&wtb, wcol); - if (but->a1 == UI_PALETTE_COLOR && ((Palette *)but->rnapoin.id.data)->active_color == (int)but->a2) { float width = rect->xmax - rect->xmin; float height = rect->ymax - rect->ymin; @@ -3123,12 +3397,22 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat bw += (bw < 0.5f) ? 0.5f : -0.5f; - glColor4f(bw, bw, bw, 1.0); - glBegin(GL_TRIANGLES); - glVertex2f(rect->xmin + 0.1f * width, rect->ymin + 0.9f * height); - glVertex2f(rect->xmin + 0.1f * width, rect->ymin + 0.5f * height); - glVertex2f(rect->xmin + 0.5f * width, rect->ymin + 0.9f * height); - glEnd(); + /* We are drawing on top of widget bases. Flush cache. */ + GPU_blend(true); + UI_widgetbase_draw_cache_flush(); + GPU_blend(false); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3f(bw, bw, bw); + immBegin(GPU_PRIM_TRIS, 3); + immVertex2f(pos, rect->xmin + 0.1f * width, rect->ymin + 0.9f * height); + immVertex2f(pos, rect->xmin + 0.1f * width, rect->ymin + 0.5f * height); + immVertex2f(pos, rect->xmin + 0.5f * width, rect->ymin + 0.9f * height); + immEnd(); + + immUnbindProgram(); } } @@ -3146,8 +3430,7 @@ static void widget_icon_has_anim(uiBut *but, uiWidgetColors *wcol, rcti *rect, i widget_init(&wtb); wtb.draw_outline = false; - /* rounded */ - rad = 0.5f * BLI_rcti_size_y(rect); + rad = wcol->roundness * BLI_rcti_size_y(rect); round_box_edges(&wtb, UI_CNR_ALL, rect, rad); widgetbase_draw(&wtb, wcol); } @@ -3169,8 +3452,7 @@ static void widget_textbut(uiWidgetColors *wcol, rcti *rect, int state, int roun widget_init(&wtb); - /* half rounded */ - rad = 0.2f * U.widget_unit; + rad = wcol->roundness * U.widget_unit; round_box_edges(&wtb, roundboxalign, rect, rad); widgetbase_draw(&wtb, wcol); @@ -3184,12 +3466,13 @@ static void widget_menubut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), widget_init(&wtb); - /* half rounded */ - rad = 0.2f * U.widget_unit; + rad = wcol->roundness * U.widget_unit; round_box_edges(&wtb, roundboxalign, rect, rad); /* decoration */ shape_preset_trias_from_rect_menu(&wtb.tria1, rect); + /* copy size and center to 2nd tria */ + wtb.tria2 = wtb.tria1; widgetbase_draw(&wtb, wcol); @@ -3204,44 +3487,31 @@ static void widget_menuiconbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(stat widget_init(&wtb); - /* half rounded */ - rad = 0.2f * U.widget_unit; - round_box_edges(&wtb, roundboxalign, rect, rad); - - /* decoration */ - widgetbase_draw(&wtb, wcol); -} - -static void widget_menunodebut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign) -{ - /* silly node link button hacks */ - uiWidgetBase wtb; - uiWidgetColors wcol_backup = *wcol; - float rad; - - widget_init(&wtb); - - /* half rounded */ - rad = 0.2f * U.widget_unit; + rad = wcol->roundness * U.widget_unit; round_box_edges(&wtb, roundboxalign, rect, rad); - wcol->inner[0] = min_ii(wcol->inner[0] + 15, 255); - wcol->inner[1] = min_ii(wcol->inner[1] + 15, 255); - wcol->inner[2] = min_ii(wcol->inner[2] + 15, 255); - wcol->outline[0] = min_ii(wcol->outline[0] + 15, 255); - wcol->outline[1] = min_ii(wcol->outline[1] + 15, 255); - wcol->outline[2] = min_ii(wcol->outline[2] + 15, 255); - /* decoration */ widgetbase_draw(&wtb, wcol); - *wcol = wcol_backup; } static void widget_pulldownbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign) { - if (state & UI_ACTIVE) { + float back[4]; + UI_GetThemeColor4fv(TH_BACK, back); + + if ((state & UI_ACTIVE) || (back[3] < 1.0f)) { uiWidgetBase wtb; - const float rad = 0.2f * U.widget_unit; + const float rad = wcol->roundness * U.widget_unit; + + if (state & UI_ACTIVE) { + copy_v4_v4_char(wcol->inner, wcol->inner_sel); + copy_v3_v3_char(wcol->text, wcol->text_sel); + copy_v3_v3_char(wcol->outline, wcol->inner); + } + else { + wcol->inner[3] *= 1.0f - back[3]; + wcol->outline[3] = 0.0f; + } widget_init(&wtb); @@ -3275,7 +3545,7 @@ static void widget_menu_radial_itembut(uiBut *but, uiWidgetColors *wcol, rcti *r wtb.draw_emboss = false; - rad = 0.5f * BLI_rcti_size_y(rect); + rad = wcol->roundness * BLI_rcti_size_y(rect); round_box_edges(&wtb, UI_CNR_ALL, rect, rad); wcol->inner[3] *= fac; @@ -3295,9 +3565,9 @@ static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, int UNUSED(sta widget_init(&wtb); - /* rounded, but no outline */ + /* no outline */ wtb.draw_outline = false; - rad = 0.2f * U.widget_unit; + rad = wcol->roundness * U.widget_unit; round_box_edges(&wtb, UI_CNR_ALL, rect, rad); widgetbase_draw(&wtb, wcol); @@ -3322,8 +3592,7 @@ static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, int state, int UN recttemp.xmax -= delta; recttemp.ymax -= delta; - /* half rounded */ - rad = BLI_rcti_size_y(&recttemp) / 3; + rad = wcol->roundness * BLI_rcti_size_y(&recttemp); round_box_edges(&wtb, UI_CNR_ALL, &recttemp, rad); /* decoration */ @@ -3364,8 +3633,7 @@ static void widget_radiobut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), widget_init(&wtb); - /* half rounded */ - rad = 0.2f * U.widget_unit; + rad = wcol->roundness * U.widget_unit; round_box_edges(&wtb, roundboxalign, rect, rad); widgetbase_draw(&wtb, wcol); @@ -3388,8 +3656,7 @@ static void widget_box(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED( wcol->inner[2] = but->col[2]; } - /* half rounded */ - rad = 0.2f * U.widget_unit; + rad = wcol->roundness * U.widget_unit; round_box_edges(&wtb, roundboxalign, rect, rad); widgetbase_draw(&wtb, wcol); @@ -3404,8 +3671,7 @@ static void widget_but(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int widget_init(&wtb); - /* half rounded */ - rad = 0.2f * U.widget_unit; + rad = wcol->roundness * U.widget_unit; round_box_edges(&wtb, roundboxalign, rect, rad); widgetbase_draw(&wtb, wcol); @@ -3415,7 +3681,7 @@ static void widget_but(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign) { uiWidgetBase wtb; - const float rad = 0.25f * U.widget_unit; + const float rad = wcol->roundness * U.widget_unit; widget_init(&wtb); @@ -3429,7 +3695,7 @@ static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), static void widget_roundbut_exec(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign) { uiWidgetBase wtb; - const float rad = 0.25f * U.widget_unit; + const float rad = wcol->roundness * U.widget_unit; widget_init(&wtb); @@ -3444,10 +3710,68 @@ static void widget_roundbut_exec(uiWidgetColors *wcol, rcti *rect, int state, in widgetbase_draw(&wtb, wcol); } +static void widget_tab(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign) +{ + const uiStyle *style = UI_style_get(); + const float rad = wcol->roundness * U.widget_unit; + const int fontid = style->widget.uifont_id; + const bool is_active = (state & UI_SELECT); + +/* Draw shaded outline - Disabled for now, seems incorrect and also looks nicer without it imho ;) */ +//#define USE_TAB_SHADED_HIGHLIGHT + + uiWidgetBase wtb; + unsigned char theme_col_tab_highlight[3]; + +#ifdef USE_TAB_SHADED_HIGHLIGHT + /* create outline highlight colors */ + if (is_active) { + interp_v3_v3v3_uchar(theme_col_tab_highlight, (unsigned char *)wcol->inner_sel, + (unsigned char *)wcol->outline, 0.2f); + } + else { + interp_v3_v3v3_uchar(theme_col_tab_highlight, (unsigned char *)wcol->inner, + (unsigned char *)wcol->outline, 0.12f); + } +#endif + + widget_init(&wtb); + + /* half rounded */ + round_box_edges(&wtb, roundboxalign, rect, rad); + + /* draw inner */ +#ifdef USE_TAB_SHADED_HIGHLIGHT + wtb.draw_outline = 0; +#endif + widgetbase_draw(&wtb, wcol); + + /* We are drawing on top of widget bases. Flush cache. */ + GPU_blend(true); + UI_widgetbase_draw_cache_flush(); + GPU_blend(false); + +#ifdef USE_TAB_SHADED_HIGHLIGHT + /* draw outline (3d look) */ + ui_draw_but_TAB_outline(rect, rad, theme_col_tab_highlight, (unsigned char *)wcol->inner); +#endif + + /* text shadow */ + BLF_enable(fontid, BLF_SHADOW); + BLF_shadow(fontid, 3, (const float[4]){1.0f, 1.0f, 1.0f, 0.25f}); + BLF_shadow_offset(fontid, 0, -1); + +#ifndef USE_TAB_SHADED_HIGHLIGHT + UNUSED_VARS(is_active, theme_col_tab_highlight); +#endif +} + static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType *wt, rcti *rect) { + bTheme *btheme = UI_GetTheme(); + uiWidgetColors *wcol = &btheme->tui.wcol_radio; uiWidgetBase wtb; - const float rad = 0.25f * U.widget_unit; + const float rad = wcol->roundness * U.widget_unit; unsigned char col[4]; /* state copy! */ @@ -3459,12 +3783,17 @@ static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType * /* note: drawextra can change rect +1 or -1, to match round errors of existing previews */ but->block->drawextra(C, but->poin, but->block->drawextra_arg1, but->block->drawextra_arg2, rect); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + /* make mask to draw over image */ UI_GetThemeColor3ubv(TH_BACK, col); - glColor3ubv(col); + immUniformColor3ubv(col); round_box__edges(&wtb, UI_CNR_ALL, rect, 0.0f, rad); - widgetbase_outline(&wtb); + widgetbase_outline(&wtb, pos); + + immUnbindProgram(); } /* outline */ @@ -3526,6 +3855,16 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.draw = widget_roundbut_exec; break; + case UI_WTYPE_TOOLBAR_ITEM: + wt.wcol_theme = &btheme->tui.wcol_toolbar_item; + wt.draw = widget_roundbut_exec; + break; + + case UI_WTYPE_TAB: + wt.wcol_theme = &btheme->tui.wcol_tab; + wt.draw = widget_tab; + break; + case UI_WTYPE_TOOLTIP: wt.wcol_theme = &btheme->tui.wcol_tooltip; wt.draw = widget_menu_back; @@ -3555,6 +3894,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) break; case UI_WTYPE_MENU_ICON_RADIO: + case UI_WTYPE_MENU_NODE_LINK: wt.wcol_theme = &btheme->tui.wcol_menu; wt.draw = widget_menuiconbut; break; @@ -3564,11 +3904,6 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.draw = widget_menubut; break; - case UI_WTYPE_MENU_NODE_LINK: - wt.wcol_theme = &btheme->tui.wcol_menu; - wt.draw = widget_menunodebut; - break; - case UI_WTYPE_PULLDOWN: wt.wcol_theme = &btheme->tui.wcol_pulldown; wt.draw = widget_pulldownbut; @@ -3648,7 +3983,7 @@ static int widget_roundbox_set(uiBut *but, rcti *rect) /* alignment */ if ((but->drawflag & UI_BUT_ALIGN) && but->type != UI_BTYPE_PULLDOWN) { - /* ui_block_position has this correction too, keep in sync */ + /* ui_popup_block_position has this correction too, keep in sync */ if (but->drawflag & (UI_BUT_ALIGN_TOP | UI_BUT_ALIGN_STITCH_TOP)) rect->ymax += U.pixelsize; if (but->drawflag & (UI_BUT_ALIGN_LEFT | UI_BUT_ALIGN_STITCH_LEFT)) @@ -3686,7 +4021,7 @@ static int widget_roundbox_set(uiBut *but, rcti *rect) } /* align with open menu */ - if (but->active) { + if (but->active && (but->type != UI_BTYPE_POPOVER)) { int direction = ui_but_menu_direction(but); if (direction == UI_DIR_UP) roundbox &= ~(UI_CNR_TOP_RIGHT | UI_CNR_TOP_LEFT); @@ -3710,6 +4045,10 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct uiFontStyle *fstyle = &style->widget; uiWidgetType *wt = NULL; +#ifdef USE_UI_POPOVER_ONCE + const rcti rect_orig = *rect; +#endif + /* handle menus separately */ if (but->dt == UI_EMBOSS_PULLDOWN) { switch (but->type) { @@ -3757,10 +4096,20 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct case UI_BTYPE_SEPR: case UI_BTYPE_SEPR_LINE: + case UI_BTYPE_SEPR_SPACER: break; case UI_BTYPE_BUT: +#ifdef USE_UI_TOOLBAR_HACK + if (UI_but_is_tool(but)) { + wt = widget_type(UI_WTYPE_TOOLBAR_ITEM); + } + else { + wt = widget_type(UI_WTYPE_EXEC); + } +#else wt = widget_type(UI_WTYPE_EXEC); +#endif break; case UI_BTYPE_NUM: @@ -3789,6 +4138,10 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct wt->wcol_theme = &btheme->tui.wcol_menu_back; break; + case UI_BTYPE_TAB: + wt = widget_type(UI_WTYPE_TAB); + break; + case UI_BTYPE_BUT_TOGGLE: case UI_BTYPE_TOGGLE: case UI_BTYPE_TOGGLE_N: @@ -3812,6 +4165,7 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct case UI_BTYPE_MENU: case UI_BTYPE_BLOCK: + case UI_BTYPE_POPOVER: if (but->flag & UI_BUT_NODE_LINK) { /* new node-link button, not active yet XXX */ wt = widget_type(UI_WTYPE_MENU_NODE_LINK); @@ -3851,13 +4205,6 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct wt = widget_type(UI_WTYPE_BOX); break; - case UI_BTYPE_LINK: - case UI_BTYPE_INLINK: - wt = widget_type(UI_WTYPE_ICON); - wt->custom = widget_link; - - break; - case UI_BTYPE_EXTRA: widget_draw_extra_mask(C, but, widget_type(UI_WTYPE_BOX), rect); break; @@ -3954,6 +4301,15 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct state |= UI_STATE_HOLD_ACTION; } + if (state & UI_ACTIVE) { + if (but->drawflag & UI_BUT_ACTIVE_LEFT) { + state |= UI_STATE_ACTIVE_LEFT; + } + else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) { + state |= UI_STATE_ACTIVE_RIGHT; + } + } + if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) if (but->dt != UI_EMBOSS_PULLDOWN) disabled = true; @@ -3968,10 +4324,29 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct wt->draw(&wt->wcol, rect, state, roundboxalign); if (disabled) - glEnable(GL_BLEND); + GPU_blend(true); + +#ifdef USE_UI_POPOVER_ONCE + if (but->block->flag & UI_BLOCK_POPOVER_ONCE) { + 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); + copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel); + wt->wcol.inner[3] = 128; + wt->wcol.roundness = 0.5f; + ui_draw_roundbox( + &rect_orig, + 0.25f * min_ff(BLI_rcti_size_x(&rect_orig), BLI_rcti_size_y(&rect_orig)), + &wt_temp->wcol); + *wt = wt_back; + } + } +#endif + wt->text(fstyle, &wt->wcol, but, rect); if (disabled) - glDisable(GL_BLEND); + GPU_blend(false); // if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) // if (but->dt != UI_EMBOSS_PULLDOWN) @@ -3979,6 +4354,28 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct } } +static void ui_draw_clip_tri(uiBlock *block, rcti *rect, uiWidgetType *wt) +{ + if (block) { + float draw_color[4]; + unsigned char *color = (unsigned char *)wt->wcol.text; + + draw_color[0] = ((float)color[0]) / 255.0f; + draw_color[1] = ((float)color[1]) / 255.0f; + draw_color[2] = ((float)color[2]) / 255.0f; + draw_color[3] = 1.0f; + + if (block->flag & UI_BLOCK_CLIPTOP) { + /* XXX no scaling for UI here yet */ + UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymax - 8, 't', draw_color); + } + if (block->flag & UI_BLOCK_CLIPBOTTOM) { + /* XXX no scaling for UI here yet */ + UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymin + 10, 'v', draw_color); + } + } +} + void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK); @@ -3989,18 +4386,79 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) else wt->draw(&wt->wcol, rect, 0, 0); - if (block) { - if (block->flag & UI_BLOCK_CLIPTOP) { - /* XXX no scaling for UI here yet */ - glColor3ubv((unsigned char *)wt->wcol.text); - UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymax - 8, 't'); + ui_draw_clip_tri(block, rect, wt); +} + +/** + * Similar to 'widget_menu_back', however we can't use the widget preset system + * because we need to pass in the original location so we know where to show the arrow. + */ +static void ui_draw_popover_back_impl( + const uiWidgetColors *wcol, rcti *rect, int direction, const float unit_size, + const float mval_origin[2]) +{ + /* tsk, this isn't nice. */ + const float unit_half = unit_size / 2; + const float cent_x = mval_origin ? mval_origin[0] : BLI_rcti_cent_x(rect); + rect->ymax -= unit_half; + rect->ymin += unit_half; + + GPU_blend(true); + + /* Extracted from 'widget_menu_back', keep separate to avoid menu changes breaking popovers */ + { + uiWidgetBase wtb; + widget_init(&wtb); + + const int roundboxalign = UI_CNR_ALL; + widget_softshadow(rect, roundboxalign, wcol->roundness * U.widget_unit); + + round_box_edges(&wtb, roundboxalign, rect, wcol->roundness * U.widget_unit); + wtb.draw_emboss = false; + widgetbase_draw(&wtb, wcol); + } + + /* Draw popover arrow (top/bottom) */ + if (ELEM(direction, UI_DIR_UP, UI_DIR_DOWN)) { + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4ubv((unsigned char *)wcol->inner); + GPU_blend(true); + immBegin(GPU_PRIM_TRIS, 3); + if (direction == UI_DIR_DOWN) { + const float y = rect->ymax; + immVertex2f(pos, cent_x - unit_half, y); + immVertex2f(pos, cent_x + unit_half, y); + immVertex2f(pos, cent_x, y + unit_half); } - if (block->flag & UI_BLOCK_CLIPBOTTOM) { - /* XXX no scaling for UI here yet */ - glColor3ubv((unsigned char *)wt->wcol.text); - UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymin + 10, 'v'); + else { + const float y = rect->ymin; + immVertex2f(pos, cent_x - unit_half, y); + immVertex2f(pos, cent_x + unit_half, y); + immVertex2f(pos, cent_x, y - unit_half); } + immEnd(); + immUnbindProgram(); + } + + GPU_blend(false); +} + +void ui_draw_popover_back(ARegion *ar, uiStyle *UNUSED(style), uiBlock *block, rcti *rect) +{ + uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK); + + if (block) { + float mval_origin[2] = {block->mx, block->my}; + ui_window_to_block_fl(ar, block, &mval_origin[0], &mval_origin[1]); + ui_draw_popover_back_impl(wt->wcol_theme, rect, block->direction, U.widget_unit / block->aspect, mval_origin); } + else { + wt->state(wt, 0); + wt->draw(&wt->wcol, rect, 0, 0); + } + + ui_draw_clip_tri(block, rect, wt); } static void draw_disk_shaded( @@ -4016,33 +4474,21 @@ static void draw_disk_shaded( float y1, y2; float fac; unsigned char r_col[4]; + unsigned int pos, col; - glBegin(GL_TRIANGLE_STRIP); - - s = sinf(start); - c = cosf(start); - - y1 = s * radius_int; - y2 = s * radius_ext; - + GPUVertFormat *format = immVertexFormat(); + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); if (shaded) { - fac = (y1 + radius_ext) * radius_ext_scale; - round_box_shade_col4_r(r_col, col1, col2, fac); - - glColor4ubv(r_col); + col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); } - - glVertex2f(c * radius_int, s * radius_int); - - if (shaded) { - fac = (y2 + radius_ext) * radius_ext_scale; - round_box_shade_col4_r(r_col, col1, col2, fac); - - glColor4ubv(r_col); + else { + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4ubv((unsigned char *)col1); } - glVertex2f(c * radius_ext, s * radius_ext); - for (i = 1; i < subd; i++) { + immBegin(GPU_PRIM_TRI_STRIP, subd * 2); + for (i = 0; i < subd; i++) { float a; a = start + ((i) / (float)(subd - 1)) * angle; @@ -4054,20 +4500,20 @@ static void draw_disk_shaded( if (shaded) { fac = (y1 + radius_ext) * radius_ext_scale; round_box_shade_col4_r(r_col, col1, col2, fac); - - glColor4ubv(r_col); + immAttrib4ubv(col, r_col); } - glVertex2f(c * radius_int, s * radius_int); + immVertex2f(pos, c * radius_int, s * radius_int); if (shaded) { fac = (y2 + radius_ext) * radius_ext_scale; round_box_shade_col4_r(r_col, col1, col2, fac); - - glColor4ubv(r_col); + immAttrib4ubv(col, r_col); } - glVertex2f(c * radius_ext, s * radius_ext); + immVertex2f(pos, c * radius_ext, s * radius_ext); } - glEnd(); + immEnd(); + + immUnbindProgram(); } void ui_draw_pie_center(uiBlock *block) @@ -4086,18 +4532,17 @@ void ui_draw_pie_center(uiBlock *block) float angle = atan2f(pie_dir[1], pie_dir[0]); float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_2 : M_PI_4; - glPushMatrix(); - glTranslatef(cx, cy, 0.0f); + GPU_matrix_push(); + GPU_matrix_translate_2f(cx, cy); - glEnable(GL_BLEND); + GPU_blend(true); if (btheme->tui.wcol_pie_menu.shaded) { char col1[4], col2[4]; shadecolors4(col1, col2, btheme->tui.wcol_pie_menu.inner, btheme->tui.wcol_pie_menu.shadetop, btheme->tui.wcol_pie_menu.shadedown); draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, col1, col2, true); } else { - glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.inner); - draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, NULL, NULL, false); + draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, btheme->tui.wcol_pie_menu.inner, NULL, false); } if (!(block->pie_data.flags & UI_PIE_INVALID_DIR)) { @@ -4107,25 +4552,34 @@ void ui_draw_pie_center(uiBlock *block) draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, col1, col2, true); } else { - glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.inner_sel); - draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, NULL, NULL, false); + draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, btheme->tui.wcol_pie_menu.inner_sel, NULL, false); } } - glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.outline); - glutil_draw_lined_arc(0.0f, (float)M_PI * 2.0f, pie_radius_internal, subd); - glutil_draw_lined_arc(0.0f, (float)M_PI * 2.0f, pie_radius_external, subd); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4ubv((unsigned char *)btheme->tui.wcol_pie_menu.outline); + + imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, pie_radius_internal, subd); + imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, pie_radius_external, subd); + + immUnbindProgram(); if (U.pie_menu_confirm > 0 && !(block->pie_data.flags & (UI_PIE_INVALID_DIR | UI_PIE_CLICK_STYLE))) { float pie_confirm_radius = U.pixelsize * (pie_radius_internal + U.pie_menu_confirm); float pie_confirm_external = U.pixelsize * (pie_radius_internal + U.pie_menu_confirm + 7.0f); - glColor4ub(btheme->tui.wcol_pie_menu.text_sel[0], btheme->tui.wcol_pie_menu.text_sel[1], btheme->tui.wcol_pie_menu.text_sel[2], 64); - draw_disk_shaded(angle - range / 2.0f, range, pie_confirm_radius, pie_confirm_external, subd, NULL, NULL, false); + const char col[4] = {btheme->tui.wcol_pie_menu.text_sel[0], + btheme->tui.wcol_pie_menu.text_sel[1], + btheme->tui.wcol_pie_menu.text_sel[2], + 64}; + + draw_disk_shaded(angle - range / 2.0f, range, pie_confirm_radius, pie_confirm_external, subd, col, NULL, false); } - glDisable(GL_BLEND); - glPopMatrix(); + GPU_blend(false); + GPU_matrix_pop(); } @@ -4135,30 +4589,42 @@ uiWidgetColors *ui_tooltip_get_theme(void) return wt->wcol_theme; } -void ui_draw_tooltip_background(uiStyle *UNUSED(style), uiBlock *UNUSED(block), rcti *rect) +/** + * Generic drawing for background. + */ +void ui_draw_widget_back_color( + uiWidgetTypeEnum type, bool use_shadow, const rcti *rect, + const float color[4]) { - uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP); + uiWidgetType *wt = widget_type(type); + + if (use_shadow) { + GPU_blend(true); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + widget_softshadow(rect, UI_CNR_ALL, 0.25f * U.widget_unit); + GPU_blend(false); + } + + rcti rect_copy = *rect; wt->state(wt, 0); - /* wt->draw ends up using same function to draw the tooltip as menu_back */ - wt->draw(&wt->wcol, rect, 0, 0); + if (color) { + rgba_float_to_uchar((unsigned char *)wt->wcol.inner, color); + } + wt->draw(&wt->wcol, &rect_copy, 0, UI_CNR_ALL); } - -void ui_draw_search_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) +void ui_draw_widget_back(uiWidgetTypeEnum type, bool use_shadow, const rcti *rect) { - uiWidgetType *wt = widget_type(UI_WTYPE_BOX); - - glEnable(GL_BLEND); - widget_softshadow(rect, UI_CNR_ALL, 0.25f * U.widget_unit); - glDisable(GL_BLEND); + ui_draw_widget_back_color(type, use_shadow, rect, NULL); +} +void ui_draw_tooltip_background(uiStyle *UNUSED(style), uiBlock *UNUSED(block), rcti *rect) +{ + uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP); wt->state(wt, 0); - if (block) - wt->draw(&wt->wcol, rect, block->flag, UI_CNR_ALL); - else - wt->draw(&wt->wcol, rect, 0, UI_CNR_ALL); + /* wt->draw ends up using same function to draw the tooltip as menu_back */ + wt->draw(&wt->wcol, rect, 0, 0); } - /* helper call to draw a menu item without button */ /* state: UI_ACTIVE or 0 */ void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state, bool use_sep) @@ -4209,8 +4675,7 @@ void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int ic UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0'); } - glColor4ubv((unsigned char *)wt->wcol.text); - UI_fontstyle_draw(fstyle, rect, drawstr); + UI_fontstyle_draw(fstyle, rect, drawstr, (unsigned char *)wt->wcol.text); } /* part text right aligned */ @@ -4218,7 +4683,7 @@ void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int ic if (cpoin) { fstyle->align = UI_STYLE_TEXT_RIGHT; rect->xmax = _rect.xmax - 5; - UI_fontstyle_draw(fstyle, rect, cpoin + 1); + UI_fontstyle_draw(fstyle, rect, cpoin + 1, (unsigned char *)wt->wcol.text); *cpoin = UI_SEP_CHAR; } } @@ -4234,9 +4699,9 @@ void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int ic height = ICON_SIZE_FROM_BUTRECT(rect); aspect = ICON_DEFAULT_HEIGHT / height; - glEnable(GL_BLEND); + GPU_blend(true); UI_icon_draw_aspect(xs, ys, iconid, aspect, 1.0f); /* XXX scale weak get from fstyle? */ - glDisable(GL_BLEND); + GPU_blend(false); } } @@ -4253,9 +4718,9 @@ void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int /* draw icon in rect above the space reserved for the label */ rect->ymin += text_size; - glEnable(GL_BLEND); + GPU_blend(true); widget_draw_preview(iconid, 1.0f, rect); - glDisable(GL_BLEND); + GPU_blend(false); BLF_width_and_height(fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]); @@ -4276,8 +4741,7 @@ void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int BLI_strncpy(drawstr, name, sizeof(drawstr)); UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0'); - glColor4ubv((unsigned char *)wt->wcol.text); - UI_fontstyle_draw(fstyle, &trect, drawstr); + UI_fontstyle_draw(fstyle, &trect, drawstr, (unsigned char *)wt->wcol.text); } } diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 77c36bf47f3..47d664eaeb2 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -48,16 +48,24 @@ #include "BKE_addon.h" #include "BKE_appdir.h" #include "BKE_colorband.h" -#include "BKE_DerivedMesh.h" #include "BKE_global.h" #include "BKE_main.h" +#include "BKE_mesh_runtime.h" #include "BIF_gl.h" +#include "BLF_api.h" + +#include "ED_screen.h" + #include "UI_interface.h" #include "UI_interface_icons.h" #include "interface_intern.h" +#include "GPU_framebuffer.h" + + +extern const bTheme U_theme_default; /* global for themes */ typedef void (*VectorDrawFunc)(int x, int y, int w, int h, float alpha); @@ -94,6 +102,7 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo static char error[4] = {240, 0, 240, 255}; static char alert[4] = {240, 60, 60, 255}; static char headerdesel[4] = {0, 0, 0, 255}; + static char back[4] = {0, 0, 0, 255}; static char setting = 0; const char *cp = error; @@ -154,18 +163,18 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo case SPACE_CONSOLE: ts = &btheme->tconsole; break; - case SPACE_TIME: - ts = &btheme->ttime; - break; case SPACE_NODE: ts = &btheme->tnode; break; - case SPACE_LOGIC: - ts = &btheme->tlogic; - break; case SPACE_CLIP: ts = &btheme->tclip; break; + case SPACE_TOPBAR: + ts = &btheme->ttopbar; + break; + case SPACE_STATUSBAR: + ts = &btheme->tstatusbar; + break; default: ts = &btheme->tv3d; break; @@ -181,6 +190,12 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo cp = ts->header; else cp = ts->button; + + copy_v4_v4_char(back, cp); + if (!ED_region_is_overlap(spacetype, theme_regionid)) { + back[3] = 255; + } + cp = back; break; case TH_LOW_GRAD: cp = ts->gradients.gradient; @@ -231,6 +246,7 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo headerdesel[0] = cp[0] > 10 ? cp[0] - 10 : 0; headerdesel[1] = cp[1] > 10 ? cp[1] - 10 : 0; headerdesel[2] = cp[2] > 10 ? cp[2] - 10 : 0; + headerdesel[3] = cp[3]; cp = headerdesel; break; case TH_HEADER_TEXT: @@ -242,14 +258,8 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo cp = ts->panelcolors.header; break; case TH_PANEL_BACK: cp = ts->panelcolors.back; break; - case TH_PANEL_SHOW_HEADER: - cp = &setting; - setting = ts->panelcolors.show_header; - break; - case TH_PANEL_SHOW_BACK: - cp = &setting; - setting = ts->panelcolors.show_back; - break; + case TH_PANEL_SUB_BACK: + cp = ts->panelcolors.sub_back; break; case TH_BUTBACK: cp = ts->button; break; @@ -659,6 +669,9 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo case TH_WIDGET_EMBOSS: cp = btheme->tui.widget_emboss; break; + case TH_EDITOR_OUTLINE: + cp = btheme->tui.editor_outline; + break; case TH_AXIS_X: cp = btheme->tui.xaxis; break; case TH_AXIS_Y: @@ -666,6 +679,17 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo case TH_AXIS_Z: cp = btheme->tui.zaxis; break; + case TH_GIZMO_HI: + cp = btheme->tui.gizmo_hi; break; + case TH_GIZMO_PRIMARY: + cp = btheme->tui.gizmo_primary; break; + case TH_GIZMO_SECONDARY: + cp = btheme->tui.gizmo_secondary; break; + case TH_GIZMO_A: + cp = btheme->tui.gizmo_a; break; + case TH_GIZMO_B: + cp = btheme->tui.gizmo_b; break; + case TH_INFO_SELECTED: cp = ts->info_selected; break; @@ -706,136 +730,6 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo return (const unsigned char *)cp; } -/* use this call to init new bone color sets in Theme */ -static void ui_theme_init_boneColorSets(bTheme *btheme) -{ - int i; - - /* define default color sets - currently we only define 15 of these, though that should be ample */ - /* set 1 */ - rgba_char_args_set(btheme->tarm[0].solid, 0x9a, 0x00, 0x00, 255); - rgba_char_args_set(btheme->tarm[0].select, 0xbd, 0x11, 0x11, 255); - rgba_char_args_set(btheme->tarm[0].active, 0xf7, 0x0a, 0x0a, 255); - /* set 2 */ - rgba_char_args_set(btheme->tarm[1].solid, 0xf7, 0x40, 0x18, 255); - rgba_char_args_set(btheme->tarm[1].select, 0xf6, 0x69, 0x13, 255); - rgba_char_args_set(btheme->tarm[1].active, 0xfa, 0x99, 0x00, 255); - /* set 3 */ - rgba_char_args_set(btheme->tarm[2].solid, 0x1e, 0x91, 0x09, 255); - rgba_char_args_set(btheme->tarm[2].select, 0x59, 0xb7, 0x0b, 255); - rgba_char_args_set(btheme->tarm[2].active, 0x83, 0xef, 0x1d, 255); - /* set 4 */ - rgba_char_args_set(btheme->tarm[3].solid, 0x0a, 0x36, 0x94, 255); - rgba_char_args_set(btheme->tarm[3].select, 0x36, 0x67, 0xdf, 255); - rgba_char_args_set(btheme->tarm[3].active, 0x5e, 0xc1, 0xef, 255); - /* set 5 */ - rgba_char_args_set(btheme->tarm[4].solid, 0xa9, 0x29, 0x4e, 255); - rgba_char_args_set(btheme->tarm[4].select, 0xc1, 0x41, 0x6a, 255); - rgba_char_args_set(btheme->tarm[4].active, 0xf0, 0x5d, 0x91, 255); - /* set 6 */ - rgba_char_args_set(btheme->tarm[5].solid, 0x43, 0x0c, 0x78, 255); - rgba_char_args_set(btheme->tarm[5].select, 0x54, 0x3a, 0xa3, 255); - rgba_char_args_set(btheme->tarm[5].active, 0x87, 0x64, 0xd5, 255); - /* set 7 */ - rgba_char_args_set(btheme->tarm[6].solid, 0x24, 0x78, 0x5a, 255); - rgba_char_args_set(btheme->tarm[6].select, 0x3c, 0x95, 0x79, 255); - rgba_char_args_set(btheme->tarm[6].active, 0x6f, 0xb6, 0xab, 255); - /* set 8 */ - rgba_char_args_set(btheme->tarm[7].solid, 0x4b, 0x70, 0x7c, 255); - rgba_char_args_set(btheme->tarm[7].select, 0x6a, 0x86, 0x91, 255); - rgba_char_args_set(btheme->tarm[7].active, 0x9b, 0xc2, 0xcd, 255); - /* set 9 */ - rgba_char_args_set(btheme->tarm[8].solid, 0xf4, 0xc9, 0x0c, 255); - rgba_char_args_set(btheme->tarm[8].select, 0xee, 0xc2, 0x36, 255); - rgba_char_args_set(btheme->tarm[8].active, 0xf3, 0xff, 0x00, 255); - /* set 10 */ - rgba_char_args_set(btheme->tarm[9].solid, 0x1e, 0x20, 0x24, 255); - rgba_char_args_set(btheme->tarm[9].select, 0x48, 0x4c, 0x56, 255); - rgba_char_args_set(btheme->tarm[9].active, 0xff, 0xff, 0xff, 255); - /* set 11 */ - rgba_char_args_set(btheme->tarm[10].solid, 0x6f, 0x2f, 0x6a, 255); - rgba_char_args_set(btheme->tarm[10].select, 0x98, 0x45, 0xbe, 255); - rgba_char_args_set(btheme->tarm[10].active, 0xd3, 0x30, 0xd6, 255); - /* set 12 */ - rgba_char_args_set(btheme->tarm[11].solid, 0x6c, 0x8e, 0x22, 255); - rgba_char_args_set(btheme->tarm[11].select, 0x7f, 0xb0, 0x22, 255); - rgba_char_args_set(btheme->tarm[11].active, 0xbb, 0xef, 0x5b, 255); - /* set 13 */ - rgba_char_args_set(btheme->tarm[12].solid, 0x8d, 0x8d, 0x8d, 255); - rgba_char_args_set(btheme->tarm[12].select, 0xb0, 0xb0, 0xb0, 255); - rgba_char_args_set(btheme->tarm[12].active, 0xde, 0xde, 0xde, 255); - /* set 14 */ - rgba_char_args_set(btheme->tarm[13].solid, 0x83, 0x43, 0x26, 255); - rgba_char_args_set(btheme->tarm[13].select, 0x8b, 0x58, 0x11, 255); - rgba_char_args_set(btheme->tarm[13].active, 0xbd, 0x6a, 0x11, 255); - /* set 15 */ - rgba_char_args_set(btheme->tarm[14].solid, 0x08, 0x31, 0x0e, 255); - rgba_char_args_set(btheme->tarm[14].select, 0x1c, 0x43, 0x0b, 255); - rgba_char_args_set(btheme->tarm[14].active, 0x34, 0x62, 0x2b, 255); - - /* reset flags too */ - for (i = 0; i < 20; i++) - btheme->tarm[i].flag = 0; -} - -/* use this call to init new variables in themespace, if they're same for all */ -static void ui_theme_init_new_do(ThemeSpace *ts) -{ - rgba_char_args_set(ts->header_text, 0, 0, 0, 255); - rgba_char_args_set(ts->header_title, 0, 0, 0, 255); - rgba_char_args_set(ts->header_text_hi, 255, 255, 255, 255); - -#if 0 - rgba_char_args_set(ts->panel_text, 0, 0, 0, 255); - rgba_char_args_set(ts->panel_title, 0, 0, 0, 255); - rgba_char_args_set(ts->panel_text_hi, 255, 255, 255, 255); -#endif - - ts->panelcolors.show_back = false; - ts->panelcolors.show_header = false; - rgba_char_args_set(ts->panelcolors.back, 114, 114, 114, 128); - rgba_char_args_set(ts->panelcolors.header, 0, 0, 0, 25); - - rgba_char_args_set(ts->button, 145, 145, 145, 245); - rgba_char_args_set(ts->button_title, 0, 0, 0, 255); - rgba_char_args_set(ts->button_text, 0, 0, 0, 255); - rgba_char_args_set(ts->button_text_hi, 255, 255, 255, 255); - - rgba_char_args_set(ts->list, 165, 165, 165, 255); - rgba_char_args_set(ts->list_title, 0, 0, 0, 255); - rgba_char_args_set(ts->list_text, 0, 0, 0, 255); - rgba_char_args_set(ts->list_text_hi, 255, 255, 255, 255); - - rgba_char_args_set(ts->tab_active, 114, 114, 114, 255); - rgba_char_args_set(ts->tab_inactive, 83, 83, 83, 255); - rgba_char_args_set(ts->tab_back, 64, 64, 64, 255); - rgba_char_args_set(ts->tab_outline, 60, 60, 60, 255); -} - -static void ui_theme_init_new(bTheme *btheme) -{ - ThemeSpace *ts; - - for (ts = UI_THEMESPACE_START(btheme); ts != UI_THEMESPACE_END(btheme); ts++) { - ui_theme_init_new_do(ts); - } -} - -static void ui_theme_space_init_handles_color(ThemeSpace *theme_space) -{ - rgba_char_args_set(theme_space->handle_free, 0, 0, 0, 255); - rgba_char_args_set(theme_space->handle_auto, 0x90, 0x90, 0x00, 255); - rgba_char_args_set(theme_space->handle_vect, 0x40, 0x90, 0x30, 255); - rgba_char_args_set(theme_space->handle_align, 0x80, 0x30, 0x60, 255); - rgba_char_args_set(theme_space->handle_sel_free, 0, 0, 0, 255); - rgba_char_args_set(theme_space->handle_sel_auto, 0xf0, 0xff, 0x40, 255); - rgba_char_args_set(theme_space->handle_sel_vect, 0x40, 0xc0, 0x30, 255); - rgba_char_args_set(theme_space->handle_sel_align, 0xf0, 0x90, 0xa0, 255); - rgba_char_args_set(theme_space->handle_vertex, 0x00, 0x00, 0x00, 0xff); - rgba_char_args_set(theme_space->handle_vertex_select, 0xff, 0xff, 0, 0xff); - rgba_char_args_set(theme_space->act_spline, 0xdb, 0x25, 0x12, 255); -} - /** * initialize default theme * \note: when you add new colors, created & saved themes need initialized @@ -843,370 +737,19 @@ static void ui_theme_space_init_handles_color(ThemeSpace *theme_space) */ void ui_theme_init_default(void) { - bTheme *btheme; /* we search for the theme with name Default */ - btheme = BLI_findstring(&U.themes, "Default", offsetof(bTheme, name)); - + bTheme *btheme = BLI_findstring(&U.themes, "Default", offsetof(bTheme, name)); if (btheme == NULL) { - btheme = MEM_callocN(sizeof(bTheme), "theme"); + btheme = MEM_callocN(sizeof(bTheme), __func__); BLI_addtail(&U.themes, btheme); - strcpy(btheme->name, "Default"); } UI_SetTheme(0, 0); /* make sure the global used in this file is set */ - /* UI buttons */ - ui_widget_color_init(&btheme->tui); - - btheme->tui.iconfile[0] = 0; - rgba_char_args_set(btheme->tui.wcol_tooltip.text, 255, 255, 255, 255); - rgba_char_args_set_fl(btheme->tui.widget_emboss, 1.0f, 1.0f, 1.0f, 0.02f); - - rgba_char_args_set(btheme->tui.xaxis, 220, 0, 0, 255); - rgba_char_args_set(btheme->tui.yaxis, 0, 220, 0, 255); - rgba_char_args_set(btheme->tui.zaxis, 0, 0, 220, 255); - - btheme->tui.menu_shadow_fac = 0.5f; - btheme->tui.menu_shadow_width = 12; - - /* Bone Color Sets */ - ui_theme_init_boneColorSets(btheme); - - /* common (new) variables */ - ui_theme_init_new(btheme); - - /* space view3d */ - rgba_char_args_set_fl(btheme->tv3d.back, 0.225, 0.225, 0.225, 1.0); - rgba_char_args_set(btheme->tv3d.text, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.text_hi, 255, 255, 255, 255); - - rgba_char_args_set_fl(btheme->tv3d.header, 0.45, 0.45, 0.45, 1.0); - rgba_char_args_set_fl(btheme->tv3d.button, 0.45, 0.45, 0.45, 0.5); -// rgba_char_args_set(btheme->tv3d.panel, 165, 165, 165, 127); - - rgba_char_args_set(btheme->tv3d.shade1, 160, 160, 160, 100); - rgba_char_args_set(btheme->tv3d.shade2, 0x7f, 0x70, 0x70, 100); - - rgba_char_args_set_fl(btheme->tv3d.grid, 0.251, 0.251, 0.251, 1.0); - rgba_char_args_set(btheme->tv3d.view_overlay, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.wire, 0x0, 0x0, 0x0, 255); - rgba_char_args_set(btheme->tv3d.wire_edit, 0x0, 0x0, 0x0, 255); - rgba_char_args_set(btheme->tv3d.lamp, 0, 0, 0, 40); - rgba_char_args_set(btheme->tv3d.speaker, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.camera, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.empty, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.select, 241, 88, 0, 255); - rgba_char_args_set(btheme->tv3d.active, 255, 170, 64, 255); - rgba_char_args_set(btheme->tv3d.group, 8, 48, 8, 255); - rgba_char_args_set(btheme->tv3d.group_active, 85, 187, 85, 255); - rgba_char_args_set(btheme->tv3d.transform, 0xff, 0xff, 0xff, 255); - rgba_char_args_set(btheme->tv3d.vertex, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.vertex_select, 255, 133, 0, 255); - rgba_char_args_set(btheme->tv3d.vertex_bevel, 0, 165, 255, 255); - rgba_char_args_set(btheme->tv3d.vertex_unreferenced, 0, 0, 0, 255); - btheme->tv3d.vertex_size = 3; - btheme->tv3d.outline_width = 1; - rgba_char_args_set(btheme->tv3d.edge, 0x0, 0x0, 0x0, 255); - rgba_char_args_set(btheme->tv3d.edge_select, 255, 160, 0, 255); - rgba_char_args_set(btheme->tv3d.edge_seam, 219, 37, 18, 255); - rgba_char_args_set(btheme->tv3d.edge_bevel, 0, 165, 255, 255); - rgba_char_args_set(btheme->tv3d.edge_facesel, 75, 75, 75, 255); - rgba_char_args_set(btheme->tv3d.face, 0, 0, 0, 18); - rgba_char_args_set(btheme->tv3d.face_select, 255, 133, 0, 60); - rgba_char_args_set(btheme->tv3d.normal, 0x22, 0xDD, 0xDD, 255); - rgba_char_args_set(btheme->tv3d.vertex_normal, 0x23, 0x61, 0xDD, 255); - rgba_char_args_set(btheme->tv3d.loop_normal, 0xDD, 0x23, 0xDD, 255); - rgba_char_args_set(btheme->tv3d.face_dot, 255, 133, 0, 255); - rgba_char_args_set(btheme->tv3d.editmesh_active, 255, 255, 255, 128); - rgba_char_args_set_fl(btheme->tv3d.edge_crease, 0.8, 0, 0.6, 1.0); - rgba_char_args_set(btheme->tv3d.edge_sharp, 0, 255, 255, 255); - rgba_char_args_set(btheme->tv3d.header_text, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.header_text_hi, 255, 255, 255, 255); - rgba_char_args_set(btheme->tv3d.button_text, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.button_text_hi, 255, 255, 255, 255); - rgba_char_args_set(btheme->tv3d.button_title, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.title, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.freestyle_edge_mark, 0x7f, 0xff, 0x7f, 255); - rgba_char_args_set(btheme->tv3d.freestyle_face_mark, 0x7f, 0xff, 0x7f, 51); - rgba_char_args_set_fl(btheme->tv3d.paint_curve_handle, 0.5f, 1.0f, 0.5f, 0.5f); - rgba_char_args_set_fl(btheme->tv3d.paint_curve_pivot, 1.0f, 0.5f, 0.5f, 0.5f); - rgba_char_args_set(btheme->tv3d.gp_vertex, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.gp_vertex_select, 255, 133, 0, 255); - btheme->tv3d.gp_vertex_size = 3; - - btheme->tv3d.facedot_size = 4; - - rgba_char_args_set(btheme->tv3d.extra_edge_len, 32, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.extra_edge_angle, 32, 32, 0, 255); - rgba_char_args_set(btheme->tv3d.extra_face_area, 0, 32, 0, 255); - rgba_char_args_set(btheme->tv3d.extra_face_angle, 0, 0, 128, 255); - - rgba_char_args_set(btheme->tv3d.cframe, 0x60, 0xc0, 0x40, 255); - - rgba_char_args_set(btheme->tv3d.nurb_uline, 0x90, 0x90, 0x00, 255); - rgba_char_args_set(btheme->tv3d.nurb_vline, 0x80, 0x30, 0x60, 255); - rgba_char_args_set(btheme->tv3d.nurb_sel_uline, 0xf0, 0xff, 0x40, 255); - rgba_char_args_set(btheme->tv3d.nurb_sel_vline, 0xf0, 0x90, 0xa0, 255); - - ui_theme_space_init_handles_color(&btheme->tv3d); - - rgba_char_args_set(btheme->tv3d.act_spline, 0xdb, 0x25, 0x12, 255); - rgba_char_args_set(btheme->tv3d.lastsel_point, 0xff, 0xff, 0xff, 255); - - rgba_char_args_set(btheme->tv3d.bone_solid, 200, 200, 200, 255); - /* alpha 80 is not meant editable, used for wire+action draw */ - rgba_char_args_set(btheme->tv3d.bone_pose, 80, 200, 255, 80); - rgba_char_args_set(btheme->tv3d.bone_pose_active, 140, 255, 255, 80); - - rgba_char_args_set(btheme->tv3d.bundle_solid, 200, 200, 200, 255); - rgba_char_args_set(btheme->tv3d.camera_path, 0x00, 0x00, 0x00, 255); - - rgba_char_args_set(btheme->tv3d.skin_root, 180, 77, 77, 255); - rgba_char_args_set(btheme->tv3d.gradients.gradient, 0, 0, 0, 0); - rgba_char_args_set(btheme->tv3d.gradients.high_gradient, 58, 58, 58, 255); - btheme->tv3d.gradients.show_grad = false; - - rgba_char_args_set(btheme->tv3d.clipping_border_3d, 50, 50, 50, 255); - - rgba_char_args_set(btheme->tv3d.time_keyframe, 0xDD, 0xD7, 0x00, 0xFF); - rgba_char_args_set(btheme->tv3d.time_gp_keyframe, 0xB5, 0xE6, 0x1D, 0xFF); - - /* space buttons */ - /* to have something initialized */ - btheme->tbuts = btheme->tv3d; - - rgba_char_args_set_fl(btheme->tbuts.back, 0.45, 0.45, 0.45, 1.0); -// rgba_char_args_set(btheme->tbuts.panel, 0x82, 0x82, 0x82, 255); - - /* graph editor */ - btheme->tipo = btheme->tv3d; - rgba_char_args_set_fl(btheme->tipo.back, 0.42, 0.42, 0.42, 1.0); - rgba_char_args_set_fl(btheme->tipo.list, 0.4, 0.4, 0.4, 1.0); - rgba_char_args_set(btheme->tipo.grid, 94, 94, 94, 255); -// rgba_char_args_set(btheme->tipo.panel, 255, 255, 255, 150); - rgba_char_args_set(btheme->tipo.shade1, 150, 150, 150, 100); /* scrollbars */ - rgba_char_args_set(btheme->tipo.shade2, 0x70, 0x70, 0x70, 100); - rgba_char_args_set(btheme->tipo.vertex, 0, 0, 0, 255); - rgba_char_args_set(btheme->tipo.vertex_select, 255, 133, 0, 255); - rgba_char_args_set(btheme->tipo.hilite, 0x60, 0xc0, 0x40, 255); - btheme->tipo.vertex_size = 6; - - rgba_char_args_set(btheme->tipo.handle_vertex, 0, 0, 0, 255); - rgba_char_args_set(btheme->tipo.handle_vertex_select, 255, 133, 0, 255); - rgba_char_args_set(btheme->tipo.handle_auto_clamped, 0x99, 0x40, 0x30, 255); - rgba_char_args_set(btheme->tipo.handle_sel_auto_clamped, 0xf0, 0xaf, 0x90, 255); - btheme->tipo.handle_vertex_size = 5; - - rgba_char_args_set(btheme->tipo.ds_channel, 82, 96, 110, 255); - rgba_char_args_set(btheme->tipo.ds_subchannel, 124, 137, 150, 255); - rgba_char_args_set(btheme->tipo.group, 79, 101, 73, 255); - rgba_char_args_set(btheme->tipo.group_active, 135, 177, 125, 255); - - /* dopesheet */ - btheme->tact = btheme->tipo; - rgba_char_args_set(btheme->tact.strip, 12, 10, 10, 128); - rgba_char_args_set(btheme->tact.strip_select, 255, 140, 0, 255); - - rgba_char_args_set(btheme->tact.anim_active, 204, 112, 26, 102); - - rgba_char_args_set(btheme->tact.keytype_keyframe, 232, 232, 232, 255); - rgba_char_args_set(btheme->tact.keytype_keyframe_select, 255, 190, 50, 255); - rgba_char_args_set(btheme->tact.keytype_extreme, 232, 179, 204, 255); - rgba_char_args_set(btheme->tact.keytype_extreme_select, 242, 128, 128, 255); - rgba_char_args_set(btheme->tact.keytype_breakdown, 179, 219, 232, 255); - rgba_char_args_set(btheme->tact.keytype_breakdown_select, 84, 191, 237, 255); - rgba_char_args_set(btheme->tact.keytype_jitter, 148, 229, 117, 255); - rgba_char_args_set(btheme->tact.keytype_jitter_select, 97, 192, 66, 255); - - rgba_char_args_set(btheme->tact.keyborder, 0, 0, 0, 255); - rgba_char_args_set(btheme->tact.keyborder_select, 0, 0, 0, 255); - - btheme->tact.keyframe_scale_fac = 1.0f; - - /* space nla */ - btheme->tnla = btheme->tact; - - rgba_char_args_set(btheme->tnla.anim_active, 204, 112, 26, 102); /* same as for dopesheet; duplicate here for easier reference */ - rgba_char_args_set(btheme->tnla.anim_non_active, 153, 135, 97, 77); - - rgba_char_args_set(btheme->tnla.nla_tweaking, 77, 243, 26, 77); - rgba_char_args_set(btheme->tnla.nla_tweakdupli, 217, 0, 0, 255); - - rgba_char_args_set(btheme->tnla.nla_transition, 28, 38, 48, 255); - rgba_char_args_set(btheme->tnla.nla_transition_sel, 46, 117, 219, 255); - rgba_char_args_set(btheme->tnla.nla_meta, 51, 38, 66, 255); - rgba_char_args_set(btheme->tnla.nla_meta_sel, 105, 33, 150, 255); - rgba_char_args_set(btheme->tnla.nla_sound, 43, 61, 61, 255); - rgba_char_args_set(btheme->tnla.nla_sound_sel, 31, 122, 122, 255); - - rgba_char_args_set(btheme->tnla.keyborder, 0, 0, 0, 255); - rgba_char_args_set(btheme->tnla.keyborder_select, 0, 0, 0, 255); - - /* space file */ - /* to have something initialized */ - btheme->tfile = btheme->tv3d; - rgba_char_args_set_fl(btheme->tfile.back, 0.3, 0.3, 0.3, 1); -// rgba_char_args_set_fl(btheme->tfile.panel, 0.3, 0.3, 0.3, 1); - rgba_char_args_set_fl(btheme->tfile.list, 0.4, 0.4, 0.4, 1); - rgba_char_args_set(btheme->tfile.text, 250, 250, 250, 255); - rgba_char_args_set(btheme->tfile.text_hi, 15, 15, 15, 255); -// rgba_char_args_set(btheme->tfile.panel, 145, 145, 145, 255); /* bookmark/ui regions */ - rgba_char_args_set(btheme->tfile.hilite, 255, 140, 25, 255); /* selected files */ - - rgba_char_args_set(btheme->tfile.image, 250, 250, 250, 255); - rgba_char_args_set(btheme->tfile.movie, 250, 250, 250, 255); - rgba_char_args_set(btheme->tfile.scene, 250, 250, 250, 255); - - - /* space seq */ - btheme->tseq = btheme->tv3d; - rgba_char_args_set(btheme->tseq.back, 116, 116, 116, 255); - rgba_char_args_set(btheme->tseq.movie, 81, 105, 135, 255); - rgba_char_args_set(btheme->tseq.movieclip, 32, 32, 143, 255); - rgba_char_args_set(btheme->tseq.mask, 152, 78, 62, 255); - rgba_char_args_set(btheme->tseq.image, 109, 88, 129, 255); - rgba_char_args_set(btheme->tseq.scene, 78, 152, 62, 255); - rgba_char_args_set(btheme->tseq.audio, 46, 143, 143, 255); - rgba_char_args_set(btheme->tseq.effect, 169, 84, 124, 255); - rgba_char_args_set(btheme->tseq.transition, 162, 95, 111, 255); - rgba_char_args_set(btheme->tseq.meta, 109, 145, 131, 255); - rgba_char_args_set(btheme->tseq.text_strip, 162, 151, 0, 255); - rgba_char_args_set(btheme->tseq.preview_back, 0, 0, 0, 255); - rgba_char_args_set(btheme->tseq.grid, 64, 64, 64, 255); - - /* space image */ - btheme->tima = btheme->tv3d; - rgba_char_args_set(btheme->tima.back, 53, 53, 53, 255); - rgba_char_args_set(btheme->tima.vertex, 0, 0, 0, 255); - rgba_char_args_set(btheme->tima.vertex_select, 255, 133, 0, 255); - rgba_char_args_set(btheme->tima.wire_edit, 192, 192, 192, 255); - rgba_char_args_set(btheme->tima.edge_select, 255, 133, 0, 255); - btheme->tima.vertex_size = 3; - btheme->tima.facedot_size = 3; - rgba_char_args_set(btheme->tima.face, 255, 255, 255, 10); - rgba_char_args_set(btheme->tima.face_select, 255, 133, 0, 60); - rgba_char_args_set(btheme->tima.editmesh_active, 255, 255, 255, 128); - rgba_char_args_set_fl(btheme->tima.preview_back, 0.0, 0.0, 0.0, 0.3); - rgba_char_args_set_fl(btheme->tima.preview_stitch_face, 0.5, 0.5, 0.0, 0.2); - rgba_char_args_set_fl(btheme->tima.preview_stitch_edge, 1.0, 0.0, 1.0, 0.2); - rgba_char_args_set_fl(btheme->tima.preview_stitch_vert, 0.0, 0.0, 1.0, 0.2); - rgba_char_args_set_fl(btheme->tima.preview_stitch_stitchable, 0.0, 1.0, 0.0, 1.0); - rgba_char_args_set_fl(btheme->tima.preview_stitch_unstitchable, 1.0, 0.0, 0.0, 1.0); - rgba_char_args_set_fl(btheme->tima.preview_stitch_active, 0.886, 0.824, 0.765, 0.140); - - rgba_char_args_test_set(btheme->tima.uv_others, 96, 96, 96, 255); - rgba_char_args_test_set(btheme->tima.uv_shadow, 112, 112, 112, 255); - - ui_theme_space_init_handles_color(&btheme->tima); - btheme->tima.handle_vertex_size = 5; - - /* space text */ - btheme->text = btheme->tv3d; - rgba_char_args_set(btheme->text.back, 153, 153, 153, 255); - rgba_char_args_set(btheme->text.shade1, 143, 143, 143, 255); - rgba_char_args_set(btheme->text.shade2, 0xc6, 0x77, 0x77, 255); - rgba_char_args_set(btheme->text.hilite, 255, 0, 0, 255); - - /* syntax highlighting */ - rgba_char_args_set(btheme->text.syntaxn, 0, 0, 200, 255); /* Numbers Blue*/ - rgba_char_args_set(btheme->text.syntaxl, 100, 0, 0, 255); /* Strings Red */ - rgba_char_args_set(btheme->text.syntaxc, 0, 100, 50, 255); /* Comments Greenish */ - rgba_char_args_set(btheme->text.syntaxv, 95, 95, 0, 255); /* Special Yellow*/ - rgba_char_args_set(btheme->text.syntaxd, 50, 0, 140, 255); /* Decorator/Preprocessor Dir. Blue-purple */ - rgba_char_args_set(btheme->text.syntaxr, 140, 60, 0, 255); /* Reserved Orange*/ - rgba_char_args_set(btheme->text.syntaxb, 128, 0, 80, 255); /* Builtin Red-purple */ - rgba_char_args_set(btheme->text.syntaxs, 76, 76, 76, 255); /* Gray (mix between fg/bg) */ - - /* space oops */ - btheme->toops = btheme->tv3d; - rgba_char_args_set_fl(btheme->toops.back, 0.45, 0.45, 0.45, 1.0); - - rgba_char_args_set_fl(btheme->toops.match, 0.2, 0.5, 0.2, 0.3); /* highlighting search match - soft green*/ - rgba_char_args_set_fl(btheme->toops.selected_highlight, 0.51, 0.53, 0.55, 0.3); - - /* space info */ - btheme->tinfo = btheme->tv3d; - rgba_char_args_set_fl(btheme->tinfo.back, 0.45, 0.45, 0.45, 1.0); - rgba_char_args_set(btheme->tinfo.info_selected, 96, 128, 255, 255); - rgba_char_args_set(btheme->tinfo.info_selected_text, 255, 255, 255, 255); - rgba_char_args_set(btheme->tinfo.info_error, 220, 0, 0, 255); - rgba_char_args_set(btheme->tinfo.info_error_text, 0, 0, 0, 255); - rgba_char_args_set(btheme->tinfo.info_warning, 220, 128, 96, 255); - rgba_char_args_set(btheme->tinfo.info_warning_text, 0, 0, 0, 255); - rgba_char_args_set(btheme->tinfo.info_info, 0, 170, 0, 255); - rgba_char_args_set(btheme->tinfo.info_info_text, 0, 0, 0, 255); - rgba_char_args_set(btheme->tinfo.info_debug, 196, 196, 196, 255); - rgba_char_args_set(btheme->tinfo.info_debug_text, 0, 0, 0, 255); - - /* space user preferences */ - btheme->tuserpref = btheme->tv3d; - rgba_char_args_set_fl(btheme->tuserpref.back, 0.45, 0.45, 0.45, 1.0); - - /* space console */ - btheme->tconsole = btheme->tv3d; - rgba_char_args_set(btheme->tconsole.back, 0, 0, 0, 255); - rgba_char_args_set(btheme->tconsole.console_output, 96, 128, 255, 255); - rgba_char_args_set(btheme->tconsole.console_input, 255, 255, 255, 255); - rgba_char_args_set(btheme->tconsole.console_info, 0, 170, 0, 255); - rgba_char_args_set(btheme->tconsole.console_error, 220, 96, 96, 255); - rgba_char_args_set(btheme->tconsole.console_cursor, 220, 96, 96, 255); - rgba_char_args_set(btheme->tconsole.console_select, 255, 255, 255, 48); - - /* space time */ - btheme->ttime = btheme->tv3d; - rgba_char_args_set_fl(btheme->ttime.back, 0.45, 0.45, 0.45, 1.0); - rgba_char_args_set_fl(btheme->ttime.grid, 0.36, 0.36, 0.36, 1.0); - rgba_char_args_set(btheme->ttime.shade1, 173, 173, 173, 255); /* sliders */ - - rgba_char_args_set(btheme->ttime.time_keyframe, 0xDD, 0xD7, 0x00, 0xFF); - rgba_char_args_set(btheme->ttime.time_gp_keyframe, 0xB5, 0xE6, 0x1D, 0xFF); - - /* space node, re-uses syntax and console color storage */ - btheme->tnode = btheme->tv3d; - rgba_char_args_set(btheme->tnode.syntaxr, 115, 115, 115, 255); /* wire inner color */ - rgba_char_args_set(btheme->tnode.edge_select, 255, 255, 255, 255); /* wire selected */ - rgba_char_args_set(btheme->tnode.syntaxl, 155, 155, 155, 160); /* TH_NODE, backdrop */ - rgba_char_args_set(btheme->tnode.syntaxn, 100, 100, 100, 255); /* in */ - rgba_char_args_set(btheme->tnode.nodeclass_output, 100, 100, 100, 255); /* output */ - rgba_char_args_set(btheme->tnode.syntaxb, 108, 105, 111, 255); /* operator */ - rgba_char_args_set(btheme->tnode.syntaxv, 104, 106, 117, 255); /* generator */ - rgba_char_args_set(btheme->tnode.syntaxc, 105, 117, 110, 255); /* group */ - rgba_char_args_set(btheme->tnode.nodeclass_texture, 108, 105, 111, 255); /* operator */ - rgba_char_args_set(btheme->tnode.nodeclass_shader, 108, 105, 111, 255); /* operator */ - rgba_char_args_set(btheme->tnode.nodeclass_filter, 108, 105, 111, 255); /* operator */ - rgba_char_args_set(btheme->tnode.nodeclass_script, 108, 105, 111, 255); /* operator */ - rgba_char_args_set(btheme->tnode.nodeclass_pattern, 108, 105, 111, 255); /* operator */ - rgba_char_args_set(btheme->tnode.nodeclass_vector, 108, 105, 111, 255); /* operator */ - rgba_char_args_set(btheme->tnode.nodeclass_layout, 108, 105, 111, 255); /* operator */ - rgba_char_args_set(btheme->tnode.movie, 155, 155, 155, 160); /* frame */ - rgba_char_args_set(btheme->tnode.syntaxs, 151, 116, 116, 255); /* matte nodes */ - rgba_char_args_set(btheme->tnode.syntaxd, 116, 151, 151, 255); /* distort nodes */ - rgba_char_args_set(btheme->tnode.console_output, 223, 202, 53, 255); /* interface nodes */ - btheme->tnode.noodle_curving = 5; - - /* space logic */ - btheme->tlogic = btheme->tv3d; - rgba_char_args_set(btheme->tlogic.back, 100, 100, 100, 255); - - /* space clip */ - btheme->tclip = btheme->tv3d; - - rgba_char_args_set(btheme->tclip.marker_outline, 0x00, 0x00, 0x00, 255); - rgba_char_args_set(btheme->tclip.marker, 0x7f, 0x7f, 0x00, 255); - rgba_char_args_set(btheme->tclip.act_marker, 0xff, 0xff, 0xff, 255); - rgba_char_args_set(btheme->tclip.sel_marker, 0xff, 0xff, 0x00, 255); - rgba_char_args_set(btheme->tclip.dis_marker, 0x7f, 0x00, 0x00, 255); - rgba_char_args_set(btheme->tclip.lock_marker, 0x7f, 0x7f, 0x7f, 255); - rgba_char_args_set(btheme->tclip.path_before, 0xff, 0x00, 0x00, 255); - rgba_char_args_set(btheme->tclip.path_after, 0x00, 0x00, 0xff, 255); - rgba_char_args_set(btheme->tclip.grid, 0x5e, 0x5e, 0x5e, 255); - rgba_char_args_set(btheme->tclip.cframe, 0x60, 0xc0, 0x40, 255); - rgba_char_args_set(btheme->tclip.list, 0x66, 0x66, 0x66, 0xff); - rgba_char_args_set(btheme->tclip.strip, 0x0c, 0x0a, 0x0a, 0x80); - rgba_char_args_set(btheme->tclip.strip_select, 0xff, 0x8c, 0x00, 0xff); - btheme->tclip.handle_vertex_size = 5; - ui_theme_space_init_handles_color(&btheme->tclip); + const int active_theme_area = btheme->active_theme_area; + memcpy(btheme, &U_theme_default, sizeof(*btheme)); + btheme->active_theme_area = active_theme_area; } void ui_style_init_default(void) @@ -1273,25 +816,35 @@ void UI_ThemeColor4(int colorid) cp = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid); glColor4ubv(cp); - } /* set the color with offset for shades */ void UI_ThemeColorShade(int colorid, int offset) { - int r, g, b; + unsigned char col[4]; + UI_GetThemeColorShade4ubv(colorid, offset, col); + glColor4ubv(col); +} + +void UI_ThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset) +{ + int r, g, b, a; const unsigned char *cp; cp = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid); - r = offset + (int) cp[0]; + r = coloffset + (int) cp[0]; CLAMP(r, 0, 255); - g = offset + (int) cp[1]; + g = coloffset + (int) cp[1]; CLAMP(g, 0, 255); - b = offset + (int) cp[2]; + b = coloffset + (int) cp[2]; CLAMP(b, 0, 255); - glColor4ub(r, g, b, cp[3]); + a = alphaoffset + (int) cp[3]; + CLAMP(a, 0, 255); + + glColor4ub(r, g, b, a); } -void UI_ThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset) + +void UI_GetThemeColorShadeAlpha4ubv(int colorid, int coloffset, int alphaoffset, unsigned char col[4]) { int r, g, b, a; const unsigned char *cp; @@ -1305,7 +858,11 @@ void UI_ThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset) CLAMP(b, 0, 255); a = alphaoffset + (int) cp[3]; CLAMP(a, 0, 255); - glColor4ub(r, g, b, a); + + col[0] = r; + col[1] = g; + col[2] = b; + col[3] = a; } void UI_GetThemeColorBlend3ubv(int colorid1, int colorid2, float fac, unsigned char col[3]) @@ -1321,6 +878,19 @@ void UI_GetThemeColorBlend3ubv(int colorid1, int colorid2, float fac, unsigned c col[2] = floorf((1.0f - fac) * cp1[2] + fac * cp2[2]); } +void UI_GetThemeColorBlend3f(int colorid1, int colorid2, float fac, float r_col[3]) +{ + const unsigned char *cp1, *cp2; + + cp1 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1); + cp2 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2); + + CLAMP(fac, 0.0f, 1.0f); + r_col[0] = ((1.0f - fac) * cp1[0] + fac * cp2[0]) / 255.0f; + r_col[1] = ((1.0f - fac) * cp1[1] + fac * cp2[1]) / 255.0f; + r_col[2] = ((1.0f - fac) * cp1[2] + fac * cp2[2]) / 255.0f; +} + /* blend between to theme colors, and set it */ void UI_ThemeColorBlend(int colorid1, int colorid2, float fac) { @@ -1373,6 +943,12 @@ void UI_ThemeColorBlendShadeAlpha(int colorid1, int colorid2, float fac, int off glColor4ub(r, g, b, a); } +void UI_FontThemeColor(int fontid, int colorid) +{ + unsigned char color[4]; + UI_GetThemeColor4ubv(colorid, color); + BLF_color4ubv(fontid, color); +} /* get individual values, not scaled */ float UI_GetThemeValuef(int colorid) @@ -1471,6 +1047,111 @@ void UI_GetThemeColorShade3ubv(int colorid, int offset, unsigned char col[3]) col[2] = b; } +void UI_GetThemeColorBlendShade3ubv(int colorid1, int colorid2, float fac, int offset, unsigned char col[3]) +{ + const unsigned char *cp1, *cp2; + + cp1 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1); + cp2 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2); + + CLAMP(fac, 0.0f, 1.0f); + + float blend[3]; + blend[0] = offset + floorf((1.0f - fac) * cp1[0] + fac * cp2[0]); + blend[1] = offset + floorf((1.0f - fac) * cp1[1] + fac * cp2[1]); + blend[2] = offset + floorf((1.0f - fac) * cp1[2] + fac * cp2[2]); + + unit_float_to_uchar_clamp_v3(col, blend); +} + +void UI_GetThemeColorShade4ubv(int colorid, int offset, unsigned char col[4]) +{ + int r, g, b; + const unsigned char *cp; + + cp = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid); + r = offset + (int) cp[0]; + CLAMP(r, 0, 255); + g = offset + (int) cp[1]; + CLAMP(g, 0, 255); + b = offset + (int) cp[2]; + CLAMP(b, 0, 255); + + col[0] = r; + col[1] = g; + col[2] = b; + col[3] = cp[3]; +} + +void UI_GetThemeColorShadeAlpha4fv(int colorid, int coloffset, int alphaoffset, float col[4]) +{ + int r, g, b, a; + const unsigned char *cp; + + cp = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid); + + r = coloffset + (int) cp[0]; + CLAMP(r, 0, 255); + g = coloffset + (int) cp[1]; + CLAMP(g, 0, 255); + b = coloffset + (int) cp[2]; + CLAMP(b, 0, 255); + a = alphaoffset + (int) cp[3]; + CLAMP(b, 0, 255); + + col[0] = ((float)r) / 255.0f; + col[1] = ((float)g) / 255.0f; + col[2] = ((float)b) / 255.0f; + col[3] = ((float)a) / 255.0f; +} + +void UI_GetThemeColorBlendShade3fv(int colorid1, int colorid2, float fac, int offset, float col[3]) +{ + int r, g, b; + const unsigned char *cp1, *cp2; + + cp1 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1); + cp2 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2); + + CLAMP(fac, 0.0f, 1.0f); + + r = offset + floorf((1.0f - fac) * cp1[0] + fac * cp2[0]); + CLAMP(r, 0, 255); + g = offset + floorf((1.0f - fac) * cp1[1] + fac * cp2[1]); + CLAMP(g, 0, 255); + b = offset + floorf((1.0f - fac) * cp1[2] + fac * cp2[2]); + CLAMP(b, 0, 255); + + col[0] = ((float)r) / 255.0f; + col[1] = ((float)g) / 255.0f; + col[2] = ((float)b) / 255.0f; +} + +void UI_GetThemeColorBlendShade4fv(int colorid1, int colorid2, float fac, int offset, float col[4]) +{ + int r, g, b, a; + const unsigned char *cp1, *cp2; + + cp1 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1); + cp2 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2); + + CLAMP(fac, 0.0f, 1.0f); + + r = offset + floorf((1.0f - fac) * cp1[0] + fac * cp2[0]); + CLAMP(r, 0, 255); + g = offset + floorf((1.0f - fac) * cp1[1] + fac * cp2[1]); + CLAMP(g, 0, 255); + b = offset + floorf((1.0f - fac) * cp1[2] + fac * cp2[2]); + CLAMP(b, 0, 255); + a = offset + floorf((1.0f - fac) * cp1[3] + fac * cp2[3]); + CLAMP(a, 0, 255); + + col[0] = ((float)r) / 255.0f; + col[1] = ((float)g) / 255.0f; + col[2] = ((float)b) / 255.0f; + col[3] = ((float)a) / 255.0f; +} + /* get the color, in char pointer */ void UI_GetThemeColor3ubv(int colorid, unsigned char col[3]) { @@ -1588,14 +1269,14 @@ void UI_ThemeClearColor(int colorid) float col[3]; UI_GetThemeColor3fv(colorid, col); - glClearColor(col[0], col[1], col[2], 0.0f); + GPU_clear_color(col[0], col[1], col[2], 0.0f); } void UI_ThemeClearColorAlpha(int colorid, float alpha) { float col[3]; UI_GetThemeColor3fv(colorid, col); - glClearColor(col[0], col[1], col[2], alpha); + GPU_clear_color(col[0], col[1], col[2], alpha); } @@ -1657,11 +1338,9 @@ void init_userdef_do_versions(Main *bmain) U.savetime = 1; // XXX error(STRINGIFY(BLENDER_STARTUP_FILE)" is buggy, please consider removing it.\n"); } - /* transform widget settings */ - if (U.tw_hotspot == 0) { - U.tw_hotspot = 14; - U.tw_size = 25; /* percentage of window size */ - U.tw_handlesize = 16; /* percentage of widget radius */ + if (U.gizmo_size == 0) { + U.gizmo_size = 75; + U.gizmo_flag |= USER_GIZMO_DRAW; } if (U.pad_rot_angle == 0.0f) U.pad_rot_angle = 15.0f; @@ -1671,9 +1350,9 @@ void init_userdef_do_versions(Main *bmain) U.fcu_inactive_alpha = 0.25f; } - /* signal for derivedmesh to use colorband */ + /* signal for evaluated mesh to use colorband */ /* run in case this was on and is now off in the user prefs [#28096] */ - vDM_ColorBand_store((U.flag & USER_CUSTOM_RANGE) ? (&U.coba_weight) : NULL, UI_GetTheme()->tv3d.vertex_unreferenced); + BKE_mesh_runtime_color_band_store((U.flag & USER_CUSTOM_RANGE) ? (&U.coba_weight) : NULL, UI_GetTheme()->tv3d.vertex_unreferenced); if (!USER_VERSION_ATLEAST(192, 0)) { strcpy(U.sounddir, "/"); @@ -1686,243 +1365,40 @@ void init_userdef_do_versions(Main *bmain) /* added seam, normal color, undo */ if (!USER_VERSION_ATLEAST(235, 0)) { - bTheme *btheme; - U.uiflag |= USER_GLOBALUNDO; if (U.undosteps == 0) U.undosteps = 32; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* check for (alpha == 0) is safe, then color was never set */ - if (btheme->tv3d.edge_seam[3] == 0) { - rgba_char_args_set(btheme->tv3d.edge_seam, 230, 150, 50, 255); - } - if (btheme->tv3d.normal[3] == 0) { - rgba_char_args_set(btheme->tv3d.normal, 0x22, 0xDD, 0xDD, 255); - } - if (btheme->tv3d.vertex_normal[3] == 0) { - rgba_char_args_set(btheme->tv3d.vertex_normal, 0x23, 0x61, 0xDD, 255); - } - if (btheme->tv3d.face_dot[3] == 0) { - rgba_char_args_set(btheme->tv3d.face_dot, 255, 138, 48, 255); - btheme->tv3d.facedot_size = 4; - } - } } if (!USER_VERSION_ATLEAST(236, 0)) { /* illegal combo... */ if (U.flag & USER_LMOUSESELECT) U.flag &= ~USER_TWOBUTTONMOUSE; } - if (!USER_VERSION_ATLEAST(237, 0)) { - bTheme *btheme; - /* new space type */ - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* check for (alpha == 0) is safe, then color was never set */ - if (btheme->ttime.back[3] == 0) { - /* copied from ui_theme_init_default */ - btheme->ttime = btheme->tv3d; - rgba_char_args_set_fl(btheme->ttime.back, 0.45, 0.45, 0.45, 1.0); - rgba_char_args_set_fl(btheme->ttime.grid, 0.36, 0.36, 0.36, 1.0); - rgba_char_args_set(btheme->ttime.shade1, 173, 173, 173, 255); /* sliders */ - } - if (btheme->text.syntaxn[3] == 0) { - rgba_char_args_set(btheme->text.syntaxn, 0, 0, 200, 255); /* Numbers Blue*/ - rgba_char_args_set(btheme->text.syntaxl, 100, 0, 0, 255); /* Strings red */ - rgba_char_args_set(btheme->text.syntaxc, 0, 100, 50, 255); /* Comments greenish */ - rgba_char_args_set(btheme->text.syntaxv, 95, 95, 0, 255); /* Special */ - rgba_char_args_set(btheme->text.syntaxb, 128, 0, 80, 255); /* Builtin, red-purple */ - } - } - } - if (!USER_VERSION_ATLEAST(238, 0)) { - bTheme *btheme; - /* bone colors */ - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* check for alpha==0 is safe, then color was never set */ - if (btheme->tv3d.bone_solid[3] == 0) { - rgba_char_args_set(btheme->tv3d.bone_solid, 200, 200, 200, 255); - rgba_char_args_set(btheme->tv3d.bone_pose, 80, 200, 255, 80); - } - } - } - if (!USER_VERSION_ATLEAST(239, 0)) { - bTheme *btheme; - /* bone colors */ - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* check for alpha==0 is safe, then color was never set */ - if (btheme->tnla.strip[3] == 0) { - rgba_char_args_set(btheme->tnla.strip_select, 0xff, 0xff, 0xaa, 255); - rgba_char_args_set(btheme->tnla.strip, 0xe4, 0x9c, 0xc6, 255); - } - } - } if (!USER_VERSION_ATLEAST(240, 0)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* Lamp theme, check for alpha==0 is safe, then color was never set */ - if (btheme->tv3d.lamp[3] == 0) { - rgba_char_args_set(btheme->tv3d.lamp, 0, 0, 0, 40); -/* TEMPORAL, remove me! (ton) */ - U.uiflag |= USER_PLAINMENUS; - } - - } + U.uiflag |= USER_PLAINMENUS; if (U.obcenter_dia == 0) U.obcenter_dia = 6; } if (!USER_VERSION_ATLEAST(242, 0)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* Node editor theme, check for alpha==0 is safe, then color was never set */ - if (btheme->tnode.syntaxn[3] == 0) { - /* re-uses syntax color storage */ - btheme->tnode = btheme->tv3d; - rgba_char_args_set(btheme->tnode.edge_select, 255, 255, 255, 255); - rgba_char_args_set(btheme->tnode.syntaxl, 150, 150, 150, 255); /* TH_NODE, backdrop */ - rgba_char_args_set(btheme->tnode.syntaxn, 129, 131, 144, 255); /* in/output */ - rgba_char_args_set(btheme->tnode.syntaxb, 127, 127, 127, 255); /* operator */ - rgba_char_args_set(btheme->tnode.syntaxv, 142, 138, 145, 255); /* generator */ - rgba_char_args_set(btheme->tnode.syntaxc, 120, 145, 120, 255); /* group */ - } - /* Group theme colors */ - if (btheme->tv3d.group[3] == 0) { - rgba_char_args_set(btheme->tv3d.group, 0x0C, 0x30, 0x0C, 255); - rgba_char_args_set(btheme->tv3d.group_active, 0x66, 0xFF, 0x66, 255); - } - /* Sequence editor theme*/ - if (btheme->tseq.movie[3] == 0) { - rgba_char_args_set(btheme->tseq.movie, 81, 105, 135, 255); - rgba_char_args_set(btheme->tseq.image, 109, 88, 129, 255); - rgba_char_args_set(btheme->tseq.scene, 78, 152, 62, 255); - rgba_char_args_set(btheme->tseq.audio, 46, 143, 143, 255); - rgba_char_args_set(btheme->tseq.effect, 169, 84, 124, 255); - rgba_char_args_set(btheme->tseq.transition, 162, 95, 111, 255); - rgba_char_args_set(btheme->tseq.meta, 109, 145, 131, 255); - } - } - /* set defaults for 3D View rotating axis indicator */ /* since size can't be set to 0, this indicates it's not saved in startup.blend */ if (U.rvisize == 0) { U.rvisize = 15; U.rvibright = 8; - U.uiflag |= USER_SHOW_ROTVIEWICON; + U.uiflag |= USER_SHOW_GIZMO_AXIS; } } - if (!USER_VERSION_ATLEAST(243, 0)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* long keyframe color */ - /* check for alpha==0 is safe, then color was never set */ - if (btheme->tact.strip[3] == 0) { - rgba_char_args_set(btheme->tv3d.edge_sharp, 255, 32, 32, 255); - rgba_char_args_set(btheme->tact.strip_select, 0xff, 0xff, 0xaa, 204); - rgba_char_args_set(btheme->tact.strip, 0xe4, 0x9c, 0xc6, 204); - } - - /* IPO-Editor - Vertex Size*/ - if (btheme->tipo.vertex_size == 0) { - btheme->tipo.vertex_size = 3; - } - } - } if (!USER_VERSION_ATLEAST(244, 0)) { /* set default number of recently-used files (if not set) */ if (U.recent_files == 0) U.recent_files = 10; } if (!USER_VERSION_ATLEAST(245, 3)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tv3d.editmesh_active, 255, 255, 255, 128); - } if (U.coba_weight.tot == 0) BKE_colorband_init(&U.coba_weight, true); } if (!USER_VERSION_ATLEAST(245, 3)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* these should all use the same color */ - rgba_char_args_set(btheme->tv3d.cframe, 0x60, 0xc0, 0x40, 255); - rgba_char_args_set(btheme->tipo.cframe, 0x60, 0xc0, 0x40, 255); - rgba_char_args_set(btheme->tact.cframe, 0x60, 0xc0, 0x40, 255); - rgba_char_args_set(btheme->tnla.cframe, 0x60, 0xc0, 0x40, 255); - rgba_char_args_set(btheme->tseq.cframe, 0x60, 0xc0, 0x40, 255); - //rgba_char_args_set(btheme->tsnd.cframe, 0x60, 0xc0, 0x40, 255); Not needed anymore - rgba_char_args_set(btheme->ttime.cframe, 0x60, 0xc0, 0x40, 255); - } - } - if (!USER_VERSION_ATLEAST(245, 3)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* action channel groups (recolor anyway) */ - rgba_char_args_set(btheme->tact.group, 0x39, 0x7d, 0x1b, 255); - rgba_char_args_set(btheme->tact.group_active, 0x7d, 0xe9, 0x60, 255); - - /* bone custom-color sets */ - if (btheme->tarm[0].solid[3] == 0) - ui_theme_init_boneColorSets(btheme); - } - } - if (!USER_VERSION_ATLEAST(245, 3)) { U.flag |= USER_ADD_VIEWALIGNED | USER_ADD_EDITMODE; } - if (!USER_VERSION_ATLEAST(245, 3)) { - bTheme *btheme; - - /* adjust themes */ - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - const char *col; - - /* IPO Editor: Handles/Vertices */ - col = btheme->tipo.vertex; - rgba_char_args_set(btheme->tipo.handle_vertex, col[0], col[1], col[2], 255); - col = btheme->tipo.vertex_select; - rgba_char_args_set(btheme->tipo.handle_vertex_select, col[0], col[1], col[2], 255); - btheme->tipo.handle_vertex_size = btheme->tipo.vertex_size; - - /* Sequence/Image Editor: colors for GPencil text */ - col = btheme->tv3d.bone_pose; - rgba_char_args_set(btheme->tseq.bone_pose, col[0], col[1], col[2], 255); - rgba_char_args_set(btheme->tima.bone_pose, col[0], col[1], col[2], 255); - col = btheme->tv3d.vertex_select; - rgba_char_args_set(btheme->tseq.vertex_select, col[0], col[1], col[2], 255); - } - } if (!USER_VERSION_ATLEAST(250, 0)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* this was not properly initialized in 2.45 */ - if (btheme->tima.face_dot[3] == 0) { - rgba_char_args_set(btheme->tima.editmesh_active, 255, 255, 255, 128); - rgba_char_args_set(btheme->tima.face_dot, 255, 133, 0, 255); - btheme->tima.facedot_size = 2; - } - - /* DopeSheet - (Object) Channel color */ - rgba_char_args_set(btheme->tact.ds_channel, 82, 96, 110, 255); - rgba_char_args_set(btheme->tact.ds_subchannel, 124, 137, 150, 255); - /* DopeSheet - Group Channel color (saner version) */ - rgba_char_args_set(btheme->tact.group, 79, 101, 73, 255); - rgba_char_args_set(btheme->tact.group_active, 135, 177, 125, 255); - - /* Graph Editor - (Object) Channel color */ - rgba_char_args_set(btheme->tipo.ds_channel, 82, 96, 110, 255); - rgba_char_args_set(btheme->tipo.ds_subchannel, 124, 137, 150, 255); - /* Graph Editor - Group Channel color */ - rgba_char_args_set(btheme->tipo.group, 79, 101, 73, 255); - rgba_char_args_set(btheme->tipo.group_active, 135, 177, 125, 255); - - /* Nla Editor - (Object) Channel color */ - rgba_char_args_set(btheme->tnla.ds_channel, 82, 96, 110, 255); - rgba_char_args_set(btheme->tnla.ds_subchannel, 124, 137, 150, 255); - /* NLA Editor - New Strip colors */ - rgba_char_args_set(btheme->tnla.strip, 12, 10, 10, 128); - rgba_char_args_set(btheme->tnla.strip_select, 255, 140, 0, 255); - } - /* adjust grease-pencil distances */ U.gp_manhattendist = 1; U.gp_euclideandist = 2; @@ -1931,29 +1407,6 @@ void init_userdef_do_versions(Main *bmain) U.ipo_new = BEZT_IPO_BEZ; } - if (!USER_VERSION_ATLEAST(250, 1)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - - /* common (new) variables, it checks for alpha==0 */ - ui_theme_init_new(btheme); - - if (btheme->tui.wcol_num.outline[3] == 0) - ui_widget_color_init(&btheme->tui); - - /* Logic editor theme, check for alpha==0 is safe, then color was never set */ - if (btheme->tlogic.syntaxn[3] == 0) { - /* re-uses syntax color storage */ - btheme->tlogic = btheme->tv3d; - rgba_char_args_set(btheme->tlogic.back, 100, 100, 100, 255); - } - - rgba_char_args_set_fl(btheme->tinfo.back, 0.45, 0.45, 0.45, 1.0); - rgba_char_args_set_fl(btheme->tuserpref.back, 0.45, 0.45, 0.45, 1.0); - } - } - if (!USER_VERSION_ATLEAST(250, 3)) { /* new audio system */ if (U.audiochannels == 0) @@ -1984,8 +1437,6 @@ void init_userdef_do_versions(Main *bmain) strcpy(km->idname, "3D View Generic"); else if (STREQ(km->idname, "EditMesh")) strcpy(km->idname, "Mesh"); - else if (STREQ(km->idname, "TimeLine")) - strcpy(km->idname, "Timeline"); else if (STREQ(km->idname, "UVEdit")) strcpy(km->idname, "UV Editor"); else if (STREQ(km->idname, "Animation_Channels")) @@ -2012,76 +1463,14 @@ void init_userdef_do_versions(Main *bmain) strcpy(km->idname, "Property Editor"); } } - if (!USER_VERSION_ATLEAST(250, 16)) { - if (U.wmdrawmethod == USER_DRAW_TRIPLE) - U.wmdrawmethod = USER_DRAW_AUTOMATIC; - } if (!USER_VERSION_ATLEAST(252, 3)) { if (U.flag & USER_LMOUSESELECT) U.flag &= ~USER_TWOBUTTONMOUSE; } if (!USER_VERSION_ATLEAST(252, 4)) { - bTheme *btheme; - /* default new handle type is auto handles */ U.keyhandles_new = HD_AUTO; - - /* init new curve colors */ - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - ui_theme_space_init_handles_color(&btheme->tv3d); - ui_theme_space_init_handles_color(&btheme->tipo); - - /* edge crease */ - rgba_char_args_set_fl(btheme->tv3d.edge_crease, 0.8, 0, 0.6, 1.0); - } - } - if (!USER_VERSION_ATLEAST(253, 0)) { - bTheme *btheme; - - /* init new curve colors */ - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (btheme->tv3d.lastsel_point[3] == 0) - rgba_char_args_set(btheme->tv3d.lastsel_point, 0xff, 0xff, 0xff, 255); - } - } - if (!USER_VERSION_ATLEAST(252, 5)) { - bTheme *btheme; - - /* interface_widgets.c */ - struct uiWidgetColors wcol_progress = { - {0, 0, 0, 255}, - {190, 190, 190, 255}, - {100, 100, 100, 180}, - {128, 128, 128, 255}, - - {0, 0, 0, 255}, - {255, 255, 255, 255}, - - 0, - 5, -5 - }; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* init progress bar theme */ - btheme->tui.wcol_progress = wcol_progress; - } - } - - if (!USER_VERSION_ATLEAST(255, 2)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tv3d.extra_edge_len, 32, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.extra_face_angle, 0, 32, 0, 255); - rgba_char_args_set(btheme->tv3d.extra_face_area, 0, 0, 128, 255); - } - } - - if (!USER_VERSION_ATLEAST(256, 4)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if ((btheme->tv3d.outline_width) == 0) btheme->tv3d.outline_width = 1; - } } if (!USER_VERSION_ATLEAST(257, 0)) { @@ -2090,226 +1479,22 @@ void init_userdef_do_versions(Main *bmain) U.autokey_flag &= ~AUTOKEY_FLAG_ONLYKEYINGSET; } - if (!USER_VERSION_ATLEAST(258, 2)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - btheme->tnode.noodle_curving = 5; - } - } - - if (!USER_VERSION_ATLEAST(259, 1)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - btheme->tv3d.speaker[3] = 255; - } - } - if (!USER_VERSION_ATLEAST(260, 3)) { - bTheme *btheme; - /* if new keyframes handle default is stuff "auto", make it "auto-clamped" instead * was changed in 260 as part of GSoC11, but version patch was wrong */ if (U.keyhandles_new == HD_AUTO) U.keyhandles_new = HD_AUTO_ANIM; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (btheme->tv3d.bundle_solid[3] == 0) - rgba_char_args_set(btheme->tv3d.bundle_solid, 200, 200, 200, 255); - - if (btheme->tv3d.camera_path[3] == 0) - rgba_char_args_set(btheme->tv3d.camera_path, 0x00, 0x00, 0x00, 255); - - if ((btheme->tclip.back[3]) == 0) { - btheme->tclip = btheme->tv3d; - - rgba_char_args_set(btheme->tclip.marker_outline, 0x00, 0x00, 0x00, 255); - rgba_char_args_set(btheme->tclip.marker, 0x7f, 0x7f, 0x00, 255); - rgba_char_args_set(btheme->tclip.act_marker, 0xff, 0xff, 0xff, 255); - rgba_char_args_set(btheme->tclip.sel_marker, 0xff, 0xff, 0x00, 255); - rgba_char_args_set(btheme->tclip.dis_marker, 0x7f, 0x00, 0x00, 255); - rgba_char_args_set(btheme->tclip.lock_marker, 0x7f, 0x7f, 0x7f, 255); - rgba_char_args_set(btheme->tclip.path_before, 0xff, 0x00, 0x00, 255); - rgba_char_args_set(btheme->tclip.path_after, 0x00, 0x00, 0xff, 255); - rgba_char_args_set(btheme->tclip.grid, 0x5e, 0x5e, 0x5e, 255); - rgba_char_args_set(btheme->tclip.cframe, 0x60, 0xc0, 0x40, 255); - rgba_char_args_set(btheme->tclip.handle_vertex, 0x00, 0x00, 0x00, 0xff); - rgba_char_args_set(btheme->tclip.handle_vertex_select, 0xff, 0xff, 0, 0xff); - btheme->tclip.handle_vertex_size = 5; - } - - /* auto-clamped handles -> based on auto */ - if (btheme->tipo.handle_auto_clamped[3] == 0) - rgba_char_args_set(btheme->tipo.handle_auto_clamped, 0x99, 0x40, 0x30, 255); - if (btheme->tipo.handle_sel_auto_clamped[3] == 0) - rgba_char_args_set(btheme->tipo.handle_sel_auto_clamped, 0xf0, 0xaf, 0x90, 255); - } - /* enable (Cycles) addon by default */ BKE_addon_ensure(&U.addons, "cycles"); } - if (!USER_VERSION_ATLEAST(260, 5)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tui.panel.header, 0, 0, 0, 25); - btheme->tui.icon_alpha = 1.0; - } - } - if (!USER_VERSION_ATLEAST(261, 4)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set_fl(btheme->tima.preview_stitch_face, 0.071, 0.259, 0.694, 0.150); - rgba_char_args_set_fl(btheme->tima.preview_stitch_edge, 1.0, 0.522, 0.0, 0.7); - rgba_char_args_set_fl(btheme->tima.preview_stitch_vert, 1.0, 0.522, 0.0, 0.5); - rgba_char_args_set_fl(btheme->tima.preview_stitch_stitchable, 0.0, 1.0, 0.0, 1.0); - rgba_char_args_set_fl(btheme->tima.preview_stitch_unstitchable, 1.0, 0.0, 0.0, 1.0); - rgba_char_args_set_fl(btheme->tima.preview_stitch_active, 0.886, 0.824, 0.765, 0.140); - - rgba_char_args_set_fl(btheme->toops.match, 0.2, 0.5, 0.2, 0.3); - rgba_char_args_set_fl(btheme->toops.selected_highlight, 0.51, 0.53, 0.55, 0.3); - } - U.use_16bit_textures = true; } - if (!USER_VERSION_ATLEAST(262, 2)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (btheme->tui.wcol_menu_item.item[3] == 255) - rgba_char_args_set(btheme->tui.wcol_menu_item.item, 172, 172, 172, 128); - } - } - - if (!USER_VERSION_ATLEAST(262, 3)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (btheme->tui.wcol_tooltip.inner[3] == 0) { - btheme->tui.wcol_tooltip = btheme->tui.wcol_menu_back; - } - if (btheme->tui.wcol_tooltip.text[0] == 160) { /* hrmf */ - rgba_char_args_set(btheme->tui.wcol_tooltip.text, 255, 255, 255, 255); - } - } - } - - if (!USER_VERSION_ATLEAST(262, 4)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (btheme->tseq.movieclip[3] == 0) { - rgba_char_args_set(btheme->tseq.movieclip, 32, 32, 143, 255); - } - } - } - - if (!USER_VERSION_ATLEAST(263, 2)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (btheme->tclip.strip[0] == 0) { - rgba_char_args_set(btheme->tclip.list, 0x66, 0x66, 0x66, 0xff); - rgba_char_args_set(btheme->tclip.strip, 0x0c, 0x0a, 0x0a, 0x80); - rgba_char_args_set(btheme->tclip.strip_select, 0xff, 0x8c, 0x00, 0xff); - } - } - } - - if (!USER_VERSION_ATLEAST(263, 6)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) - rgba_char_args_set(btheme->tv3d.skin_root, 180, 77, 77, 255); - } - - if (!USER_VERSION_ATLEAST(263, 7)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* DopeSheet Summary */ - rgba_char_args_set(btheme->tact.anim_active, 204, 112, 26, 102); - - /* NLA Colors */ - rgba_char_args_set(btheme->tnla.anim_active, 204, 112, 26, 102); /* same as dopesheet above */ - rgba_char_args_set(btheme->tnla.anim_non_active, 153, 135, 97, 77); - - rgba_char_args_set(btheme->tnla.nla_tweaking, 77, 243, 26, 77); - rgba_char_args_set(btheme->tnla.nla_tweakdupli, 217, 0, 0, 255); - - rgba_char_args_set(btheme->tnla.nla_transition, 28, 38, 48, 255); - rgba_char_args_set(btheme->tnla.nla_transition_sel, 46, 117, 219, 255); - rgba_char_args_set(btheme->tnla.nla_meta, 51, 38, 66, 255); - rgba_char_args_set(btheme->tnla.nla_meta_sel, 105, 33, 150, 255); - rgba_char_args_set(btheme->tnla.nla_sound, 43, 61, 61, 255); - rgba_char_args_set(btheme->tnla.nla_sound_sel, 31, 122, 122, 255); - } - } - - if (!USER_VERSION_ATLEAST(263, 11)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (btheme->tseq.mask[3] == 0) { - rgba_char_args_set(btheme->tseq.mask, 152, 78, 62, 255); - } - } - } - - if (!USER_VERSION_ATLEAST(263, 15)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tv3d.bone_pose_active, 140, 255, 255, 80); - } - } - - if (!USER_VERSION_ATLEAST(263, 16)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (btheme->tact.anim_active[3] == 0) - rgba_char_args_set(btheme->tact.anim_active, 204, 112, 26, 102); - - if (btheme->tnla.anim_active[3] == 0) - rgba_char_args_set(btheme->tnla.anim_active, 204, 112, 26, 102); - } - } - - if (!USER_VERSION_ATLEAST(263, 22)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (btheme->tipo.lastsel_point[3] == 0) - rgba_char_args_set(btheme->tipo.lastsel_point, 0xff, 0xff, 0xff, 255); - - if (btheme->tv3d.skin_root[3] == 0) - rgba_char_args_set(btheme->tv3d.skin_root, 180, 77, 77, 255); - } - } - - if (!USER_VERSION_ATLEAST(264, 9)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tui.xaxis, 220, 0, 0, 255); - rgba_char_args_set(btheme->tui.yaxis, 0, 220, 0, 255); - rgba_char_args_set(btheme->tui.zaxis, 0, 0, 220, 255); - } - } - if (!USER_VERSION_ATLEAST(267, 0)) { - /* Freestyle color settings */ - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* check for alpha == 0 is safe, then color was never set */ - if (btheme->tv3d.freestyle_edge_mark[3] == 0) { - rgba_char_args_set(btheme->tv3d.freestyle_edge_mark, 0x7f, 0xff, 0x7f, 255); - rgba_char_args_set(btheme->tv3d.freestyle_face_mark, 0x7f, 0xff, 0x7f, 51); - } - - if (btheme->tv3d.wire_edit[3] == 0) { - rgba_char_args_set(btheme->tv3d.wire_edit, 0x0, 0x0, 0x0, 255); - } - } /* GL Texture Garbage Collection */ if (U.textimeout == 0) { @@ -2319,9 +1504,6 @@ void init_userdef_do_versions(Main *bmain) if (U.memcachelimit <= 0) { U.memcachelimit = 32; } - if (U.frameserverport == 0) { - U.frameserverport = 8080; - } if (U.dbl_click_time == 0) { U.dbl_click_time = 350; } @@ -2350,178 +1532,12 @@ void init_userdef_do_versions(Main *bmain) U.tweak_threshold = 10; } - if (!USER_VERSION_ATLEAST(265, 1)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* note: the toggle operator for transparent backdrops limits to these spacetypes */ - if (btheme->tnode.button[3] == 255) { - btheme->tv3d.button[3] = 128; - btheme->tnode.button[3] = 128; - btheme->tima.button[3] = 128; - btheme->tseq.button[3] = 128; - btheme->tclip.button[3] = 128; - } - } - } - - /* panel header/backdrop supported locally per editor now */ - if (!USER_VERSION_ATLEAST(265, 2)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - ThemeSpace *ts; - - /* new color, panel backdrop. Not used anywhere yet, until you enable it */ - copy_v3_v3_char(btheme->tui.panel.back, btheme->tbuts.button); - btheme->tui.panel.back[3] = 128; - - for (ts = UI_THEMESPACE_START(btheme); ts != UI_THEMESPACE_END(btheme); ts++) { - ts->panelcolors = btheme->tui.panel; - } - } - } - /* NOTE!! from now on use U.versionfile and U.subversionfile */ #undef USER_VERSION_ATLEAST #define USER_VERSION_ATLEAST(ver, subver) MAIN_VERSION_ATLEAST((&(U)), ver, subver) - if (!USER_VERSION_ATLEAST(266, 0)) { - bTheme *btheme; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* rna definition limits fac to 0.01 */ - if (btheme->tui.menu_shadow_fac == 0.0f) { - btheme->tui.menu_shadow_fac = 0.5f; - btheme->tui.menu_shadow_width = 12; - } - } - } - - if (!USER_VERSION_ATLEAST(265, 4)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->text.syntaxd, 50, 0, 140, 255); /* Decorator/Preprocessor Dir. Blue-purple */ - rgba_char_args_set(btheme->text.syntaxr, 140, 60, 0, 255); /* Reserved Orange */ - rgba_char_args_set(btheme->text.syntaxs, 76, 76, 76, 255); /* Gray (mix between fg/bg) */ - } - } - - if (!USER_VERSION_ATLEAST(265, 6)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - copy_v4_v4_char(btheme->tv3d.gradients.high_gradient, btheme->tv3d.back); - } - } - - if (!USER_VERSION_ATLEAST(265, 9)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_test_set(btheme->tnode.syntaxs, 151, 116, 116, 255); /* matte nodes */ - rgba_char_args_test_set(btheme->tnode.syntaxd, 116, 151, 151, 255); /* distort nodes */ - } - } - - if (!USER_VERSION_ATLEAST(265, 11)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_test_set(btheme->tconsole.console_select, 255, 255, 255, 48); - } - } - - if (!USER_VERSION_ATLEAST(266, 2)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_test_set(btheme->tnode.console_output, 223, 202, 53, 255); /* interface nodes */ - } - } - - if (!USER_VERSION_ATLEAST(268, 3)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_test_set(btheme->tima.uv_others, 96, 96, 96, 255); - rgba_char_args_test_set(btheme->tima.uv_shadow, 112, 112, 112, 255); - } - } - - if (!USER_VERSION_ATLEAST(269, 5)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tima.wire_edit, 192, 192, 192, 255); - rgba_char_args_set(btheme->tima.edge_select, 255, 133, 0, 255); - } - } - - if (!USER_VERSION_ATLEAST(269, 6)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - char r, g, b; - r = btheme->tnode.syntaxn[0]; - g = btheme->tnode.syntaxn[1]; - b = btheme->tnode.syntaxn[2]; - rgba_char_args_test_set(btheme->tnode.nodeclass_output, r, g, b, 255); - r = btheme->tnode.syntaxb[0]; - g = btheme->tnode.syntaxb[1]; - b = btheme->tnode.syntaxb[2]; - rgba_char_args_test_set(btheme->tnode.nodeclass_filter, r, g, b, 255); - rgba_char_args_test_set(btheme->tnode.nodeclass_vector, r, g, b, 255); - rgba_char_args_test_set(btheme->tnode.nodeclass_texture, r, g, b, 255); - rgba_char_args_test_set(btheme->tnode.nodeclass_shader, r, g, b, 255); - rgba_char_args_test_set(btheme->tnode.nodeclass_script, r, g, b, 255); - rgba_char_args_test_set(btheme->tnode.nodeclass_pattern, r, g, b, 255); - rgba_char_args_test_set(btheme->tnode.nodeclass_layout, r, g, b, 255); - } - } - - if (!USER_VERSION_ATLEAST(269, 8)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_test_set(btheme->tinfo.info_selected, 96, 128, 255, 255); - rgba_char_args_test_set(btheme->tinfo.info_selected_text, 255, 255, 255, 255); - rgba_char_args_test_set(btheme->tinfo.info_error, 220, 0, 0, 255); - rgba_char_args_test_set(btheme->tinfo.info_error_text, 0, 0, 0, 255); - rgba_char_args_test_set(btheme->tinfo.info_warning, 220, 128, 96, 255); - rgba_char_args_test_set(btheme->tinfo.info_warning_text, 0, 0, 0, 255); - rgba_char_args_test_set(btheme->tinfo.info_info, 0, 170, 0, 255); - rgba_char_args_test_set(btheme->tinfo.info_info_text, 0, 0, 0, 255); - rgba_char_args_test_set(btheme->tinfo.info_debug, 196, 196, 196, 255); - rgba_char_args_test_set(btheme->tinfo.info_debug_text, 0, 0, 0, 255); - } - } if (!USER_VERSION_ATLEAST(269, 9)) { - bTheme *btheme; - - U.tw_size = U.tw_size * 5.0f; - - /* Action Editor (and NLA Editor) - Keyframe Colors */ - /* Graph Editor - larger vertex size defaults */ - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* Action Editor ................. */ - /* key types */ - rgba_char_args_set(btheme->tact.keytype_keyframe, 232, 232, 232, 255); - rgba_char_args_set(btheme->tact.keytype_keyframe_select, 255, 190, 50, 255); - rgba_char_args_set(btheme->tact.keytype_extreme, 232, 179, 204, 255); - rgba_char_args_set(btheme->tact.keytype_extreme_select, 242, 128, 128, 255); - rgba_char_args_set(btheme->tact.keytype_breakdown, 179, 219, 232, 255); - rgba_char_args_set(btheme->tact.keytype_breakdown_select, 84, 191, 237, 255); - rgba_char_args_set(btheme->tact.keytype_jitter, 148, 229, 117, 255); - rgba_char_args_set(btheme->tact.keytype_jitter_select, 97, 192, 66, 255); - - /* key border */ - rgba_char_args_set(btheme->tact.keyborder, 0, 0, 0, 255); - rgba_char_args_set(btheme->tact.keyborder_select, 0, 0, 0, 255); - - /* NLA ............................ */ - /* key border */ - rgba_char_args_set(btheme->tnla.keyborder, 0, 0, 0, 255); - rgba_char_args_set(btheme->tnla.keyborder_select, 0, 0, 0, 255); - - /* Graph Editor ................... */ - btheme->tipo.vertex_size = 6; - btheme->tipo.handle_vertex_size = 5; - } - /* grease pencil - new layer color */ if (U.gpencil_new_layer_col[3] < 0.1f) { /* defaults to black, but must at least be visible! */ @@ -2529,143 +1545,10 @@ void init_userdef_do_versions(Main *bmain) } } - if (!USER_VERSION_ATLEAST(269, 10)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - ThemeSpace *ts; - - for (ts = UI_THEMESPACE_START(btheme); ts != UI_THEMESPACE_END(btheme); ts++) { - rgba_char_args_set(ts->tab_active, 114, 114, 114, 255); - rgba_char_args_set(ts->tab_inactive, 83, 83, 83, 255); - rgba_char_args_set(ts->tab_back, 64, 64, 64, 255); - rgba_char_args_set(ts->tab_outline, 60, 60, 60, 255); - } - } - } - - if (!USER_VERSION_ATLEAST(271, 0)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tui.wcol_tooltip.text, 255, 255, 255, 255); - } - } - - if (!USER_VERSION_ATLEAST(272, 2)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set_fl(btheme->tv3d.paint_curve_handle, 0.5f, 1.0f, 0.5f, 0.5f); - rgba_char_args_set_fl(btheme->tv3d.paint_curve_pivot, 1.0f, 0.5f, 0.5f, 0.5f); - rgba_char_args_set_fl(btheme->tima.paint_curve_handle, 0.5f, 1.0f, 0.5f, 0.5f); - rgba_char_args_set_fl(btheme->tima.paint_curve_pivot, 1.0f, 0.5f, 0.5f, 0.5f); - rgba_char_args_set(btheme->tnode.syntaxr, 115, 115, 115, 255); - } - } - if (!USER_VERSION_ATLEAST(271, 5)) { - bTheme *btheme; - - struct uiWidgetColors wcol_pie_menu = { - {10, 10, 10, 200}, - {25, 25, 25, 230}, - {140, 140, 140, 255}, - {45, 45, 45, 230}, - - {160, 160, 160, 255}, - {255, 255, 255, 255}, - - 1, - 10, -10 - }; - U.pie_menu_radius = 100; U.pie_menu_threshold = 12; U.pie_animation_timeout = 6; - - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - btheme->tui.wcol_pie_menu = wcol_pie_menu; - - ui_theme_space_init_handles_color(&btheme->tclip); - ui_theme_space_init_handles_color(&btheme->tima); - btheme->tima.handle_vertex_size = 5; - btheme->tclip.handle_vertex_size = 5; - } - } - - if (!USER_VERSION_ATLEAST(271, 6)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* check for (alpha == 0) is safe, then color was never set */ - if (btheme->tv3d.loop_normal[3] == 0) { - rgba_char_args_set(btheme->tv3d.loop_normal, 0xDD, 0x23, 0xDD, 255); - } - } - } - - if (!USER_VERSION_ATLEAST(272, 3)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set_fl(btheme->tui.widget_emboss, 1.0f, 1.0f, 1.0f, 0.02f); - } - } - - if (!USER_VERSION_ATLEAST(273, 1)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* Grease Pencil vertex settings */ - rgba_char_args_set(btheme->tv3d.gp_vertex, 0, 0, 0, 255); - rgba_char_args_set(btheme->tv3d.gp_vertex_select, 255, 133, 0, 255); - btheme->tv3d.gp_vertex_size = 3; - - rgba_char_args_set(btheme->tseq.gp_vertex, 0, 0, 0, 255); - rgba_char_args_set(btheme->tseq.gp_vertex_select, 255, 133, 0, 255); - btheme->tseq.gp_vertex_size = 3; - - rgba_char_args_set(btheme->tima.gp_vertex, 0, 0, 0, 255); - rgba_char_args_set(btheme->tima.gp_vertex_select, 255, 133, 0, 255); - btheme->tima.gp_vertex_size = 3; - - rgba_char_args_set(btheme->tnode.gp_vertex, 0, 0, 0, 255); - rgba_char_args_set(btheme->tnode.gp_vertex_select, 255, 133, 0, 255); - btheme->tnode.gp_vertex_size = 3; - - /* Timeline Keyframe Indicators */ - rgba_char_args_set(btheme->ttime.time_keyframe, 0xDD, 0xD7, 0x00, 0xFF); - rgba_char_args_set(btheme->ttime.time_gp_keyframe, 0xB5, 0xE6, 0x1D, 0xFF); - } - } - - if (!USER_VERSION_ATLEAST(273, 5)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - unsigned char *cp = (unsigned char *)btheme->tv3d.clipping_border_3d; - int c; - copy_v4_v4_char((char *)cp, btheme->tv3d.back); - c = cp[0] - 8; - CLAMP(c, 0, 255); - cp[0] = c; - c = cp[1] - 8; - CLAMP(c, 0, 255); - cp[1] = c; - c = cp[2] - 8; - CLAMP(c, 0, 255); - cp[2] = c; - cp[3] = 255; - } - } - - if (!USER_VERSION_ATLEAST(274, 5)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - copy_v4_v4_char(btheme->tima.metadatatext, btheme->tima.text_hi); - copy_v4_v4_char(btheme->tseq.metadatatext, btheme->tseq.text_hi); - } - } - - if (!USER_VERSION_ATLEAST(275, 1)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - copy_v4_v4_char(btheme->tclip.metadatatext, btheme->tseq.text_hi); - } } if (!USER_VERSION_ATLEAST(275, 2)) { @@ -2676,83 +1559,10 @@ void init_userdef_do_versions(Main *bmain) U.node_margin = 80; } - if (!USER_VERSION_ATLEAST(276, 1)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set_fl(btheme->tima.preview_back, 0.0f, 0.0f, 0.0f, 0.3f); - } - } - - if (!USER_VERSION_ATLEAST(276, 2)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tclip.gp_vertex, 0, 0, 0, 255); - rgba_char_args_set(btheme->tclip.gp_vertex_select, 255, 133, 0, 255); - btheme->tclip.gp_vertex_size = 3; - } - } - - if (!USER_VERSION_ATLEAST(276, 3)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tseq.text_strip, 162, 151, 0, 255); - } - } - - if (!USER_VERSION_ATLEAST(276, 8)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tui.wcol_progress.item, 128, 128, 128, 255); - } - } - - if (!USER_VERSION_ATLEAST(276, 10)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - /* 3dView Keyframe Indicators */ - rgba_char_args_set(btheme->tv3d.time_keyframe, 0xDD, 0xD7, 0x00, 0xFF); - rgba_char_args_set(btheme->tv3d.time_gp_keyframe, 0xB5, 0xE6, 0x1D, 0xFF); - } - } - - if (!USER_VERSION_ATLEAST(277, 0)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (memcmp(btheme->tui.wcol_list_item.item, btheme->tui.wcol_list_item.text_sel, sizeof(char) * 3) == 0) { - copy_v4_v4_char(btheme->tui.wcol_list_item.item, btheme->tui.wcol_text.item); - copy_v4_v4_char(btheme->tui.wcol_list_item.text_sel, btheme->tui.wcol_text.text_sel); - } - } - } - - if (!USER_VERSION_ATLEAST(277, 2)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - if (btheme->tact.keyframe_scale_fac < 0.1f) - btheme->tact.keyframe_scale_fac = 1.0f; - } - } - - if (!USER_VERSION_ATLEAST(278, 2)) { - bTheme *btheme; - for (btheme = U.themes.first; btheme; btheme = btheme->next) { - rgba_char_args_set(btheme->tv3d.vertex_bevel, 0, 165, 255, 255); - rgba_char_args_set(btheme->tv3d.edge_bevel, 0, 165, 255, 255); - } - } - - if (!USER_VERSION_ATLEAST(278, 3)) { - for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) { - /* Keyframe Indicators (were using wrong alpha) */ - btheme->tv3d.time_keyframe[3] = btheme->tv3d.time_gp_keyframe[3] = 255; - btheme->ttime.time_keyframe[3] = btheme->ttime.time_gp_keyframe[3] = 255; - } - } - if (!USER_VERSION_ATLEAST(278, 6)) { /* Clear preference flags for re-use. */ U.flag &= ~( - USER_FLAG_DEPRECATED_1 | USER_FLAG_DEPRECATED_2 | USER_FLAG_DEPRECATED_3 | + USER_FLAG_NUMINPUT_ADVANCED | USER_FLAG_DEPRECATED_2 | USER_FLAG_DEPRECATED_3 | USER_FLAG_DEPRECATED_6 | USER_FLAG_DEPRECATED_7 | USER_FLAG_DEPRECATED_9 | USER_DEVELOPER_UI); U.uiflag &= ~( @@ -2760,13 +1570,20 @@ void init_userdef_do_versions(Main *bmain) U.transopts &= ~( USER_TR_DEPRECATED_2 | USER_TR_DEPRECATED_3 | USER_TR_DEPRECATED_4 | USER_TR_DEPRECATED_6 | USER_TR_DEPRECATED_7); - U.gameflags &= ~( - USER_GL_RENDER_DEPRECATED_0 | USER_GL_RENDER_DEPRECATED_1 | - USER_GL_RENDER_DEPRECATED_3 | USER_GL_RENDER_DEPRECATED_4); U.uiflag |= USER_LOCK_CURSOR_ADJUST; } + + if (!USER_VERSION_ATLEAST(280, 20)) { + U.gpu_viewport_quality = 0.6f; + + /* Reset theme, old themes will not be compatible with minor version updates from now on. */ + for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) { + memcpy(btheme, &U_theme_default, sizeof(*btheme)); + } + } + /** * Include next version bump. */ @@ -2780,11 +1597,8 @@ void init_userdef_do_versions(Main *bmain) if (U.image_draw_method == 0) U.image_draw_method = IMAGE_DRAW_METHOD_2DTEXTURE; - // keep the following until the new audaspace is default to be built with -#ifdef WITH_SYSTEM_AUDASPACE // we default to the first audio device U.audiodevice = 0; -#endif /* Not versioning, just avoid errors. */ #ifndef WITH_CYCLES diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 299001d4e45..3a527712367 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -49,11 +49,12 @@ #include "BKE_screen.h" #include "BKE_global.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" +#include "GPU_state.h" #include "WM_api.h" -#include "BIF_gl.h" - #include "BLF_api.h" #include "ED_screen.h" @@ -147,50 +148,50 @@ static void view2d_masks(View2D *v2d, bool check_scrollers) scroll = view2d_scroll_mapped(v2d->scroll); - /* scrollers shrink mask area, but should be based off regionsize + /* scrollers are based off regionsize * - they can only be on one to two edges of the region they define * - if they overlap, they must not occupy the corners (which are reserved for other widgets) */ if (scroll) { + const int scroll_width = (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) ? + V2D_SCROLL_WIDTH_TEXT : V2D_SCROLL_WIDTH; + const int scroll_height = (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) ? + V2D_SCROLL_HEIGHT_TEXT : V2D_SCROLL_HEIGHT; + /* vertical scroller */ if (scroll & V2D_SCROLL_LEFT) { /* on left-hand edge of region */ v2d->vert = v2d->mask; - v2d->vert.xmax = V2D_SCROLL_WIDTH; - v2d->mask.xmin = v2d->vert.xmax + 1; + v2d->vert.xmax = scroll_width; } else if (scroll & V2D_SCROLL_RIGHT) { /* on right-hand edge of region */ v2d->vert = v2d->mask; v2d->vert.xmax++; /* one pixel extra... was leaving a minor gap... */ - v2d->vert.xmin = v2d->vert.xmax - V2D_SCROLL_WIDTH; - v2d->mask.xmax = v2d->vert.xmin - 1; + v2d->vert.xmin = v2d->vert.xmax - scroll_width; } /* horizontal scroller */ if (scroll & (V2D_SCROLL_BOTTOM)) { /* on bottom edge of region */ v2d->hor = v2d->mask; - v2d->hor.ymax = V2D_SCROLL_HEIGHT; - v2d->mask.ymin = v2d->hor.ymax + 1; + v2d->hor.ymax = scroll_height; } else if (scroll & V2D_SCROLL_TOP) { /* on upper edge of region */ v2d->hor = v2d->mask; - v2d->hor.ymin = v2d->hor.ymax - V2D_SCROLL_HEIGHT; - v2d->mask.ymax = v2d->hor.ymin - 1; + v2d->hor.ymin = v2d->hor.ymax - scroll_height; } /* adjust vertical scroller if there's a horizontal scroller, to leave corner free */ if (scroll & V2D_SCROLL_VERTICAL) { - /* just set y min/max for vertical scroller to y min/max of mask as appropriate */ if (scroll & (V2D_SCROLL_BOTTOM)) { /* on bottom edge of region */ - v2d->vert.ymin = v2d->mask.ymin; + v2d->vert.ymin = v2d->hor.ymax; } else if (scroll & V2D_SCROLL_TOP) { /* on upper edge of region */ - v2d->vert.ymax = v2d->mask.ymax; + v2d->vert.ymax = v2d->hor.ymin; } } } @@ -357,6 +358,11 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) /* set masks (always do), but leave scroller scheck to totrect_set */ view2d_masks(v2d, 0); + if (do_init) { + /* Visible by default. */ + v2d->alpha_hor = v2d->alpha_vert = 255; + } + /* set 'tot' rect before setting cur? */ /* XXX confusing stuff here still - I made this function not check scroller hide - that happens in totrect_set */ if (tot_changed) @@ -813,7 +819,7 @@ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag) } /* region possibly changed, so refresh */ - ED_region_tag_redraw(ar); + ED_region_tag_redraw_no_rebuild(ar); } } } @@ -839,7 +845,7 @@ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag) } /* region possibly changed, so refresh */ - ED_region_tag_redraw(ar); + ED_region_tag_redraw_no_rebuild(ar); } } } @@ -1107,9 +1113,6 @@ void UI_view2d_view_ortho(View2D *v2d) /* set matrix on all appropriate axes */ wmOrtho2(curmasked.xmin, curmasked.xmax, curmasked.ymin, curmasked.ymax); - - /* XXX is this necessary? */ - glLoadIdentity(); } /** @@ -1137,9 +1140,6 @@ void UI_view2d_view_orthoSpecial(ARegion *ar, View2D *v2d, const bool xaxis) wmOrtho2(curmasked.xmin - xofs, curmasked.xmax - xofs, -yofs, ar->winy - yofs); else wmOrtho2(-xofs, ar->winx - xofs, curmasked.ymin - yofs, curmasked.ymax - yofs); - - /* XXX is this necessary? */ - glLoadIdentity(); } @@ -1151,7 +1151,7 @@ void UI_view2d_view_restore(const bContext *C) int height = BLI_rcti_size_y(&ar->winrct) + 1; wmOrtho2(0.0f, (float)width, 0.0f, (float)height); - glLoadIdentity(); + GPU_matrix_identity_set(); // ED_region_pixelspace(CTX_wm_region(C)); } @@ -1305,12 +1305,45 @@ void UI_view2d_grid_draw(View2D *v2d, View2DGrid *grid, int flag) { float vec1[2], vec2[2]; int a, step; + int vertical_minor_step = (BLI_rcti_size_x(&v2d->mask) + 1) / (U.v2d_min_gridsize * UI_DPI_FAC), + horizontal_major_step = (BLI_rcti_size_y(&v2d->mask) + 1) / (U.v2d_min_gridsize * UI_DPI_FAC); + unsigned char grid_line_color[3]; /* check for grid first, as it may not exist */ if (grid == NULL) return; - glBegin(GL_LINES); + /* Count the needed vertices for the gridlines */ + unsigned vertex_count = 0; + if (flag & V2D_VERTICAL_LINES) { + /* vertical lines */ + vertex_count += 2 * vertical_minor_step; /* minor gridlines */ + vertex_count += 2 * (vertical_minor_step + 2); /* major gridlines */ + } + if (flag & V2D_HORIZONTAL_LINES) { + /* horizontal lines */ + vertex_count += 2 * (horizontal_major_step + 1); /* major gridlines */ + + /* fine lines */ + if (flag & V2D_HORIZONTAL_FINELINES) + vertex_count += 2 * (horizontal_major_step + 1); + } + /* axes */ + if (flag & V2D_HORIZONTAL_AXIS) + vertex_count += 2; + if (flag & V2D_VERTICAL_AXIS) + vertex_count += 2; + + /* If there is nothing to render, exit early */ + if (vertex_count == 0) + return; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); + + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(GPU_PRIM_LINES, vertex_count); /* vertical lines */ if (flag & V2D_VERTICAL_LINES) { @@ -1320,24 +1353,31 @@ void UI_view2d_grid_draw(View2D *v2d, View2DGrid *grid, int flag) vec2[1] = v2d->cur.ymax; /* minor gridlines */ - step = (BLI_rcti_size_x(&v2d->mask) + 1) / (U.v2d_min_gridsize * UI_DPI_FAC); - UI_ThemeColor(TH_GRID); + step = vertical_minor_step; + if (step != 0) { + UI_GetThemeColor3ubv(TH_GRID, grid_line_color); - for (a = 0; a < step; a++) { - glVertex2fv(vec1); - glVertex2fv(vec2); + for (a = 0; a < step; a++) { + immSkipAttrib(color); + immVertex2fv(pos, vec1); + immAttrib3ubv(color, grid_line_color); + immVertex2fv(pos, vec2); - vec2[0] = vec1[0] += grid->dx; + vec2[0] = vec1[0] += grid->dx; + } } /* major gridlines */ vec2[0] = vec1[0] -= 0.5f * grid->dx; - UI_ThemeColorShade(TH_GRID, 16); + + UI_GetThemeColorShade3ubv(TH_GRID, 16, grid_line_color); step++; for (a = 0; a <= step; a++) { - glVertex2fv(vec1); - glVertex2fv(vec2); + immSkipAttrib(color); + immVertex2fv(pos, vec1); + immAttrib3ubv(color, grid_line_color); + immVertex2fv(pos, vec2); vec2[0] = vec1[0] -= grid->dx; } @@ -1350,12 +1390,15 @@ void UI_view2d_grid_draw(View2D *v2d, View2DGrid *grid, int flag) vec1[0] = grid->startx; vec2[0] = v2d->cur.xmax; - step = (BLI_rcti_size_y(&v2d->mask) + 1) / (U.v2d_min_gridsize * UI_DPI_FAC); + step = horizontal_major_step; + + UI_GetThemeColor3ubv(TH_GRID, grid_line_color); - UI_ThemeColor(TH_GRID); for (a = 0; a <= step; a++) { - glVertex2fv(vec1); - glVertex2fv(vec2); + immSkipAttrib(color); + immVertex2fv(pos, vec1); + immAttrib3ubv(color, grid_line_color); + immVertex2fv(pos, vec2); vec2[1] = vec1[1] += grid->dy; } @@ -1365,10 +1408,12 @@ void UI_view2d_grid_draw(View2D *v2d, View2DGrid *grid, int flag) step++; if (flag & V2D_HORIZONTAL_FINELINES) { - UI_ThemeColorShade(TH_GRID, 16); + UI_GetThemeColorShade3ubv(TH_GRID, 16, grid_line_color); for (a = 0; a < step; a++) { - glVertex2fv(vec1); - glVertex2fv(vec2); + immSkipAttrib(color); + immVertex2fv(pos, vec1); + immAttrib3ubv(color, grid_line_color); + immVertex2fv(pos, vec2); vec2[1] = vec1[1] -= grid->dy; } @@ -1376,7 +1421,7 @@ void UI_view2d_grid_draw(View2D *v2d, View2DGrid *grid, int flag) } /* Axes are drawn as darker lines */ - UI_ThemeColorShade(TH_GRID, -50); + UI_GetThemeColorShade3ubv(TH_GRID, -50, grid_line_color); /* horizontal axis */ if (flag & V2D_HORIZONTAL_AXIS) { @@ -1384,8 +1429,10 @@ void UI_view2d_grid_draw(View2D *v2d, View2DGrid *grid, int flag) vec2[0] = v2d->cur.xmax; vec1[1] = vec2[1] = 0.0f; - glVertex2fv(vec1); - glVertex2fv(vec2); + immSkipAttrib(color); + immVertex2fv(pos, vec1); + immAttrib3ubv(color, grid_line_color); + immVertex2fv(pos, vec2); } /* vertical axis */ @@ -1394,91 +1441,157 @@ void UI_view2d_grid_draw(View2D *v2d, View2DGrid *grid, int flag) vec2[1] = v2d->cur.ymax; vec1[0] = vec2[0] = 0.0f; - glVertex2fv(vec1); - glVertex2fv(vec2); + immSkipAttrib(color); + immVertex2fv(pos, vec1); + immAttrib3ubv(color, grid_line_color); + immVertex2fv(pos, vec2); } - glEnd(); + immEnd(); + immUnbindProgram(); } /* Draw a constant grid in given 2d-region */ void UI_view2d_constant_grid_draw(View2D *v2d, float step) { - float start; + float start_x, start_y; + int count_x, count_y; - UI_ThemeColorShade(TH_BACK, -10); + start_x = v2d->cur.xmin; + if (start_x < 0.0) + start_x += -(float)fmod(v2d->cur.xmin, step); + else + start_x += (step - (float)fmod(v2d->cur.xmin, step)); - start = v2d->cur.xmin - (float)fmod(v2d->cur.xmin, step); + if (start_x > v2d->cur.xmax) + count_x = 0; + else + count_x = (v2d->cur.xmax - start_x) / step + 1; - glBegin(GL_LINES); - for (; start < v2d->cur.xmax; start += step) { - glVertex2f(start, v2d->cur.ymin); - glVertex2f(start, v2d->cur.ymax); - } + start_y = v2d->cur.ymin; + if (start_y < 0.0) + start_y += -(float)fmod(v2d->cur.ymin, step); + else + start_y += (step - (float)fabs(fmod(v2d->cur.ymin, step))); - start = v2d->cur.ymin - (float)fmod(v2d->cur.ymin, step); - for (; start < v2d->cur.ymax; start += step) { - glVertex2f(v2d->cur.xmin, start); - glVertex2f(v2d->cur.xmax, start); - } + if (start_y > v2d->cur.ymax) + count_y = 0; + else + count_y = (v2d->cur.ymax - start_y) / step + 1; - /* X and Y axis */ - UI_ThemeColorShade(TH_BACK, -18); - glVertex2f(0.0f, v2d->cur.ymin); - glVertex2f(0.0f, v2d->cur.ymax); - glVertex2f(v2d->cur.xmin, 0.0f); - glVertex2f(v2d->cur.xmax, 0.0f); + if (count_x > 0 || count_y > 0) { + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + float theme_color[3]; + + UI_GetThemeColorShade3fv(TH_BACK, -10, theme_color); + + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(GPU_PRIM_LINES, count_x * 2 + count_y * 2 + 4); + + immAttrib3fv(color, theme_color); + for (int i = 0; i < count_x ; start_x += step, i++) { + immVertex2f(pos, start_x, v2d->cur.ymin); + immVertex2f(pos, start_x, v2d->cur.ymax); + } + + for (int i = 0; i < count_y; start_y += step, i++) { + immVertex2f(pos, v2d->cur.xmin, start_y); + immVertex2f(pos, v2d->cur.xmax, start_y); + } + + /* X and Y axis */ + UI_GetThemeColorShade3fv(TH_BACK, -18, theme_color); - glEnd(); + immAttrib3fv(color, theme_color); + immVertex2f(pos, 0.0f, v2d->cur.ymin); + immVertex2f(pos, 0.0f, v2d->cur.ymax); + immVertex2f(pos, v2d->cur.xmin, 0.0f); + immVertex2f(pos, v2d->cur.xmax, 0.0f); + + immEnd(); + immUnbindProgram(); + } } /* Draw a multi-level grid in given 2d-region */ void UI_view2d_multi_grid_draw(View2D *v2d, int colorid, float step, int level_size, int totlevels) { + /* Exit if there is nothing to draw */ + if (totlevels == 0) + return; + int offset = -10; float lstep = step; - int level; + unsigned char grid_line_color[3]; - glLineWidth(1.0f); - for (level = 0; level < totlevels; ++level) { - int i; - float start; + /* Make an estimate of at least how many vertices will be needed */ + unsigned vertex_count = 4; + vertex_count += 2 * ((int)((v2d->cur.xmax - v2d->cur.xmin) / lstep) + 1); + vertex_count += 2 * ((int)((v2d->cur.ymax - v2d->cur.ymin) / lstep) + 1); - UI_ThemeColorShade(colorid, offset); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); - i = (v2d->cur.xmin >= 0.0f ? -(int)(-v2d->cur.xmin / lstep) : (int)(v2d->cur.xmin / lstep)); - start = i * lstep; + GPU_line_width(1.0f); + + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBeginAtMost(GPU_PRIM_LINES, vertex_count); + + for (int level = 0; level < totlevels; ++level) { + UI_GetThemeColorShade3ubv(colorid, offset, grid_line_color); + + int i = (int)(v2d->cur.xmin / lstep); + if (v2d->cur.xmin > 0.0f) + i++; + float start = i * lstep; - glBegin(GL_LINES); for (; start < v2d->cur.xmax; start += lstep, ++i) { if (i == 0 || (level < totlevels - 1 && i % level_size == 0)) continue; - glVertex2f(start, v2d->cur.ymin); - glVertex2f(start, v2d->cur.ymax); + + immSkipAttrib(color); + immVertex2f(pos, start, v2d->cur.ymin); + immAttrib3ubv(color, grid_line_color); + immVertex2f(pos, start, v2d->cur.ymax); } - i = (v2d->cur.ymin >= 0.0f ? -(int)(-v2d->cur.ymin / lstep) : (int)(v2d->cur.ymin / lstep)); + i = (int)(v2d->cur.ymin / lstep); + if (v2d->cur.ymin > 0.0f) + i++; start = i * lstep; for (; start < v2d->cur.ymax; start += lstep, ++i) { if (i == 0 || (level < totlevels - 1 && i % level_size == 0)) continue; - glVertex2f(v2d->cur.xmin, start); - glVertex2f(v2d->cur.xmax, start); - } - - /* X and Y axis */ - UI_ThemeColorShade(colorid, offset - 8); - glVertex2f(0.0f, v2d->cur.ymin); - glVertex2f(0.0f, v2d->cur.ymax); - glVertex2f(v2d->cur.xmin, 0.0f); - glVertex2f(v2d->cur.xmax, 0.0f); - glEnd(); + immSkipAttrib(color); + immVertex2f(pos, v2d->cur.xmin, start); + immAttrib3ubv(color, grid_line_color); + immVertex2f(pos, v2d->cur.xmax, start); + } lstep *= level_size; offset -= 6; } + + /* X and Y axis */ + UI_GetThemeColorShade3ubv(colorid, -18 + ((totlevels - 1) * -6), grid_line_color); + + immSkipAttrib(color); + immVertex2f(pos, 0.0f, v2d->cur.ymin); + immAttrib3ubv(color, grid_line_color); + immVertex2f(pos, 0.0f, v2d->cur.ymax); + + immSkipAttrib(color); + immVertex2f(pos, v2d->cur.xmin, 0.0f); + immAttrib3ubv(color, grid_line_color); + immVertex2f(pos, v2d->cur.xmax, 0.0f); + + immEnd(); + immUnbindProgram(); } /* the price we pay for not exposting structs :( */ @@ -1534,11 +1647,14 @@ View2DScrollers *UI_view2d_scrollers_calc( /* scrollers is allocated here... */ scrollers = MEM_callocN(sizeof(View2DScrollers), "View2DScrollers"); + /* Always update before drawing (for dynamically sized scrollers). */ + view2d_masks(v2d, false); + vert = v2d->vert; hor = v2d->hor; /* slider rects need to be smaller than region */ - smaller = (int)(0.2f * U.widget_unit); + smaller = (int)(0.1f * U.widget_unit); hor.xmin += smaller; hor.xmax -= smaller; if (scroll & V2D_SCROLL_BOTTOM) @@ -1698,9 +1814,15 @@ static void scroll_printstr(Scene *scene, float x, float y, float val, int power /* Draw scrollbars in the given 2d-region */ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *vs) { + bTheme *btheme = UI_GetTheme(); Scene *scene = CTX_data_scene(C); rcti vert, hor; - int scroll = view2d_scroll_mapped(v2d->scroll); + const int scroll = view2d_scroll_mapped(v2d->scroll); + const char emboss_alpha = btheme->tui.widget_emboss[3]; + unsigned char scrollers_back_color[4]; + + /* Color for scrollbar backs */ + UI_GetThemeColor4ubv(TH_BACK, scrollers_back_color); /* make copies of rects for less typing */ vert = vs->vert; @@ -1708,11 +1830,10 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v /* horizontal scrollbar */ if (scroll & V2D_SCROLL_HORIZONTAL) { - bTheme *btheme = UI_GetTheme(); uiWidgetColors wcol = btheme->tui.wcol_scroll; + const float alpha_fac = v2d->alpha_hor / 255.0f; rcti slider; int state; - unsigned char col[4]; slider.xmin = vs->hor_min; slider.xmax = vs->hor_max; @@ -1721,6 +1842,11 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v state = (v2d->scroll_ui & V2D_SCROLL_H_ACTIVE) ? UI_SCROLL_PRESSED : 0; + wcol.inner[3] *= alpha_fac; + wcol.item[3] *= alpha_fac; + wcol.outline[3] *= alpha_fac; + btheme->tui.widget_emboss[3] *= alpha_fac; /* will be reset later */ + /* show zoom handles if: * - zooming on x-axis is allowed (no scroll otherwise) * - slider bubble is large enough (no overdraw confusion) @@ -1735,17 +1861,11 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v state |= UI_SCROLL_ARROWS; } - /* clean rect behind slider, but not with transparent background */ - UI_GetThemeColor4ubv(TH_BACK, col); - if (col[3] == 255) { - glColor3ub(col[0], col[1], col[2]); - glRecti(v2d->hor.xmin, v2d->hor.ymin, v2d->hor.xmax, v2d->hor.ymax); - } - UI_draw_widget_scroll(&wcol, &hor, &slider, state); /* scale indicators */ if ((scroll & V2D_SCROLL_SCALE_HORIZONTAL) && (vs->grid)) { + const int font_id = BLF_default(); View2DGrid *grid = vs->grid; float fac, dfac, fac2, val; @@ -1760,7 +1880,7 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v dfac = dfac * BLI_rcti_size_x(&hor); /* set starting value, and text color */ - UI_ThemeColor(TH_TEXT); + UI_FontThemeColor(font_id, TH_TEXT); val = grid->startx; /* if we're clamping to whole numbers only, make sure entries won't be repeated */ @@ -1777,6 +1897,8 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v if (dfac > 0.0f) { float h = 0.1f * UI_UNIT_Y + (float)(hor.ymin); + BLF_batch_draw_begin(); + for (; fac < hor.xmax - 0.5f * U.widget_unit; fac += dfac, val += grid->dx) { /* make prints look nicer for scrollers */ @@ -1803,17 +1925,18 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v break; } } + + BLF_batch_draw_end(); } } } /* vertical scrollbar */ if (scroll & V2D_SCROLL_VERTICAL) { - bTheme *btheme = UI_GetTheme(); uiWidgetColors wcol = btheme->tui.wcol_scroll; rcti slider; + const float alpha_fac = v2d->alpha_vert / 255.0f; int state; - unsigned char col[4]; slider.xmin = vert.xmin; slider.xmax = vert.xmax; @@ -1822,6 +1945,11 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v state = (v2d->scroll_ui & V2D_SCROLL_V_ACTIVE) ? UI_SCROLL_PRESSED : 0; + wcol.inner[3] *= alpha_fac; + wcol.item[3] *= alpha_fac; + wcol.outline[3] *= alpha_fac; + btheme->tui.widget_emboss[3] *= alpha_fac; /* will be reset later */ + /* show zoom handles if: * - zooming on y-axis is allowed (no scroll otherwise) * - slider bubble is large enough (no overdraw confusion) @@ -1836,13 +1964,6 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v state |= UI_SCROLL_ARROWS; } - /* clean rect behind slider, but not with transparent background */ - UI_GetThemeColor4ubv(TH_BACK, col); - if (col[3] == 255) { - glColor3ub(col[0], col[1], col[2]); - glRecti(v2d->vert.xmin, v2d->vert.ymin, v2d->vert.xmax, v2d->vert.ymax); - } - UI_draw_widget_scroll(&wcol, &vert, &slider, state); @@ -1864,7 +1985,8 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v dfac = dfac * BLI_rcti_size_y(&vert); /* set starting value, and text color */ - UI_ThemeColor(TH_TEXT); + const int font_id = BLF_default(); + UI_FontThemeColor(font_id, TH_TEXT); val = grid->starty; /* if vertical clamping (to whole numbers) is used (i.e. in Sequencer), apply correction */ @@ -1873,9 +1995,8 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v /* draw vertical steps */ if (dfac > 0.0f) { - - BLF_rotation_default(M_PI_2); - BLF_enable_default(BLF_ROTATION); + BLF_rotation(font_id, M_PI_2); + BLF_enable(font_id, BLF_ROTATION); for (; fac < vert.ymax - 10; fac += dfac, val += grid->dy) { @@ -1886,11 +2007,13 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v scroll_printstr(scene, (float)(vert.xmax) - 2.0f, fac, val, grid->powery, vs->yunits, 'v'); } - BLF_disable_default(BLF_ROTATION); + BLF_disable(font_id, BLF_ROTATION); } } } + /* Was changed above, so reset. */ + btheme->tui.widget_emboss[3] = emboss_alpha; } /* free temporary memory used for drawing scrollers */ @@ -2299,9 +2422,8 @@ void UI_view2d_offset(struct View2D *v2d, float xfac, float yfac) * - 'v' = in vertical scroller. * - 0 = not in scroller. */ -short UI_view2d_mouse_in_scrollers(const bContext *C, View2D *v2d, int x, int y) +short UI_view2d_mouse_in_scrollers(const ARegion *ar, View2D *v2d, int x, int y) { - ARegion *ar = CTX_wm_region(C); int co[2]; int scroll = view2d_scroll_mapped(v2d->scroll); @@ -2410,7 +2532,8 @@ void UI_view2d_text_cache_draw(ARegion *ar) int col_pack_prev = 0; /* investigate using BLF_ascender() */ - const float default_height = g_v2d_strings ? BLF_height_default("28", 3) : 0.0f; + const int font_id = BLF_default(); + const float default_height = g_v2d_strings ? BLF_height(font_id, "28", 3) : 0.0f; wmOrtho2_region_pixelspace(ar); @@ -2421,7 +2544,7 @@ void UI_view2d_text_cache_draw(ARegion *ar) if (yofs < 1) yofs = 1; if (col_pack_prev != v2s->col.pack) { - glColor3ubv(v2s->col.ub); + BLF_color3ubv(font_id, v2s->col.ub); col_pack_prev = v2s->col.pack; } @@ -2430,12 +2553,11 @@ void UI_view2d_text_cache_draw(ARegion *ar) (float)(v2s->mval[0] + xofs), (float)(v2s->mval[1] + yofs), 0.0, v2s->str, BLF_DRAW_STR_DUMMY_MAX); else { - BLF_clipping_default(v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4); - BLF_enable_default(BLF_CLIPPING); - BLF_draw_default( - v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, - v2s->str, BLF_DRAW_STR_DUMMY_MAX); - BLF_disable_default(BLF_CLIPPING); + BLF_enable(font_id, BLF_CLIPPING); + BLF_clipping(font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4); + BLF_draw_default(v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, + v2s->str, BLF_DRAW_STR_DUMMY_MAX); + BLF_disable(font_id, BLF_CLIPPING); } } g_v2d_strings = NULL; @@ -2444,11 +2566,6 @@ void UI_view2d_text_cache_draw(ARegion *ar) BLI_memarena_free(g_v2d_strings_arena); g_v2d_strings_arena = NULL; } - - // glMatrixMode(GL_PROJECTION); - // glPopMatrix(); - // glMatrixMode(GL_MODELVIEW); - // glPopMatrix(); } diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 3cad2167cfa..dfc401c1635 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -33,6 +33,7 @@ #include "MEM_guardedalloc.h" #include "DNA_userdef_types.h" +#include "DNA_windowmanager_types.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" @@ -169,18 +170,13 @@ static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float /* validate that view is in valid configuration after this operation */ UI_view2d_curRect_validate(v2d); + /* don't rebuild full tree in outliner, since we're just changing our view */ + ED_region_tag_redraw_no_rebuild(vpd->ar); + /* request updates to be done... */ - ED_region_tag_redraw(vpd->ar); WM_event_add_mousemove(C); UI_view2d_sync(vpd->sc, vpd->sa, v2d, V2D_LOCK_COPY); - - /* exceptions */ - if (vpd->sa->spacetype == SPACE_OUTLINER) { - /* don't rebuild full tree, since we're just changing our view */ - SpaceOops *soops = vpd->sa->spacedata.first; - soops->storeflag |= SO_TREESTORE_REDRAW; - } } static void view_pan_apply(bContext *C, wmOperator *op) @@ -642,6 +638,7 @@ static void view_zoomstep_apply_ex( View2D *v2d = &ar->v2d; const rctf cur_old = v2d->cur; float dx, dy; + const int snap_test = ED_region_snap_size_test(ar); /* calculate amount to move view by, ensuring symmetry so the * old zoom level is restored after zooming back the same amount @@ -726,8 +723,14 @@ static void view_zoomstep_apply_ex( /* validate that view is in valid configuration after this operation */ UI_view2d_curRect_validate(v2d); + if (ED_region_snap_size_apply(ar, snap_test)) { + ScrArea *sa = CTX_wm_area(C); + ED_area_tag_redraw(sa); + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + } + /* request updates to be done... */ - ED_region_tag_redraw(vzd->ar); + ED_region_tag_redraw_no_rebuild(vzd->ar); UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); } @@ -902,6 +905,7 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) v2dViewZoomData *vzd = op->customdata; View2D *v2d = vzd->v2d; float dx, dy; + const int snap_test = ED_region_snap_size_test(vzd->ar); /* get amount to move view by */ dx = RNA_float_get(op->ptr, "deltax"); @@ -966,8 +970,14 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) /* validate that view is in valid configuration after this operation */ UI_view2d_curRect_validate(v2d); + if (ED_region_snap_size_apply(vzd->ar, snap_test)) { + ScrArea *sa = CTX_wm_area(C); + ED_area_tag_redraw(sa); + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + } + /* request updates to be done... */ - ED_region_tag_redraw(vzd->ar); + ED_region_tag_redraw_no_rebuild(vzd->ar); UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); } @@ -1488,7 +1498,7 @@ void UI_view2d_smooth_view( v2d->cur = sms.new_cur; UI_view2d_curRect_validate(v2d); - ED_region_tag_redraw(ar); + ED_region_tag_redraw_no_rebuild(ar); UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); } } @@ -1532,7 +1542,7 @@ static int view2d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w UI_view2d_curRect_validate(v2d); UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); - ED_region_tag_redraw(ar); + ED_region_tag_redraw_no_rebuild(ar); if (v2d->sms == NULL) { UI_view2d_zoom_cache_reset(); @@ -1668,6 +1678,21 @@ static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_ return SCROLLHANDLE_BAR; } +static bool scroller_activate_poll(bContext *C) +{ + if (!view2d_poll(C)) { + return false; + } + + wmWindow *win = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + View2D *v2d = &ar->v2d; + wmEvent *event = win->eventstate; + + /* check if mouse in scrollbars, if they're enabled */ + return (UI_view2d_mouse_in_scrollers(ar, v2d, event->x, event->y) != 0); +} + /* initialize customdata for scroller manipulation operator */ static void scroller_activate_init(bContext *C, wmOperator *op, const wmEvent *event, short in_scroller) { @@ -1746,7 +1771,7 @@ static void scroller_activate_init(bContext *C, wmOperator *op, const wmEvent *e } UI_view2d_scrollers_free(scrollers); - ED_region_tag_redraw(ar); + ED_region_tag_redraw_no_rebuild(ar); } /* cleanup temp customdata */ @@ -1760,7 +1785,7 @@ static void scroller_activate_exit(bContext *C, wmOperator *op) MEM_freeN(op->customdata); op->customdata = NULL; - ED_region_tag_redraw(CTX_wm_region(C)); + ED_region_tag_redraw_no_rebuild(CTX_wm_region(C)); } } @@ -1822,7 +1847,7 @@ static void scroller_activate_apply(bContext *C, wmOperator *op) UI_view2d_curRect_validate(v2d); /* request updates to be done... */ - ED_region_tag_redraw(vsm->ar); + ED_region_tag_redraw_no_rebuild(vsm->ar); UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); } @@ -1906,7 +1931,7 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent * short in_scroller = 0; /* check if mouse in scrollbars, if they're enabled */ - in_scroller = UI_view2d_mouse_in_scrollers(C, v2d, event->x, event->y); + in_scroller = UI_view2d_mouse_in_scrollers(ar, v2d, event->x, event->y); /* if in a scroller, init customdata then set modal handler which will catch mousedown to start doing useful stuff */ if (in_scroller) { @@ -2000,7 +2025,7 @@ static void VIEW2D_OT_scroller_activate(wmOperatorType *ot) ot->modal = scroller_activate_modal; ot->cancel = scroller_activate_cancel; - ot->poll = view2d_poll; + ot->poll = scroller_activate_poll; } /* ********************************************************* */ @@ -2012,6 +2037,7 @@ static int reset_exec(bContext *C, wmOperator *UNUSED(op)) ARegion *ar = CTX_wm_region(C); View2D *v2d = &ar->v2d; int winx, winy; + const int snap_test = ED_region_snap_size_test(ar); /* zoom 1.0 */ winx = (float)(BLI_rcti_size_x(&v2d->mask) + 1); @@ -2046,6 +2072,12 @@ static int reset_exec(bContext *C, wmOperator *UNUSED(op)) /* validate that view is in valid configuration after this operation */ UI_view2d_curRect_validate(v2d); + if (ED_region_snap_size_apply(ar, snap_test)) { + ScrArea *sa = CTX_wm_area(C); + ED_area_tag_redraw(sa); + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + } + /* request updates to be done... */ ED_region_tag_redraw(ar); UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); |