UI: add subpanel support.

In the Python API, any panel becomes a subpanel by setting bl_parent_id
to the name of the parent panel. These subpanels can contain advanced or
less commonly used settings.
This commit is contained in:
Brecht Van Lommel 2018-06-03 13:32:36 +02:00
parent 9b01e7bc27
commit d7c2b78822
15 changed files with 403 additions and 190 deletions

View File

@ -28,7 +28,7 @@
* and keep comment above the defines.
* Use STRINGIFY() rather than defining with quotes */
#define BLENDER_VERSION 280
#define BLENDER_SUBVERSION 16
#define BLENDER_SUBVERSION 17
/* Several breakages with 270, e.g. constraint deg vs rad */
#define BLENDER_MINVERSION 270
#define BLENDER_MINSUBVERSION 6

View File

@ -205,7 +205,8 @@ typedef struct PanelType {
char translation_context[BKE_ST_MAXNAME];
char context[BKE_ST_MAXNAME]; /* for buttons window */
char category[BKE_ST_MAXNAME]; /* for category tabs */
char owner_id[BKE_ST_MAXNAME]; /* for work-spaces to selectively show. */
char owner_id[BKE_ST_MAXNAME]; /* for work-spaces to selectively show. */
char parent_id[BKE_ST_MAXNAME]; /* parent idname for subpanels */
int space_type;
int region_type;
@ -218,6 +219,10 @@ typedef struct PanelType {
/* draw entirely, view changes should be handled here */
void (*draw)(const struct bContext *C, struct Panel *pa);
/* sub panels */
struct PanelType *parent;
ListBase children;
/* RNA integration */
ExtensionRNA ext;
} PanelType;

View File

@ -69,13 +69,19 @@ static void spacetype_free(SpaceType *st)
for (art = st->regiontypes.first; art; art = art->next) {
BLI_freelistN(&art->drawcalls);
for (pt = art->paneltypes.first; pt; pt = pt->next)
if (pt->ext.free)
for (pt = art->paneltypes.first; pt; pt = pt->next) {
if (pt->ext.free) {
pt->ext.free(pt->ext.data);
}
for (ht = art->headertypes.first; ht; ht = ht->next)
if (ht->ext.free)
BLI_freelistN(&pt->children);
}
for (ht = art->headertypes.first; ht; ht = ht->next) {
if (ht->ext.free) {
ht->ext.free(ht->ext.data);
}
}
BLI_freelistN(&art->paneltypes);
BLI_freelistN(&art->headertypes);
@ -169,10 +175,35 @@ void BKE_spacedata_freelist(ListBase *lb)
BLI_freelistN(lb);
}
static void panel_list_copy(ListBase *newlb, const ListBase *lb)
{
BLI_listbase_clear(newlb);
BLI_duplicatelist(newlb, lb);
/* copy panel pointers */
Panel *newpa = newlb->first;
Panel *pa = lb->first;
for (; newpa; newpa = newpa->next, pa = pa->next) {
newpa->activedata = NULL;
Panel *newpatab = newlb->first;
Panel *patab = lb->first;
while (newpatab) {
if (newpa->paneltab == patab) {
newpa->paneltab = newpatab;
break;
}
newpatab = newpatab->next;
patab = patab->next;
}
panel_list_copy(&newpa->children, &pa->children);
}
}
ARegion *BKE_area_region_copy(SpaceType *st, ARegion *ar)
{
ARegion *newar = MEM_dupallocN(ar);
Panel *pa, *newpa, *patab;
newar->prev = newar->next = NULL;
BLI_listbase_clear(&newar->handlers);
@ -199,25 +230,10 @@ ARegion *BKE_area_region_copy(SpaceType *st, ARegion *ar)
if (ar->v2d.tab_offset)
newar->v2d.tab_offset = MEM_dupallocN(ar->v2d.tab_offset);
BLI_listbase_clear(&newar->panels);
BLI_duplicatelist(&newar->panels, &ar->panels);
panel_list_copy(&newar->panels, &ar->panels);
BLI_listbase_clear(&newar->ui_previews);
BLI_duplicatelist(&newar->ui_previews, &ar->ui_previews);
/* copy panel pointers */
for (newpa = newar->panels.first; newpa; newpa = newpa->next) {
patab = newar->panels.first;
pa = ar->panels.first;
while (patab) {
if (newpa->paneltab == pa) {
newpa->paneltab = patab;
break;
}
patab = patab->next;
pa = pa->next;
}
}
return newar;
}
@ -329,6 +345,19 @@ void BKE_region_callback_free_manipulatormap_set(void (*callback)(struct wmManip
region_free_manipulatormap_callback = callback;
}
static void panel_list_free(ListBase *lb)
{
Panel *pa, *pa_next;
for (pa = lb->first; pa; pa = pa_next) {
pa_next = pa->next;
if (pa->activedata) {
MEM_freeN(pa->activedata);
}
panel_list_free(&pa->children);
MEM_freeN(pa);
}
}
/* not region itself */
void BKE_area_region_free(SpaceType *st, ARegion *ar)
{
@ -351,16 +380,7 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar)
ar->v2d.tab_offset = NULL;
}
if (!BLI_listbase_is_empty(&ar->panels)) {
Panel *pa, *pa_next;
for (pa = ar->panels.first; pa; pa = pa_next) {
pa_next = pa->next;
if (pa->activedata) {
MEM_freeN(pa->activedata);
}
MEM_freeN(pa);
}
}
panel_list_free(&ar->panels);
for (uilst = ar->ui_lists.first; uilst; uilst = uilst->next) {
if (uilst->dyn_data) {

View File

@ -6396,19 +6396,24 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd)
/* *********** READ AREA **************** */
static void direct_link_region(FileData *fd, ARegion *ar, int spacetype)
static void direct_link_panel_list(FileData *fd, ListBase *lb)
{
Panel *pa;
uiList *ui_list;
link_list(fd, lb);
link_list(fd, &ar->panels);
for (pa = ar->panels.first; pa; pa = pa->next) {
for (Panel *pa = lb->first; pa; pa = pa->next) {
pa->paneltab = newdataadr(fd, pa->paneltab);
pa->runtime_flag = 0;
pa->activedata = NULL;
pa->type = NULL;
direct_link_panel_list(fd, &pa->children);
}
}
static void direct_link_region(FileData *fd, ARegion *ar, int spacetype)
{
uiList *ui_list;
direct_link_panel_list(fd, &ar->panels);
link_list(fd, &ar->panels_category_active);

View File

@ -2762,14 +2762,19 @@ static void write_soops(WriteData *wd, SpaceOops *so)
}
}
static void write_panel_list(WriteData *wd, ListBase *lb)
{
for (Panel *pa = lb->first; pa; pa = pa->next) {
writestruct(wd, DATA, Panel, 1, pa);
write_panel_list(wd, &pa->children);
}
}
static void write_area_regions(WriteData *wd, ScrArea *area)
{
for (ARegion *region = area->regionbase.first; region; region = region->next) {
write_region(wd, region, area->spacetype);
for (Panel *pa = region->panels.first; pa; pa = pa->next) {
writestruct(wd, DATA, Panel, 1, pa);
}
write_panel_list(wd, &region->panels);
for (PanelCategoryStack *pc_act = region->panels_category_active.first; pc_act; pc_act = pc_act->next) {
writestruct(wd, DATA, PanelCategoryStack, 1, pc_act);

View File

@ -491,8 +491,6 @@ void UI_blocklist_update_window_matrix(const struct bContext *C, const struct Li
void UI_blocklist_draw(const struct bContext *C, const struct ListBase *lb);
void UI_block_update_from_old(const struct bContext *C, struct uiBlock *block);
uiBlock *UI_block_find_in_region(const char *name, struct ARegion *ar);
void UI_block_emboss_set(uiBlock *block, char dt);
void UI_block_free(const struct bContext *C, uiBlock *block);
@ -824,9 +822,10 @@ void UI_panels_begin(const struct bContext *C, struct ARegion *ar);
void UI_panels_end(const struct bContext *C, struct ARegion *ar, int *x, int *y);
void UI_panels_draw(const struct bContext *C, struct ARegion *ar);
struct Panel *UI_panel_find_by_type(struct ARegion *ar, struct PanelType *pt);
struct Panel *UI_panel_begin(struct ScrArea *sa, struct ARegion *ar, uiBlock *block,
struct PanelType *pt, struct Panel *pa, bool *r_open);
struct Panel *UI_panel_find_by_type(struct ListBase *lb, struct PanelType *pt);
struct Panel *UI_panel_begin(struct ScrArea *sa, struct ARegion *ar, struct ListBase *lb,
uiBlock *block, struct PanelType *pt, struct Panel *pa,
bool *r_open);
void UI_panel_end(uiBlock *block, int width, int height);
void UI_panels_scale(struct ARegion *ar, float new_width);

View File

@ -74,6 +74,7 @@ typedef enum ThemeColorID {
/* panels */
TH_PANEL_HEADER,
TH_PANEL_BACK,
TH_PANEL_SUB_BACK,
TH_PANEL_SHOW_HEADER,
TH_PANEL_SHOW_BACK,

View File

@ -2814,11 +2814,6 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh
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;

View File

@ -110,6 +110,7 @@ typedef struct uiHandlePanelData {
int startsizex, startsizey;
} uiHandlePanelData;
static int get_panel_real_size_y(Panel *pa);
static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelState state);
/*********************** space specific code ************************/
@ -133,45 +134,80 @@ 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;
}
}
*r_pa = NULL;
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;
}
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;
}
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;
}
/* 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;
if (sbuts->align) {
if (sbuts->re_align || 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;
}
return 0;
/* 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;
}
/* Detect panel marked for animation, if we're not already animating. */
if (pa_animation) {
if (!no_animation) {
*pa_animate = pa_animation;
}
return true;
}
return false;
}
/****************************** panels ******************************/
@ -215,14 +251,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;
@ -230,7 +266,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;
}
@ -243,7 +279,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);
@ -275,9 +311,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));
@ -285,7 +323,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))) {
@ -308,6 +346,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));
@ -315,14 +355,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++;
}
@ -348,6 +392,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;
@ -372,15 +429,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;
}
@ -600,6 +655,8 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con
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;
@ -613,7 +670,7 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con
unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_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;
@ -681,25 +738,28 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con
/* horizontal title */
if (is_closed_x == false) {
unsigned int col;
ui_draw_aligned_panel_header(style, block, &headrect, 'h');
Gwn_VertFormat *format = immVertexFormat();
pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
col = GWN_vertformat_attr_add(format, "color", GWN_COMP_F32, 4, GWN_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;
if (show_drag) {
unsigned int col;
Gwn_VertFormat *format = immVertexFormat();
pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
col = GWN_vertformat_attr_add(format, "color", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
BLI_rctf_scale(&itemrect, 0.7f);
immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
ui_draw_panel_dragwidget(pos, col, &itemrect);
immUnbindProgram();
/* 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;
/* Restore format for the following draws. */
pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
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 = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
}
}
/* if the panel is minimized vertically:
@ -727,8 +787,12 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
/* panel backdrop */
if (UI_GetThemeValue(TH_PANEL_SHOW_BACK)) {
/* draw with background color */
if (is_subpanel) {
glEnable(GL_BLEND);
immUniformThemeColor(TH_PANEL_SUB_BACK);
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
}
else if (UI_GetThemeValue(TH_PANEL_SHOW_BACK)) {
glEnable(GL_BLEND);
immUniformThemeColor(TH_PANEL_BACK);
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
@ -786,6 +850,16 @@ static int get_panel_size_y(Panel *pa)
return PNL_HEADER + pa->sizey;
}
static int get_panel_real_size_y(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;
}
/* 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)
@ -858,6 +932,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)
@ -949,10 +1041,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++) {
@ -1022,20 +1121,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)
{
@ -1074,8 +1180,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
@ -1102,14 +1207,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);
}
@ -1371,6 +1478,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;
@ -1465,7 +1574,7 @@ 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)) {
panel_activate_state(C, block->panel, PANEL_STATE_DRAG);
}
else if (show_pin && BLI_rctf_isect_x(&rect_pin, mx)) {

View File

@ -254,6 +254,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_SUB_BACK:
cp = ts->panelcolors.sub_back; break;
case TH_PANEL_SHOW_HEADER:
cp = &setting;
setting = ts->panelcolors.show_header;
@ -821,6 +823,7 @@ static void ui_theme_init_new_do(ThemeSpace *ts)
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->panelcolors.sub_back, 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);
@ -3022,6 +3025,16 @@ void init_userdef_do_versions(void)
}
}
if (!USER_VERSION_ATLEAST(280, 17)) {
for (bTheme *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->panelcolors.sub_back, 0, 0, 0, 25);
}
}
}
/**
* Include next version bump.
*/

View File

@ -1806,6 +1806,84 @@ BLI_INLINE bool streq_array_any(const char *s, const char *arr[])
return false;
}
static void ed_panel_draw(const bContext *C,
ScrArea *sa,
ARegion *ar,
ListBase *lb,
PanelType *pt,
Panel *panel,
int w,
int em,
bool vertical)
{
uiStyle *style = UI_style_get_dpi();
/* draw panel */
uiBlock *block = UI_block_begin(C, ar, pt->idname, UI_EMBOSS);
bool open;
panel = UI_panel_begin(sa, ar, lb, block, pt, panel, &open);
/* bad fixed values */
int triangle = (int)(UI_UNIT_Y * 1.1f);
int xco, yco, h = 0;
if (pt->draw_header && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) {
/* for enabled buttons */
panel->layout = UI_block_layout(
block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER,
triangle, (UI_UNIT_Y * 1.1f) + style->panelspace, UI_UNIT_Y, 1, 0, style);
pt->draw_header(C, panel);
UI_block_layout_resolve(block, &xco, &yco);
panel->labelofs = xco - triangle;
panel->layout = NULL;
}
else {
panel->labelofs = 0;
}
if (open) {
short panelContext;
/* panel context can either be toolbar region or normal panels region */
if (ar->regiontype == RGN_TYPE_TOOLS)
panelContext = UI_LAYOUT_TOOLBAR;
else
panelContext = UI_LAYOUT_PANEL;
panel->layout = UI_block_layout(
block, UI_LAYOUT_VERTICAL, panelContext,
style->panelspace, 0, w - 2 * style->panelspace, em, 0, style);
pt->draw(C, panel);
UI_block_layout_resolve(block, &xco, &yco);
panel->layout = NULL;
if (yco != 0) {
h = -yco + 2 * style->panelspace;
}
}
UI_block_end(C, block);
/* Draw child panels. */
if (open) {
for (LinkData *link = pt->children.first; link; link = link->next) {
PanelType *child_pt = link->data;
Panel *child_panel = UI_panel_find_by_type(&panel->children, child_pt);
if (child_pt->draw && (!child_pt->poll || child_pt->poll(C, child_pt))) {
ed_panel_draw(C, sa, ar, &panel->children, child_pt, child_panel, w, em, vertical);
}
}
}
UI_panel_end(block, w, h);
}
/**
* \param contexts: A NULL terminated array of context strings to match against.
* Matching against any of these strings will draw the panel.
@ -1815,13 +1893,10 @@ void ED_region_panels(const bContext *C, ARegion *ar, const char *contexts[], in
{
const WorkSpace *workspace = CTX_wm_workspace(C);
ScrArea *sa = CTX_wm_area(C);
uiStyle *style = UI_style_get_dpi();
uiBlock *block;
PanelType *pt;
Panel *panel;
View2D *v2d = &ar->v2d;
View2DScrollers *scrollers;
int x, y, xco, yco, w, em, triangle;
int x, y, w, em;
bool is_context_new = 0;
int scroll;
@ -1859,6 +1934,11 @@ void ED_region_panels(const bContext *C, ARegion *ar, const char *contexts[], in
/* collect panels to draw */
for (pt = ar->type->paneltypes.last; pt; pt = pt->prev) {
/* Only draw top level panels. */
if (pt->parent) {
continue;
}
/* verify context */
if (contexts && pt->context[0] && !streq_array_any(pt->context, contexts)) {
continue;
@ -1920,9 +2000,7 @@ void ED_region_panels(const bContext *C, ARegion *ar, const char *contexts[], in
BLI_SMALLSTACK_ITER_BEGIN(pt_stack, pt)
{
bool open;
panel = UI_panel_find_by_type(ar, pt);
Panel *panel = UI_panel_find_by_type(&ar->panels, pt);
if (use_category_tabs && pt->category[0] && !STREQ(category, pt->category)) {
if ((panel == NULL) || ((panel->flag & PNL_PIN) == 0)) {
@ -1930,56 +2008,7 @@ void ED_region_panels(const bContext *C, ARegion *ar, const char *contexts[], in
}
}
/* draw panel */
block = UI_block_begin(C, ar, pt->idname, UI_EMBOSS);
panel = UI_panel_begin(sa, ar, block, pt, panel, &open);
/* bad fixed values */
triangle = (int)(UI_UNIT_Y * 1.1f);
if (pt->draw_header && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) {
/* for enabled buttons */
panel->layout = UI_block_layout(
block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER,
triangle, (UI_UNIT_Y * 1.1f) + style->panelspace, UI_UNIT_Y, 1, 0, style);
pt->draw_header(C, panel);
UI_block_layout_resolve(block, &xco, &yco);
panel->labelofs = xco - triangle;
panel->layout = NULL;
}
else {
panel->labelofs = 0;
}
if (open) {
short panelContext;
/* panel context can either be toolbar region or normal panels region */
if (ar->regiontype == RGN_TYPE_TOOLS)
panelContext = UI_LAYOUT_TOOLBAR;
else
panelContext = UI_LAYOUT_PANEL;
panel->layout = UI_block_layout(
block, UI_LAYOUT_VERTICAL, panelContext,
style->panelspace, 0, w - 2 * style->panelspace, em, 0, style);
pt->draw(C, panel);
UI_block_layout_resolve(block, &xco, &yco);
panel->layout = NULL;
yco -= 2 * style->panelspace;
UI_panel_end(block, w, -yco);
}
else {
yco = 0;
UI_panel_end(block, w, 0);
}
UI_block_end(C, block);
ed_panel_draw(C, sa, ar, &ar->panels, pt, panel, w, em, vertical);
}
BLI_SMALLSTACK_ITER_END;

View File

@ -121,7 +121,9 @@ typedef struct Panel { /* the part from uiBlock that needs saved in file */
char panelname[64], tabname[64]; /* defined as UI_MAX_NAME_STR */
char drawname[64]; /* panelname is identifier for restoring location */
int ofsx, ofsy, sizex, sizey;
int ofsx, ofsy; /* offset within the region */
int sizex, sizey; /* panel size including children */
int blocksizex, blocksizey; /* panel size excluding children */
short labelofs, pad;
short flag, runtime_flag;
short control;
@ -129,6 +131,7 @@ typedef struct Panel { /* the part from uiBlock that needs saved in file */
int sortorder; /* panels are aligned according to increasing sortorder */
struct Panel *paneltab; /* this panel is tabbed in *paneltab */
void *activedata; /* runtime for panel manipulation */
ListBase children; /* sub panels */
} Panel;

View File

@ -150,9 +150,9 @@ typedef struct uiWidgetStateColors {
typedef struct uiPanelColors {
char header[4];
char back[4];
char sub_back[4];
short show_header;
short show_back;
int pad;
} uiPanelColors;
typedef struct uiGradientColors {

View File

@ -179,6 +179,12 @@ static void rna_Panel_unregister(Main *UNUSED(bmain), StructRNA *type)
RNA_struct_free_extension(type, &pt->ext);
RNA_struct_free(&BLENDER_RNA, type);
if (pt->parent) {
LinkData *link = BLI_findptr(&pt->parent->children, pt, offsetof(LinkData, data));
BLI_freelinkN(&pt->parent->children, link);
}
BLI_freelistN(&pt->children);
BLI_freelinkN(&art->paneltypes, pt);
/* update while blender is running */
@ -190,7 +196,7 @@ static StructRNA *rna_Panel_register(
StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free)
{
ARegionType *art;
PanelType *pt, dummypt = {NULL};
PanelType *pt, *parent = NULL, dummypt = {NULL};
Panel dummypanel = {NULL};
PointerRNA dummyptr;
int have_function[3];
@ -229,6 +235,10 @@ static StructRNA *rna_Panel_register(
BLI_freelinkN(&art->paneltypes, pt);
break;
}
if (dummypt.parent_id[0] && STREQ(pt->idname, dummypt.parent_id)) {
parent = pt;
}
}
if (!RNA_struct_available_or_report(reports, dummypt.idname)) {
return NULL;
@ -236,6 +246,11 @@ static StructRNA *rna_Panel_register(
if (!RNA_struct_bl_idname_ok_or_report(reports, dummypt.idname, "_PT_")) {
return NULL;
}
if (dummypt.parent_id[0] && !parent) {
BKE_reportf(reports, RPT_ERROR, "Registering panel class: parent '%s' for '%s' not found",
dummypt.parent_id, dummypt.idname);
return NULL;
}
/* create a new panel type */
pt = MEM_callocN(sizeof(PanelType), "python buttons panel");
@ -267,6 +282,11 @@ static StructRNA *rna_Panel_register(
else
BLI_addtail(&art->paneltypes, pt);
if (parent) {
pt->parent = parent;
BLI_addtail(&parent->children, BLI_genericNodeN(pt));
}
{
const char *owner_id = RNA_struct_state_owner_get();
if (owner_id) {
@ -1102,6 +1122,11 @@ static void rna_def_panel(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG);
RNA_def_property_ui_text(prop, "Options", "Options for this panel type");
prop = RNA_def_property(srna, "bl_parent_id", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->parent_id");
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_ui_text(prop, "Parent ID Name", "If this is set, the panel becomes a subpanel");
prop = RNA_def_property(srna, "use_pin", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PNL_PIN);
RNA_def_property_ui_text(prop, "Pin", "");

View File

@ -902,6 +902,10 @@ static void rna_def_userdef_theme_ui_panel(BlenderRNA *brna)
prop = RNA_def_property(srna, "back", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_ui_text(prop, "Background", "");
RNA_def_property_update(prop, 0, "rna_userdef_update");
prop = RNA_def_property(srna, "sub_back", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_ui_text(prop, "Sub Background", "");
RNA_def_property_update(prop, 0, "rna_userdef_update");
prop = RNA_def_property(srna, "show_header", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_ui_text(prop, "Show Header", "");