Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/interface/interface_panel.c
| Show First 20 Lines • Show All 267 Lines • ▼ Show 20 Lines | static Panel *panel_add_instanced(ARegion *region, | ||||
| } | } | ||||
| panel->sortorder = max_sortorder + 1; | panel->sortorder = max_sortorder + 1; | ||||
| BLI_addtail(panels, panel); | BLI_addtail(panels, panel); | ||||
| return panel; | return panel; | ||||
| } | } | ||||
| /** | |||||
| * Called in situations where panels need to be added dynamically rather than | |||||
| * having only one panel corresponding to each #PanelType. | |||||
| */ | |||||
| Panel *UI_panel_add_instanced(const bContext *C, | Panel *UI_panel_add_instanced(const bContext *C, | ||||
| ARegion *region, | ARegion *region, | ||||
| ListBase *panels, | ListBase *panels, | ||||
| const char *panel_idname, | const char *panel_idname, | ||||
| PointerRNA *custom_data) | PointerRNA *custom_data) | ||||
| { | { | ||||
| ARegionType *region_type = region->type; | ARegionType *region_type = region->type; | ||||
| PanelType *panel_type = BLI_findstring( | PanelType *panel_type = BLI_findstring( | ||||
| ®ion_type->paneltypes, panel_idname, offsetof(PanelType, idname)); | ®ion_type->paneltypes, panel_idname, offsetof(PanelType, idname)); | ||||
| if (panel_type == NULL) { | if (panel_type == NULL) { | ||||
| printf("Panel type '%s' not found.\n", panel_idname); | printf("Panel type '%s' not found.\n", panel_idname); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| Panel *new_panel = panel_add_instanced(region, panels, panel_type, custom_data); | Panel *new_panel = panel_add_instanced(region, panels, panel_type, custom_data); | ||||
| /* Do this after #panel_add_instatnced so all sub-panels are added. */ | /* Do this after #panel_add_instatnced so all sub-panels are added. */ | ||||
| panel_set_expansion_from_list_data(C, new_panel); | panel_set_expansion_from_list_data(C, new_panel); | ||||
| return new_panel; | return new_panel; | ||||
| } | } | ||||
| /** | |||||
| * Find a unique key to append to the #PanelType.idname for the lookup to the panel's #uiBlock. | |||||
| * Needed for instanced panels, where there can be multiple with the same type and identifier. | |||||
| */ | |||||
| void UI_list_panel_unique_str(Panel *panel, char *r_name) | void UI_list_panel_unique_str(Panel *panel, char *r_name) | ||||
| { | { | ||||
| /* The panel sort-order will be unique for a specific panel type because the instanced | /* The panel sort-order will be unique for a specific panel type because the instanced | ||||
| * panel list is regenerated for every change in the data order / length. */ | * panel list is regenerated for every change in the data order / length. */ | ||||
| snprintf(r_name, INSTANCED_PANEL_UNIQUE_STR_LEN, "%d", panel->sortorder); | snprintf(r_name, INSTANCED_PANEL_UNIQUE_STR_LEN, "%d", panel->sortorder); | ||||
| } | } | ||||
| /** | /** | ||||
| Show All 13 Lines | static void panel_delete(const bContext *C, ARegion *region, ListBase *panels, Panel *panel) | ||||
| BLI_remlink(panels, panel); | BLI_remlink(panels, panel); | ||||
| if (panel->activedata) { | if (panel->activedata) { | ||||
| MEM_freeN(panel->activedata); | MEM_freeN(panel->activedata); | ||||
| } | } | ||||
| MEM_freeN(panel); | MEM_freeN(panel); | ||||
| } | } | ||||
| /** | |||||
| * Remove instanced panels from the region's panel list. | |||||
| * | |||||
| * \note Can be called with NULL \a C, but it should be avoided because | |||||
| * handlers might not be removed. | |||||
| */ | |||||
| void UI_panels_free_instanced(const bContext *C, ARegion *region) | void UI_panels_free_instanced(const bContext *C, ARegion *region) | ||||
| { | { | ||||
| /* Delete panels with the instanced flag. */ | /* Delete panels with the instanced flag. */ | ||||
| LISTBASE_FOREACH_MUTABLE (Panel *, panel, ®ion->panels) { | LISTBASE_FOREACH_MUTABLE (Panel *, panel, ®ion->panels) { | ||||
| if ((panel->type != NULL) && (panel->type->flag & PANEL_TYPE_INSTANCED)) { | if ((panel->type != NULL) && (panel->type->flag & PANEL_TYPE_INSTANCED)) { | ||||
| /* Make sure the panel's handler is removed before deleting it. */ | /* Make sure the panel's handler is removed before deleting it. */ | ||||
| if (C != NULL && panel->activedata != NULL) { | if (C != NULL && panel->activedata != NULL) { | ||||
| panel_activate_state(C, panel, PANEL_STATE_EXIT); | panel_activate_state(C, panel, PANEL_STATE_EXIT); | ||||
| } | } | ||||
| /* Free panel's custom data. */ | /* Free panel's custom data. */ | ||||
| if (panel->runtime.custom_data_ptr != NULL) { | if (panel->runtime.custom_data_ptr != NULL) { | ||||
| MEM_freeN(panel->runtime.custom_data_ptr); | MEM_freeN(panel->runtime.custom_data_ptr); | ||||
| } | } | ||||
| /* Free the panel and its sub-panels. */ | /* Free the panel and its sub-panels. */ | ||||
| panel_delete(C, region, ®ion->panels, panel); | panel_delete(C, region, ®ion->panels, panel); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Check if the instanced panels in the region's panels correspond to the list of data the panels | |||||
| * represent. Returns false if the panels have been reordered or if the types from the list data | |||||
| * don't match in any way. | |||||
| * | |||||
| * \param data: The list of data to check against the instanced panels. | |||||
| * \param panel_idname_func: Function to find the #PanelType.idname for each item in the data list. | |||||
| * For a readability and generality, this lookup happens separately for each type of panel list. | |||||
| */ | |||||
| bool UI_panel_list_matches_data(ARegion *region, | bool UI_panel_list_matches_data(ARegion *region, | ||||
| ListBase *data, | ListBase *data, | ||||
| uiListPanelIDFromDataFunc panel_idname_func) | uiListPanelIDFromDataFunc panel_idname_func) | ||||
| { | { | ||||
| /* Check for NULL data. */ | /* Check for NULL data. */ | ||||
| int data_len = 0; | int data_len = 0; | ||||
| Link *data_link = NULL; | Link *data_link = NULL; | ||||
| if (data == NULL) { | if (data == NULL) { | ||||
| ▲ Show 20 Lines • Show All 315 Lines • ▼ Show 20 Lines | Panel *UI_panel_find_by_type(ListBase *lb, const PanelType *pt) | ||||
| LISTBASE_FOREACH (Panel *, panel, lb) { | LISTBASE_FOREACH (Panel *, panel, lb) { | ||||
| if (STREQLEN(panel->panelname, idname, sizeof(panel->panelname))) { | if (STREQLEN(panel->panelname, idname, sizeof(panel->panelname))) { | ||||
| return panel; | return panel; | ||||
| } | } | ||||
| } | } | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /** | |||||
| * \note \a panel should be return value from #UI_panel_find_by_type and can be NULL. | |||||
| */ | |||||
| Panel *UI_panel_begin( | Panel *UI_panel_begin( | ||||
| ARegion *region, ListBase *lb, uiBlock *block, PanelType *pt, Panel *panel, bool *r_open) | ARegion *region, ListBase *lb, uiBlock *block, PanelType *pt, Panel *panel, bool *r_open) | ||||
| { | { | ||||
| Panel *panel_last; | Panel *panel_last; | ||||
| const char *drawname = CTX_IFACE_(pt->translation_context, pt->label); | const char *drawname = CTX_IFACE_(pt->translation_context, pt->label); | ||||
| const char *idname = pt->idname; | const char *idname = pt->idname; | ||||
| const bool newpanel = (panel == NULL); | const bool newpanel = (panel == NULL); | ||||
| ▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | if (UI_panel_is_closed(panel)) { | ||||
| return panel; | return panel; | ||||
| } | } | ||||
| *r_open = true; | *r_open = true; | ||||
| return panel; | return panel; | ||||
| } | } | ||||
| /** | |||||
| * Create the panel header button group, used to mark which buttons are part of | |||||
| * panel headers for the panel search process that happens later. This Should be | |||||
| * called before adding buttons for the panel's header layout. | |||||
| */ | |||||
| void UI_panel_header_buttons_begin(Panel *panel) | void UI_panel_header_buttons_begin(Panel *panel) | ||||
| { | { | ||||
| uiBlock *block = panel->runtime.block; | uiBlock *block = panel->runtime.block; | ||||
| ui_block_new_button_group(block, UI_BUTTON_GROUP_LOCK | UI_BUTTON_GROUP_PANEL_HEADER); | ui_block_new_button_group(block, UI_BUTTON_GROUP_LOCK | UI_BUTTON_GROUP_PANEL_HEADER); | ||||
| } | } | ||||
| /** | |||||
| * Finish the button group for the panel header to avoid putting panel body buttons in it. | |||||
| */ | |||||
| void UI_panel_header_buttons_end(Panel *panel) | void UI_panel_header_buttons_end(Panel *panel) | ||||
| { | { | ||||
| uiBlock *block = panel->runtime.block; | uiBlock *block = panel->runtime.block; | ||||
| /* A button group should always be created in #UI_panel_header_buttons_begin. */ | /* A button group should always be created in #UI_panel_header_buttons_begin. */ | ||||
| BLI_assert(!BLI_listbase_is_empty(&block->button_groups)); | BLI_assert(!BLI_listbase_is_empty(&block->button_groups)); | ||||
| uiButtonGroup *button_group = block->button_groups.last; | uiButtonGroup *button_group = block->button_groups.last; | ||||
| ▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | static void panel_matches_search_filter_recursive(const Panel *panel, bool *filter_matches) | ||||
| /* If the panel has no match we need to make sure that its children are too. */ | /* If the panel has no match we need to make sure that its children are too. */ | ||||
| if (!*filter_matches) { | if (!*filter_matches) { | ||||
| LISTBASE_FOREACH (const Panel *, child_panel, &panel->children) { | LISTBASE_FOREACH (const Panel *, child_panel, &panel->children) { | ||||
| panel_matches_search_filter_recursive(child_panel, filter_matches); | panel_matches_search_filter_recursive(child_panel, filter_matches); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Find whether a panel or any of its sub-panels contain a property that matches the search filter, | |||||
| * depending on the search process running in #UI_block_apply_search_filter earlier. | |||||
| */ | |||||
| bool UI_panel_matches_search_filter(const Panel *panel) | bool UI_panel_matches_search_filter(const Panel *panel) | ||||
| { | { | ||||
| bool search_filter_matches = false; | bool search_filter_matches = false; | ||||
| panel_matches_search_filter_recursive(panel, &search_filter_matches); | panel_matches_search_filter_recursive(panel, &search_filter_matches); | ||||
| return search_filter_matches; | return search_filter_matches; | ||||
| } | } | ||||
| /** | /** | ||||
| ▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | static void region_panels_remove_invisible_layouts(ARegion *region) | ||||
| LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { | LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { | ||||
| if (panel->runtime_flag & PANEL_ACTIVE) { | if (panel->runtime_flag & PANEL_ACTIVE) { | ||||
| BLI_assert(panel->runtime.block != NULL); | BLI_assert(panel->runtime.block != NULL); | ||||
| panel_remove_invisible_layouts_recursive(panel, NULL); | panel_remove_invisible_layouts_recursive(panel, NULL); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Get the panel's expansion state, taking into account | |||||
| * expansion set from property search if it applies. | |||||
| */ | |||||
| bool UI_panel_is_closed(const Panel *panel) | bool UI_panel_is_closed(const Panel *panel) | ||||
| { | { | ||||
| /* Header-less panels can never be closed, otherwise they could disappear. */ | /* Header-less panels can never be closed, otherwise they could disappear. */ | ||||
| if (panel->type && panel->type->flag & PANEL_TYPE_NO_HEADER) { | if (panel->type && panel->type->flag & PANEL_TYPE_NO_HEADER) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (panel->runtime_flag & PANEL_USE_CLOSED_FROM_SEARCH) { | if (panel->runtime_flag & PANEL_USE_CLOSED_FROM_SEARCH) { | ||||
| Show All 9 Lines | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Drawing | /** \name Drawing | ||||
| * \{ */ | * \{ */ | ||||
| /** | |||||
| * Draw panels, selected (panels currently being dragged) on top. | |||||
| */ | |||||
| void UI_panels_draw(const bContext *C, ARegion *region) | void UI_panels_draw(const bContext *C, ARegion *region) | ||||
| { | { | ||||
| /* Draw in reverse order, because #uiBlocks are added in reverse order | /* Draw in reverse order, because #uiBlocks are added in reverse order | ||||
| * and we need child panels to draw on top. */ | * and we need child panels to draw on top. */ | ||||
| LISTBASE_FOREACH_BACKWARD (uiBlock *, block, ®ion->uiblocks) { | LISTBASE_FOREACH_BACKWARD (uiBlock *, block, ®ion->uiblocks) { | ||||
| if (block->active && block->panel && !UI_panel_is_dragging(block->panel) && | if (block->active && block->panel && !UI_panel_is_dragging(block->panel) && | ||||
| !UI_block_is_search_only(block)) { | !UI_block_is_search_only(block)) { | ||||
| UI_block_draw(C, block); | UI_block_draw(C, block); | ||||
| } | } | ||||
| } | } | ||||
| LISTBASE_FOREACH_BACKWARD (uiBlock *, block, ®ion->uiblocks) { | LISTBASE_FOREACH_BACKWARD (uiBlock *, block, ®ion->uiblocks) { | ||||
| if (block->active && block->panel && UI_panel_is_dragging(block->panel) && | if (block->active && block->panel && UI_panel_is_dragging(block->panel) && | ||||
| !UI_block_is_search_only(block)) { | !UI_block_is_search_only(block)) { | ||||
| UI_block_draw(C, block); | UI_block_draw(C, block); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| #define PNL_ICON UI_UNIT_X /* Could be UI_UNIT_Y too. */ | #define PNL_ICON UI_UNIT_X /* Could be UI_UNIT_Y too. */ | ||||
| /* For button layout next to label. */ | |||||
| void UI_panel_label_offset(const uiBlock *block, int *r_x, int *r_y) | void UI_panel_label_offset(const uiBlock *block, int *r_x, int *r_y) | ||||
| { | { | ||||
| Panel *panel = block->panel; | Panel *panel = block->panel; | ||||
| const bool is_subpanel = (panel->type && panel->type->parent); | const bool is_subpanel = (panel->type && panel->type->parent); | ||||
| *r_x = UI_UNIT_X * 1.0f; | *r_x = UI_UNIT_X * 1.0f; | ||||
| *r_y = UI_UNIT_Y * 1.5f; | *r_y = UI_UNIT_Y * 1.5f; | ||||
| ▲ Show 20 Lines • Show All 202 Lines • ▼ Show 20 Lines | UI_draw_roundbox_4fv( | ||||
| radius, | radius, | ||||
| panel_headercolor); | panel_headercolor); | ||||
| } | } | ||||
| GPU_blend(GPU_BLEND_NONE); | GPU_blend(GPU_BLEND_NONE); | ||||
| immUnbindProgram(); | immUnbindProgram(); | ||||
| } | } | ||||
| /** | |||||
| * Draw a panel integrated in buttons-window, tool/property lists etc. | |||||
| */ | |||||
| void ui_draw_aligned_panel(const uiStyle *style, | void ui_draw_aligned_panel(const uiStyle *style, | ||||
| const uiBlock *block, | const uiBlock *block, | ||||
| const rcti *rect, | const rcti *rect, | ||||
| const bool show_pin, | const bool show_pin, | ||||
| const bool show_background, | const bool show_background, | ||||
| const bool region_search_filter_active) | const bool region_search_filter_active) | ||||
| { | { | ||||
| const Panel *panel = block->panel; | const Panel *panel = block->panel; | ||||
| ▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Category Drawing (Tabs) | /** \name Category Drawing (Tabs) | ||||
| * \{ */ | * \{ */ | ||||
| #define TABS_PADDING_BETWEEN_FACTOR 4.0f | #define TABS_PADDING_BETWEEN_FACTOR 4.0f | ||||
| #define TABS_PADDING_TEXT_FACTOR 6.0f | #define TABS_PADDING_TEXT_FACTOR 6.0f | ||||
| /** | |||||
| * Draw vertical tabs on the left side of the region, one tab per category. | |||||
| */ | |||||
| void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) | void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) | ||||
| { | { | ||||
| // #define USE_FLAT_INACTIVE | // #define USE_FLAT_INACTIVE | ||||
| const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT); | const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT); | ||||
| View2D *v2d = ®ion->v2d; | View2D *v2d = ®ion->v2d; | ||||
| const uiStyle *style = UI_style_get(); | const uiStyle *style = UI_style_get(); | ||||
| const uiFontStyle *fstyle = &style->widget; | const uiFontStyle *fstyle = &style->widget; | ||||
| const int fontid = fstyle->uifont_id; | const int fontid = fstyle->uifont_id; | ||||
| ▲ Show 20 Lines • Show All 1,000 Lines • ▼ Show 20 Lines | if (LIKELY(category)) { | ||||
| } | } | ||||
| } | } | ||||
| return WM_UI_HANDLER_BREAK; | return WM_UI_HANDLER_BREAK; | ||||
| } | } | ||||
| return WM_UI_HANDLER_CONTINUE; | return WM_UI_HANDLER_CONTINUE; | ||||
| } | } | ||||
| /** | |||||
| * Handle region panel events like opening and closing panels, changing categories, etc. | |||||
| * | |||||
| * \note Could become a modal key-map. | |||||
| */ | |||||
| int ui_handler_panel_region(bContext *C, | int ui_handler_panel_region(bContext *C, | ||||
| const wmEvent *event, | const wmEvent *event, | ||||
| ARegion *region, | ARegion *region, | ||||
| const uiBut *active_but) | const uiBut *active_but) | ||||
| { | { | ||||
| /* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */ | /* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */ | ||||
| if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { | if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { | ||||
| return WM_UI_HANDLER_CONTINUE; | return WM_UI_HANDLER_CONTINUE; | ||||
| ▲ Show 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| panel->runtime.custom_data_ptr = custom_data; | panel->runtime.custom_data_ptr = custom_data; | ||||
| LISTBASE_FOREACH (Panel *, child_panel, &panel->children) { | LISTBASE_FOREACH (Panel *, child_panel, &panel->children) { | ||||
| ui_panel_custom_data_set_recursive(child_panel, custom_data); | ui_panel_custom_data_set_recursive(child_panel, custom_data); | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Set a context for this entire panel and its current layout. This should be used whenever panel | |||||
| * callbacks that are called outside of regular drawing might require context. Currently it affects | |||||
| * the #PanelType.reorder callback only. | |||||
| */ | |||||
| void UI_panel_context_pointer_set(Panel *panel, const char *name, PointerRNA *ptr) | void UI_panel_context_pointer_set(Panel *panel, const char *name, PointerRNA *ptr) | ||||
| { | { | ||||
| uiLayoutSetContextPointer(panel->layout, name, ptr); | uiLayoutSetContextPointer(panel->layout, name, ptr); | ||||
| panel->runtime.context = uiLayoutGetContextStore(panel->layout); | panel->runtime.context = uiLayoutGetContextStore(panel->layout); | ||||
| } | } | ||||
| void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data) | void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 166 Lines • Show Last 20 Lines | |||||