diff options
Diffstat (limited to 'source/blender/editors/interface')
12 files changed, 371 insertions, 144 deletions
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_depth.c b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c index 3fb5a74944b..c6d950112b9 100644 --- a/source/blender/editors/interface/eyedroppers/eyedropper_depth.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c @@ -162,7 +162,7 @@ static void depthdropper_depth_sample_pt(bContext *C, View3D *v3d = area->spacedata.first; RegionView3D *rv3d = region->regiondata; /* weak, we could pass in some reference point */ - const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3]; + const float *view_co = v3d->camera ? v3d->camera->object_to_world[3] : rv3d->viewinv[3]; const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin}; copy_v2_v2_int(ddr->name_pos, mval); diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 422fc34aa50..331d1581db5 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -3474,6 +3474,7 @@ void UI_block_free(const bContext *C, uiBlock *block) BLI_freelistN(&block->saferct); BLI_freelistN(&block->color_pickers.list); + BLI_freelistN(&block->dynamic_listeners); ui_block_free_button_groups(block); ui_block_free_views(block); @@ -3481,6 +3482,20 @@ void UI_block_free(const bContext *C, uiBlock *block) MEM_freeN(block); } +void UI_block_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) +{ + /* Don't need to let invisible blocks (old blocks from previous redraw) listen. */ + if (!block->active) { + return; + } + + LISTBASE_FOREACH (uiBlockDynamicListener *, listener, &block->dynamic_listeners) { + listener->listener_func(listener_params); + } + + ui_block_views_listen(block, listener_params); +} + void UI_blocklist_update_window_matrix(const bContext *C, const ListBase *lb) { ARegion *region = CTX_wm_region(C); @@ -6452,6 +6467,11 @@ void UI_but_hint_drawstr_set(uiBut *but, const char *string) ui_but_add_shortcut(but, string, false); } +void UI_but_icon_indicator_number_set(uiBut *but, const int indicator_number) +{ + UI_icon_text_overlay_init_from_count(&but->icon_overlay_text, indicator_number); +} + void UI_but_node_link_set(uiBut *but, bNodeSocket *socket, const float draw_color[4]) { but->flag |= UI_BUT_NODE_LINK; diff --git a/source/blender/editors/interface/interface_context_path.cc b/source/blender/editors/interface/interface_context_path.cc index e8f552e26a3..74a07d6edc8 100644 --- a/source/blender/editors/interface/interface_context_path.cc +++ b/source/blender/editors/interface/interface_context_path.cc @@ -17,6 +17,8 @@ #include "UI_interface.hh" #include "UI_resources.h" +#include "RNA_prototypes.h" + #include "WM_api.h" namespace blender::ui { @@ -41,7 +43,13 @@ void context_path_add_generic(Vector<ContextPathItem> &path, static_cast<BIFIconID>(RNA_struct_ui_icon(rna_ptr.type)) : icon_override; - path.append({name, int(icon)}); + if (&rna_type == &RNA_NodeTree) { + ID *id = (ID *)ptr; + path.append({name, int(icon), ID_REAL_USERS(id)}); + } + else { + path.append({name, int(icon), 1}); + } } /* -------------------------------------------------------------------- */ @@ -60,7 +68,9 @@ void template_breadcrumbs(uiLayout &layout, Span<ContextPathItem> context_path) if (i > 0) { uiItemL(sub_row, "", ICON_RIGHTARROW_THIN); } - uiItemL(sub_row, context_path[i].name.c_str(), context_path[i].icon); + uiBut *but = uiItemL_ex( + sub_row, context_path[i].name.c_str(), context_path[i].icon, false, false); + UI_but_icon_indicator_number_set(but, context_path[i].icon_indicator_number); } } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 34f33b71fb8..ac16d41dabe 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3519,7 +3519,7 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data) { - /* label and roundbox can overlap real buttons (backdrops...) */ + /* Label and round-box can overlap real buttons (backdrops...). */ if (ELEM(actbut->type, UI_BTYPE_LABEL, UI_BTYPE_SEPR, @@ -3551,7 +3551,7 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data) { - /* label and roundbox can overlap real buttons (backdrops...) */ + /* Label and round-box can overlap real buttons (backdrops...). */ if (ELEM(actbut->type, UI_BTYPE_LABEL, UI_BTYPE_SEPR, @@ -3894,7 +3894,7 @@ static void ui_do_but_textedit( } if (event->type == WM_IME_COMPOSITE_EVENT && ime_data->result_len) { if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) && - strcmp(ime_data->str_result, "\xE3\x80\x82") == 0) { + STREQ(ime_data->str_result, "\xE3\x80\x82")) { /* Convert Ideographic Full Stop (U+3002) to decimal point when entering numbers. */ ui_textedit_insert_ascii(but, data, '.'); } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index c080dce0f08..9a4f98ebcd6 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -415,8 +415,15 @@ static void vicon_collection_color_draw( const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w; - UI_icon_draw_ex( - x, y, ICON_OUTLINER_COLLECTION, aspect, 1.0f, 0.0f, collection_color->color, true); + UI_icon_draw_ex(x, + y, + ICON_OUTLINER_COLLECTION, + aspect, + 1.0f, + 0.0f, + collection_color->color, + true, + UI_NO_ICON_OVERLAY_TEXT); } # define DEF_ICON_COLLECTION_COLOR_DRAW(index, color) \ @@ -444,7 +451,8 @@ static void vicon_strip_color_draw( const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w; - UI_icon_draw_ex(x, y, ICON_SNAP_FACE, aspect, 1.0f, 0.0f, strip_color->color, true); + UI_icon_draw_ex( + x, y, ICON_SNAP_FACE, aspect, 1.0f, 0.0f, strip_color->color, true, UI_NO_ICON_OVERLAY_TEXT); } # define DEF_ICON_STRIP_COLOR_DRAW(index, color) \ @@ -472,8 +480,15 @@ static void vicon_strip_color_draw_library_data_indirect( { const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w; - UI_icon_draw_ex( - x, y, ICON_LIBRARY_DATA_DIRECT, aspect, ICON_INDIRECT_DATA_ALPHA * alpha, 0.0f, NULL, false); + UI_icon_draw_ex(x, + y, + ICON_LIBRARY_DATA_DIRECT, + aspect, + ICON_INDIRECT_DATA_ALPHA * alpha, + 0.0f, + NULL, + false, + UI_NO_ICON_OVERLAY_TEXT); } static void vicon_strip_color_draw_library_data_override_noneditable( @@ -488,7 +503,8 @@ static void vicon_strip_color_draw_library_data_override_noneditable( ICON_INDIRECT_DATA_ALPHA * alpha * 0.75f, 0.0f, NULL, - false); + false, + UI_NO_ICON_OVERLAY_TEXT); } /* Dynamically render icon instead of rendering a plain color to a texture/buffer @@ -923,7 +939,7 @@ static void init_internal_icons(void) char iconfilestr[FILE_MAX]; if (icondir) { - BLI_join_dirfile(iconfilestr, sizeof(iconfilestr), icondir, btheme->tui.iconfile); + BLI_path_join(iconfilestr, sizeof(iconfilestr), icondir, btheme->tui.iconfile); /* if the image is missing bbuf will just be NULL */ bbuf = IMB_loadiffname(iconfilestr, IB_rect, NULL); @@ -1047,7 +1063,7 @@ static void init_iconfile_list(struct ListBase *list) /* check to see if the image is the right size, continue if not */ /* copying strings here should go ok, assuming that we never get back * a complete path to file longer than 256 chars */ - BLI_join_dirfile(iconfilestr, sizeof(iconfilestr), icondir, filename); + BLI_path_join(iconfilestr, sizeof(iconfilestr), icondir, filename); bbuf = IMB_loadiffname(iconfilestr, IB_rect); if (bbuf) { @@ -1716,9 +1732,47 @@ static void icon_draw_texture(float x, int ih, float alpha, const float rgb[3], - bool with_border) -{ - if (g_icon_draw_cache.enabled) { + bool with_border, + const IconTextOverlay *text_overlay) +{ + const float zoom_factor = w / UI_DPI_ICON_SIZE; + float text_width = 0.0f; + + /* No need to show if too zoomed out, otherwise it just adds noise. */ + const bool show_indicator = (text_overlay && text_overlay->text[0] != '\0') && + (zoom_factor > 0.7f); + + if (show_indicator) { + /* Handle the little numbers on top of the icon. */ + uchar text_color[4]; + UI_GetThemeColor3ubv(TH_TEXT, text_color); + text_color[3] = 255; + + uiFontStyle fstyle_small = *UI_FSTYLE_WIDGET; + fstyle_small.points *= zoom_factor; + fstyle_small.points *= 0.8f; + + rcti text_rect = { + .xmax = x + UI_UNIT_X * zoom_factor, + .xmin = x, + .ymax = y, + .ymin = y, + }; + + UI_fontstyle_draw(&fstyle_small, + &text_rect, + text_overlay->text, + sizeof(text_overlay->text), + text_color, + &(struct uiFontStyleDraw_Params){ + .align = UI_STYLE_TEXT_RIGHT, + }); + text_width = (float)UI_fontstyle_string_width(&fstyle_small, text_overlay->text) / UI_UNIT_X / + zoom_factor; + } + + /* Draw the actual icon. */ + if (!show_indicator && g_icon_draw_cache.enabled) { icon_draw_texture_cached(x, y, w, h, ix, iy, iw, ih, alpha, rgb, with_border); return; } @@ -1735,7 +1789,7 @@ static void icon_draw_texture(float x, GPUTexture *texture = with_border ? icongltex.tex[1] : icongltex.tex[0]; - GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR); + GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_ICON); GPU_shader_bind(shader); const int img_binding = GPU_shader_get_texture_binding(shader, "image"); @@ -1752,6 +1806,7 @@ static void icon_draw_texture(float x, GPU_shader_uniform_vector(shader, rect_tex_loc, 4, 1, (float[4]){x1, y1, x2, y2}); GPU_shader_uniform_vector(shader, rect_geom_loc, 4, 1, (float[4]){x, y, x + w, y + h}); + GPU_shader_uniform_1f(shader, "text_width", text_width); GPU_texture_bind_ex(texture, GPU_SAMPLER_ICON, img_binding, false); @@ -1786,7 +1841,8 @@ static void icon_draw_size(float x, int draw_size, const float desaturate, const uchar mono_rgba[4], - const bool mono_border) + const bool mono_border, + const IconTextOverlay *text_overlay) { bTheme *btheme = UI_GetTheme(); const float fdraw_size = (float)draw_size; @@ -1874,7 +1930,8 @@ static void icon_draw_size(float x, di->data.texture.h, alpha, NULL, - false); + false, + text_overlay); } else if (di->type == ICON_TYPE_MONO_TEXTURE) { /* Monochrome icon that uses text or theme color. */ @@ -1908,7 +1965,8 @@ static void icon_draw_size(float x, di->data.texture.h + 2 * border_texel, color[3], color, - with_border); + with_border, + text_overlay); } else if (di->type == ICON_TYPE_BUFFER) { @@ -2425,17 +2483,27 @@ int UI_icon_color_from_collection(const Collection *collection) void UI_icon_draw(float x, float y, int icon_id) { - UI_icon_draw_ex(x, y, icon_id, U.inv_dpi_fac, 1.0f, 0.0f, NULL, false); + UI_icon_draw_ex(x, y, icon_id, U.inv_dpi_fac, 1.0f, 0.0f, NULL, false, UI_NO_ICON_OVERLAY_TEXT); } void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha) { - UI_icon_draw_ex(x, y, icon_id, U.inv_dpi_fac, alpha, 0.0f, NULL, false); + UI_icon_draw_ex(x, y, icon_id, U.inv_dpi_fac, alpha, 0.0f, NULL, false, UI_NO_ICON_OVERLAY_TEXT); } void UI_icon_draw_preview(float x, float y, int icon_id, float aspect, float alpha, int size) { - icon_draw_size(x, y, icon_id, aspect, alpha, ICON_SIZE_PREVIEW, size, false, NULL, false); + icon_draw_size(x, + y, + icon_id, + aspect, + alpha, + ICON_SIZE_PREVIEW, + size, + false, + NULL, + false, + UI_NO_ICON_OVERLAY_TEXT); } void UI_icon_draw_ex(float x, @@ -2445,7 +2513,8 @@ void UI_icon_draw_ex(float x, float alpha, float desaturate, const uchar mono_color[4], - const bool mono_border) + const bool mono_border, + const IconTextOverlay *text_overlay) { const int draw_size = get_draw_size(ICON_SIZE_ICON); icon_draw_size(x, @@ -2457,7 +2526,19 @@ void UI_icon_draw_ex(float x, draw_size, desaturate, mono_color, - mono_border); + mono_border, + text_overlay); +} + +void UI_icon_text_overlay_init_from_count(IconTextOverlay *text_overlay, + const int icon_indicator_number) +{ + /* The icon indicator is used as an aggregator, no need to show if it is 1. */ + if (icon_indicator_number < 2) { + text_overlay->text[0] = '\0'; + return; + } + BLI_str_format_integer_unit(text_overlay->text, icon_indicator_number); } /* ********** Alert Icons ********** */ diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 6ef7d346418..982ac1753e1 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -23,6 +23,7 @@ struct ARegion; struct AnimationEvalContext; struct CurveMapping; struct CurveProfile; +struct IconTextOverlay; struct ID; struct ImBuf; struct Main; @@ -275,6 +276,9 @@ struct uiBut { uiButPushedStateFunc pushed_state_func; const void *pushed_state_arg; + /** Little indicator (e.g., counter) displayed on top of some icons. */ + struct IconTextOverlay icon_overlay_text; + /* pointer back */ uiBlock *block; }; @@ -473,6 +477,12 @@ typedef enum uiButtonGroupFlag { } uiButtonGroupFlag; ENUM_OPERATORS(uiButtonGroupFlag, UI_BUTTON_GROUP_PANEL_HEADER); +typedef struct uiBlockDynamicListener { + struct uiBlockDynamicListener *next, *prev; + + void (*listener_func)(const struct wmRegionListenerParams *params); +} uiBlockDynamicListener; + struct uiBlock { uiBlock *next, *prev; @@ -495,6 +505,8 @@ struct uiBlock { * state that is persistent over redraws (e.g. collapsed tree-view items). */ ListBase views; + ListBase dynamic_listeners; /* #uiBlockDynamicListener */ + char name[UI_MAX_NAME_STR]; float winmat[4][4]; @@ -1536,6 +1548,8 @@ void ui_interface_tag_script_reload_queries(void); /* interface_view.cc */ void ui_block_free_views(struct uiBlock *block); +void ui_block_views_listen(const uiBlock *block, + const struct wmRegionListenerParams *listener_params); uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, const uiViewHandle *new_view); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index c906a5b36f1..a456cef7be6 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -3235,7 +3235,7 @@ static uiBut *uiItemL_(uiLayout *layout, const char *name, int icon) return but; } -void uiItemL_ex( +uiBut *uiItemL_ex( uiLayout *layout, const char *name, int icon, const bool highlight, const bool redalert) { uiBut *but = uiItemL_(layout, name, icon); @@ -3248,6 +3248,8 @@ void uiItemL_ex( if (redalert) { UI_but_flag_enable(but, UI_BUT_REDALERT); } + + return but; } void uiItemL(uiLayout *layout, const char *name, int icon) @@ -5812,6 +5814,14 @@ void UI_menutype_draw(bContext *C, MenuType *mt, struct uiLayout *layout) printf("%s: opening menu \"%s\"\n", __func__, mt->idname); } + if (mt->listener) { + /* Forward the menu type listener to the block we're drawing in. */ + uiBlock *block = uiLayoutGetBlock(layout); + uiBlockDynamicListener *listener = MEM_mallocN(sizeof(*listener), "uiBlockDynamicListener"); + listener->listener_func = mt->listener; + BLI_addtail(&block->dynamic_listeners, listener); + } + if (layout->context) { CTX_store_set(C, layout->context); } diff --git a/source/blender/editors/interface/interface_ops.cc b/source/blender/editors/interface/interface_ops.cc index 8b29f1075b8..2d06dd2c465 100644 --- a/source/blender/editors/interface/interface_ops.cc +++ b/source/blender/editors/interface/interface_ops.cc @@ -1859,8 +1859,7 @@ static void edittranslation_find_po_file(const char *root, /* First, full lang code. */ BLI_snprintf(tstr, sizeof(tstr), "%s.po", uilng); - BLI_join_dirfile(path, maxlen, root, uilng); - BLI_path_append(path, maxlen, tstr); + BLI_path_join(path, maxlen, root, uilng, tstr); if (BLI_is_file(path)) { return; } @@ -1885,7 +1884,7 @@ static void edittranslation_find_po_file(const char *root, BLI_strncpy(tstr + szt, tc, sizeof(tstr) - szt); } - BLI_join_dirfile(path, maxlen, root, tstr); + BLI_path_join(path, maxlen, root, tstr); strcat(tstr, ".po"); BLI_path_append(path, maxlen, tstr); if (BLI_is_file(path)) { diff --git a/source/blender/editors/interface/interface_panel.cc b/source/blender/editors/interface/interface_panel.cc index 7a69e2f9b2f..24d8281aad8 100644 --- a/source/blender/editors/interface/interface_panel.cc +++ b/source/blender/editors/interface/interface_panel.cc @@ -1112,7 +1112,8 @@ static void panel_draw_aligned_widgets(const uiStyle *style, 0.7f, 0.0f, title_color, - false); + false, + UI_NO_ICON_OVERLAY_TEXT); GPU_blend(GPU_BLEND_NONE); } @@ -1140,7 +1141,8 @@ static void panel_draw_aligned_widgets(const uiStyle *style, 1.0f, 0.0f, title_color, - false); + false, + UI_NO_ICON_OVERLAY_TEXT); GPU_blend(GPU_BLEND_NONE); } diff --git a/source/blender/editors/interface/interface_region_menu_popup.cc b/source/blender/editors/interface/interface_region_menu_popup.cc index 569f657a544..0d19390d508 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.cc +++ b/source/blender/editors/interface/interface_region_menu_popup.cc @@ -10,6 +10,7 @@ #include <cstdarg> #include <cstdlib> #include <cstring> +#include <functional> #include "MEM_guardedalloc.h" @@ -166,28 +167,80 @@ struct uiPopupMenu { uiBut *but; ARegion *butregion; + /* Menu hash is created from this, to keep a memory of recently opened menus. */ + const char *title; + int mx, my; bool popup, slideout; - uiMenuCreateFunc menu_func; - void *menu_arg; + std::function<void(bContext *C, uiLayout *layout)> menu_func; }; +/** + * \param title: Optional. If set, it will be used to store recently opened menus so they can be + * opened with the mouse over the last chosen entry again. + */ +static void ui_popup_menu_create_block(bContext *C, + uiPopupMenu *pup, + const char *title, + const char *block_name) +{ + const uiStyle *style = UI_style_get_dpi(); + + pup->block = UI_block_begin(C, nullptr, block_name, UI_EMBOSS_PULLDOWN); + if (!pup->but) { + pup->block->flag |= UI_BLOCK_IS_FLIP | UI_BLOCK_NO_FLIP; + } + if (title && title[0]) { + pup->block->flag |= UI_BLOCK_POPUP_MEMORY; + pup->block->puphash = ui_popup_menu_hash(title); + } + pup->layout = UI_block_layout( + pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style); + + /* NOTE: this intentionally differs from the menu & sub-menu default because many operators + * use popups like this to select one of their options - + * where having invoke doesn't make sense. + * When the menu was opened from a button, use invoke still for compatibility. This used to be + * the default and changing now could cause issues. */ + const wmOperatorCallContext opcontext = pup->but ? WM_OP_INVOKE_REGION_WIN : + WM_OP_EXEC_REGION_WIN; + + uiLayoutSetOperatorContext(pup->layout, opcontext); + + if (pup->but) { + if (pup->but->context) { + uiLayoutContextCopy(pup->layout, pup->but->context); + } + } +} + static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup) { uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup); - if (pup->menu_func) { - pup->block->handle = handle; - pup->menu_func(C, pup->layout, pup->menu_arg); - pup->block->handle = nullptr; + int minwidth = 0; + + if (!pup->layout) { + ui_popup_menu_create_block(C, pup, pup->title, __func__); + + if (pup->menu_func) { + pup->block->handle = handle; + pup->menu_func(C, pup->layout); + pup->block->handle = nullptr; + } + + if (uiLayoutGetUnitsX(pup->layout) != 0.0f) { + /* Use the minimum width from the layout if it's set. */ + minwidth = uiLayoutGetUnitsX(pup->layout) * UI_UNIT_X; + } + + pup->layout = nullptr; } /* Find block minimum width. */ - int minwidth; - if (uiLayoutGetUnitsX(pup->layout) != 0.0f) { - /* Use the minimum width from the layout if it's set. */ - minwidth = uiLayoutGetUnitsX(pup->layout) * UI_UNIT_X; + if (minwidth) { + /* Skip. */ } else if (pup->but) { /* Minimum width to enforce. */ @@ -236,7 +289,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT); if (pup->popup) { - int offset[2]; + int offset[2] = {0, 0}; uiBut *but_activate = nullptr; UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_NUMSELECT); @@ -244,36 +297,42 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi UI_block_direction_set(block, direction); /* offset the mouse position, possibly based on earlier selection */ - uiBut *bt; - if ((block->flag & UI_BLOCK_POPUP_MEMORY) && (bt = ui_popup_menu_memory_get(block))) { - /* position mouse on last clicked item, at 0.8*width of the - * button, so it doesn't overlap the text too much, also note - * the offset is negative because we are inverse moving the - * block to be under the mouse */ - offset[0] = -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect)); - offset[1] = -(bt->rect.ymin + 0.5f * UI_UNIT_Y); - - if (ui_but_is_editable(bt)) { - but_activate = bt; - } - } - else { - /* position mouse at 0.8*width of the button and below the tile - * on the first item */ - offset[0] = 0; - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { - offset[0] = min_ii(offset[0], - -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect))); + if (!handle->refresh) { + uiBut *bt; + if ((block->flag & UI_BLOCK_POPUP_MEMORY) && (bt = ui_popup_menu_memory_get(block))) { + /* position mouse on last clicked item, at 0.8*width of the + * button, so it doesn't overlap the text too much, also note + * the offset is negative because we are inverse moving the + * block to be under the mouse */ + offset[0] = -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect)); + offset[1] = -(bt->rect.ymin + 0.5f * UI_UNIT_Y); + + if (ui_but_is_editable(bt)) { + but_activate = bt; + } } + else { + /* position mouse at 0.8*width of the button and below the tile + * on the first item */ + offset[0] = 0; + LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + offset[0] = min_ii(offset[0], + -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect))); + } - offset[1] = 2.1 * UI_UNIT_Y; + offset[1] = 2.1 * UI_UNIT_Y; - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { - if (ui_but_is_editable(but_iter)) { - but_activate = but_iter; - break; + LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + if (ui_but_is_editable(but_iter)) { + but_activate = but_iter; + break; + } } } + copy_v2_v2_int(handle->prev_bounds_offset, offset); + } + else { + copy_v2_v2_int(offset, handle->prev_bounds_offset); } /* in rare cases this is needed since moving the popup @@ -312,27 +371,35 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi return pup->block; } -uiPopupBlockHandle *ui_popup_menu_create( - bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg) +static void ui_block_free_func_POPUP(void *arg_pup) +{ + uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup); + MEM_delete(pup); +} + +static uiPopupBlockHandle *ui_popup_menu_create( + bContext *C, + ARegion *butregion, + uiBut *but, + const char *title, + std::function<void(bContext *, uiLayout *)> menu_func) { wmWindow *window = CTX_wm_window(C); - const uiStyle *style = UI_style_get_dpi(); - uiPopupMenu *pup = MEM_cnew<uiPopupMenu>(__func__); - pup->block = UI_block_begin(C, nullptr, __func__, UI_EMBOSS_PULLDOWN); - pup->block->flag |= UI_BLOCK_NUMSELECT; /* Default menus to numeric-selection. */ - pup->layout = UI_block_layout( - pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style); - pup->slideout = but ? ui_block_is_menu(but->block) : false; - pup->but = but; - uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN); + uiPopupMenu *pup = MEM_new<uiPopupMenu>(__func__); + pup->title = title; + /* menu is created from a callback */ + pup->menu_func = menu_func; + if (but) { + pup->slideout = ui_block_is_menu(but->block); + pup->but = but; + } if (!but) { /* no button to start from, means we are a popup */ pup->mx = window->eventstate->xy[0]; pup->my = window->eventstate->xy[1]; pup->popup = true; - pup->block->flag |= UI_BLOCK_NO_FLIP; } /* some enums reversing is strange, currently we have no good way to * reverse some enum's but not others, so reverse all so the first menu @@ -346,17 +413,10 @@ uiPopupBlockHandle *ui_popup_menu_create( pup->block->flag |= UI_BLOCK_NO_FLIP; } #endif - if (but->context) { - uiLayoutContextCopy(pup->layout, but->context); - } } - /* menu is created from a callback */ - pup->menu_func = menu_func; - pup->menu_arg = arg; - uiPopupBlockHandle *handle = ui_popup_block_create( - C, butregion, but, nullptr, ui_block_func_POPUP, pup, nullptr); + C, butregion, but, nullptr, ui_block_func_POPUP, pup, ui_block_free_func_POPUP); if (!but) { handle->popup = true; @@ -365,68 +425,73 @@ uiPopupBlockHandle *ui_popup_menu_create( WM_event_add_mousemove(window); } - MEM_freeN(pup); - return handle; } +uiPopupBlockHandle *ui_popup_menu_create( + bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg) +{ + return ui_popup_menu_create( + C, butregion, but, nullptr, [menu_func, arg](bContext *C, uiLayout *layout) { + menu_func(C, layout, arg); + }); +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name Popup Menu API with begin & end * \{ */ +static void create_title_button(uiLayout *layout, const char *title, int icon) +{ + uiBlock *block = uiLayoutGetBlock(layout); + char titlestr[256]; + + if (icon) { + BLI_snprintf(titlestr, sizeof(titlestr), " %s", title); + uiDefIconTextBut(block, + UI_BTYPE_LABEL, + 0, + icon, + titlestr, + 0, + 0, + 200, + UI_UNIT_Y, + nullptr, + 0.0, + 0.0, + 0, + 0, + ""); + } + else { + uiBut *but = uiDefBut( + block, UI_BTYPE_LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, nullptr, 0.0, 0.0, 0, 0, ""); + but->drawflag = UI_BUT_TEXT_LEFT; + } + + uiItemS(layout); +} + +/* Used to directly create a popup menu that is not refreshed on redraw. */ uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, const char *title, const char *block_name, int icon) { - const uiStyle *style = UI_style_get_dpi(); - uiPopupMenu *pup = MEM_cnew<uiPopupMenu>(__func__); + uiPopupMenu *pup = MEM_new<uiPopupMenu>(__func__); - pup->block = UI_block_begin(C, nullptr, block_name, UI_EMBOSS_PULLDOWN); - pup->block->flag |= UI_BLOCK_POPUP_MEMORY | UI_BLOCK_IS_FLIP; - pup->block->puphash = ui_popup_menu_hash(title); - pup->layout = UI_block_layout( - pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style); + pup->title = title; - /* NOTE: this intentionally differs from the menu & sub-menu default because many operators - * use popups like this to select one of their options - - * where having invoke doesn't make sense */ - uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN); + ui_popup_menu_create_block(C, pup, title, block_name); /* create in advance so we can let buttons point to retval already */ pup->block->handle = MEM_cnew<uiPopupBlockHandle>(__func__); - /* create title button */ if (title[0]) { - char titlestr[256]; - - if (icon) { - BLI_snprintf(titlestr, sizeof(titlestr), " %s", title); - uiDefIconTextBut(pup->block, - UI_BTYPE_LABEL, - 0, - icon, - titlestr, - 0, - 0, - 200, - UI_UNIT_Y, - nullptr, - 0.0, - 0.0, - 0, - 0, - ""); - } - else { - uiBut *but = uiDefBut( - pup->block, UI_BTYPE_LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, nullptr, 0.0, 0.0, 0, 0, ""); - but->drawflag = UI_BUT_TEXT_LEFT; - } - - uiItemS(pup->layout); + create_title_button(pup->layout, title, icon); } return pup; @@ -465,7 +530,7 @@ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) UI_popup_handlers_add(C, &window->modalhandlers, menu, 0); WM_event_add_mousemove(window); - MEM_freeN(pup); + MEM_delete(pup); } bool UI_popup_menu_end_or_cancel(bContext *C, uiPopupMenu *pup) @@ -477,7 +542,7 @@ bool UI_popup_menu_end_or_cancel(bContext *C, uiPopupMenu *pup) UI_block_layout_resolve(pup->block, nullptr, nullptr); MEM_freeN(pup->block->handle); UI_block_free(C, pup->block); - MEM_freeN(pup); + MEM_delete(pup); return false; } @@ -541,6 +606,20 @@ void UI_popup_menu_reports(bContext *C, ReportList *reports) } } +static void ui_popup_menu_create_from_menutype(bContext *C, + MenuType *mt, + const char *title, + const int icon) +{ + uiPopupBlockHandle *handle = ui_popup_menu_create( + C, nullptr, nullptr, title, [mt, title, icon](bContext *C, uiLayout *layout) -> void { + create_title_button(layout, title, icon); + ui_item_menutype_func(C, layout, mt); + }); + + handle->can_refresh = true; +} + int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) { MenuType *mt = WM_menutype_find(idname, true); @@ -554,14 +633,21 @@ int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) /* cancel but allow event to pass through, just like operators do */ return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); } + /* For now always recreate menus on redraw that were invoked with this function. Maybe we want to + * make that optional somehow. */ + const bool allow_refresh = true; - uiPopupMenu *pup = UI_popup_menu_begin( - C, CTX_IFACE_(mt->translation_context, mt->label), ICON_NONE); - uiLayout *layout = UI_popup_menu_layout(pup); - - UI_menutype_draw(C, mt, layout); - - UI_popup_menu_end(C, pup); + const char *title = CTX_IFACE_(mt->translation_context, mt->label); + if (allow_refresh) { + ui_popup_menu_create_from_menutype(C, mt, title, ICON_NONE); + } + else { + /* If no refresh is needed, create the block directly. */ + uiPopupMenu *pup = UI_popup_menu_begin(C, title, ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); + UI_menutype_draw(C, mt, layout); + UI_popup_menu_end(C, pup); + } return OPERATOR_INTERFACE; } diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 6ba80e2e0d9..1bad9b34a72 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1420,21 +1420,25 @@ static void widget_draw_icon( /* to indicate draggable */ if (ui_but_drag_is_draggable(but) && (but->flag & UI_ACTIVE)) { - UI_icon_draw_ex(xs, ys, icon, aspect, 1.25f, 0.0f, color, has_theme); + UI_icon_draw_ex( + xs, ys, icon, aspect, 1.25f, 0.0f, color, has_theme, &but->icon_overlay_text); } else if (but->flag & (UI_ACTIVE | UI_SELECT | UI_SELECT_DRAW)) { - UI_icon_draw_ex(xs, ys, icon, aspect, alpha, 0.0f, color, has_theme); + UI_icon_draw_ex( + xs, ys, icon, aspect, alpha, 0.0f, color, has_theme, &but->icon_overlay_text); } else if (!((but->icon != ICON_NONE) && UI_but_is_tool(but))) { if (has_theme) { alpha *= 0.8f; } - UI_icon_draw_ex(xs, ys, icon, aspect, alpha, 0.0f, color, has_theme); + UI_icon_draw_ex( + xs, ys, icon, aspect, alpha, 0.0f, color, has_theme, &but->icon_overlay_text); } else { const bTheme *btheme = UI_GetTheme(); const float desaturate = 1.0 - btheme->tui.icon_saturation; - UI_icon_draw_ex(xs, ys, icon, aspect, alpha, desaturate, color, has_theme); + UI_icon_draw_ex( + xs, ys, icon, aspect, alpha, desaturate, color, has_theme, &but->icon_overlay_text); } } @@ -5426,7 +5430,8 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, GPU_blend(GPU_BLEND_ALPHA); /* XXX scale weak get from fstyle? */ - UI_icon_draw_ex(xs, ys, iconid, aspect, 1.0f, 0.0f, wt->wcol.text, false); + UI_icon_draw_ex( + xs, ys, iconid, aspect, 1.0f, 0.0f, wt->wcol.text, false, UI_NO_ICON_OVERLAY_TEXT); GPU_blend(GPU_BLEND_NONE); } diff --git a/source/blender/editors/interface/views/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc index c568a8cab74..71974b4d2f9 100644 --- a/source/blender/editors/interface/views/interface_view.cc +++ b/source/blender/editors/interface/views/interface_view.cc @@ -81,7 +81,7 @@ void ui_block_free_views(uiBlock *block) } } -void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) +void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) { ARegion *region = listener_params->region; |