UI: Tabs categories for panels (D75)

- works by defining panel categories, currently restricted to the toolbar.
- no panels define bl_categories yet, so no user visible changes since tabs only show when there are multiple.
- panel pinning is available in rmb menu or alt+lmb.
This commit is contained in:
Campbell Barton 2013-12-17 03:21:55 +11:00
parent 2231b565f0
commit a621d1e488
Notes: blender-bot 2024-04-11 14:26:06 +02:00
Referenced by issue #37601, Implement Vertical Tabs for Toolbars and other regions
12 changed files with 659 additions and 34 deletions

View File

@ -171,6 +171,7 @@ typedef struct PanelType {
char label[BKE_ST_MAXNAME]; /* for panel header */
char translation_context[BKE_ST_MAXNAME];
char context[BKE_ST_MAXNAME]; /* for buttons window */
char category[BKE_ST_MAXNAME]; /* for category tabs */
int space_type;
int region_type;

View File

@ -173,6 +173,8 @@ ARegion *BKE_area_region_copy(SpaceType *st, ARegion *ar)
newar->prev = newar->next = NULL;
newar->handlers.first = newar->handlers.last = NULL;
newar->uiblocks.first = newar->uiblocks.last = NULL;
newar->panels_category.first = newar->panels_category.last = NULL;
newar->panels_category_active.first = newar->panels_category_active.last = NULL;
newar->ui_lists.first = newar->ui_lists.last = NULL;
newar->swinid = 0;
@ -306,6 +308,8 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar)
}
}
BLI_freelistN(&ar->ui_lists);
BLI_freelistN(&ar->panels_category);
BLI_freelistN(&ar->panels_category_active);
}
/* not area itself */

View File

@ -6125,6 +6125,8 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype)
pa->type = NULL;
}
link_list(fd, &ar->panels_category_active);
link_list(fd, &ar->ui_lists);
for (ui_list = ar->ui_lists.first; ui_list; ui_list = ui_list->next) {
@ -6161,6 +6163,7 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype)
ar->v2d.tab_num = 0;
ar->v2d.tab_cur = 0;
ar->v2d.sms = NULL;
ar->panels_category.first = ar->panels_category.last = NULL;
ar->handlers.first = ar->handlers.last = NULL;
ar->uiblocks.first = ar->uiblocks.last = NULL;
ar->headerstr = NULL;

View File

@ -2488,6 +2488,7 @@ static void write_screens(WriteData *wd, ListBase *scrbase)
SpaceLink *sl;
Panel *pa;
uiList *ui_list;
PanelCategoryStack *pc_act;
ARegion *ar;
writestruct(wd, DATA, "ScrArea", 1, sa);
@ -2498,6 +2499,9 @@ static void write_screens(WriteData *wd, ListBase *scrbase)
for (pa= ar->panels.first; pa; pa= pa->next)
writestruct(wd, DATA, "Panel", 1, pa);
for (pc_act = ar->panels_category_active.first; pc_act; pc_act = pc_act->next)
writestruct(wd, DATA, "PanelCategoryStack", 1, pc_act);
for (ui_list = ar->ui_lists.first; ui_list; ui_list = ui_list->next)
write_uilist(wd, ui_list);
}

View File

@ -45,6 +45,7 @@ struct ListBase;
struct ARegion;
struct ARegionType;
struct ScrArea;
struct wmEvent;
struct wmWindow;
struct wmWindowManager;
struct wmOperator;
@ -176,6 +177,8 @@ enum {
#define UI_PANEL_WIDTH 340
#define UI_COMPACT_PANEL_WIDTH 160
#define UI_PANEL_CATEGORY_MARGIN_WIDTH (U.widget_unit * 0.9f)
/* but->drawflag - these flags should only affect how the button is drawn. */
/* Note: currently, these flags _are not passed_ to the widget's state() or draw() functions
* (except for the 'align' ones)!
@ -665,10 +668,23 @@ void uiBeginPanels(const struct bContext *C, struct ARegion *ar);
void uiEndPanels(const struct bContext *C, struct ARegion *ar, int *x, int *y);
void uiDrawPanels(const struct bContext *C, struct ARegion *ar);
struct Panel *uiBeginPanel(struct ScrArea *sa, struct ARegion *ar, uiBlock *block, struct PanelType *pt, int *open);
struct Panel *uiPanelFindByType(struct ARegion *ar, struct PanelType *pt);
struct Panel *uiBeginPanel(struct ScrArea *sa, struct ARegion *ar, uiBlock *block,
struct PanelType *pt, struct Panel *pa, bool *r_open);
void uiEndPanel(uiBlock *block, int width, int height);
void uiScalePanels(struct ARegion *ar, float new_width);
bool UI_panel_category_is_visible(struct ARegion *ar);
void UI_panel_category_add(struct ARegion *ar, const char *name);
struct PanelCategoryDyn *UI_panel_category_find(struct ARegion *ar, const char *idname);
struct PanelCategoryStack *UI_panel_category_active_find(struct ARegion *ar, const char *idname);
const char *UI_panel_category_active_get(struct ARegion *ar, bool set_fallback);
void UI_panel_category_active_set(struct ARegion *ar, const char *idname);
struct PanelCategoryDyn *UI_panel_category_find_mouse_over_ex(struct ARegion *ar, const int x, const int y);
struct PanelCategoryDyn *UI_panel_category_find_mouse_over(struct ARegion *ar, const struct wmEvent *event);
void UI_panel_category_clear_all(struct ARegion *ar);
void UI_panel_category_draw_all(struct ARegion *ar, const char *category_id_active);
/* Handlers
*
* Handlers that can be registered in regions, areas and windows for

View File

@ -1209,7 +1209,7 @@ void uiDrawBlock(const bContext *C, uiBlock *block)
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_draw_aligned_panel(&style, block, &rect, UI_panel_category_is_visible(ar));
/* widgets */
for (but = block->buttons.first; but; but = but->next) {

View File

@ -5272,6 +5272,25 @@ static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
uiPupBlock(C, menu_add_shortcut, but);
}
/**
* menu to chow when right clicking on the panel header
*/
void ui_panel_menu(bContext *C, ARegion *ar, Panel *pa)
{
bScreen *sc = CTX_wm_screen(C);
PointerRNA ptr;
uiPopupMenu *pup;
uiLayout *layout;
RNA_pointer_create(&sc->id, &RNA_Panel, pa, &ptr);
pup = uiPupMenuBegin(C, IFACE_("Panel"), ICON_NONE);
layout = uiPupMenuLayout(pup);
if (UI_panel_category_is_visible(ar)) {
uiItemR(layout, &ptr, "use_pin", 0, "Pin" UI_SEP_CHAR_S "Alt+Left Mouse", ICON_NONE);
}
uiPupMenuEnd(C, pup);
}
static bool ui_but_menu(bContext *C, uiBut *but)
{

View File

@ -124,14 +124,6 @@ enum {
#define PNL_GRID (UI_UNIT_Y / 5) /* 4 default */
#define PNL_HEADER (UI_UNIT_Y + 4) /* 24 default */
/* panel->flag */
#define PNL_SELECT 1
#define PNL_CLOSEDX 2
#define PNL_CLOSEDY 4
#define PNL_CLOSED 6
/*#define PNL_TABBED 8*/ /*UNUSED*/
#define PNL_OVERLAP 16
/* Button text selection:
* extension direction, selextend, inside ui_do_but_TEX */
#define EXTEND_LEFT 1
@ -505,7 +497,7 @@ struct AutoComplete;
/* interface_panel.c */
extern int ui_handler_panel_region(struct bContext *C, const struct wmEvent *event, struct ARegion *ar);
extern void ui_draw_aligned_panel(struct uiStyle *style, uiBlock *block, const rcti *rect);
extern void ui_draw_aligned_panel(struct uiStyle *style, uiBlock *block, const rcti *rect, const bool show_pin);
/* interface_draw.c */
extern void ui_dropshadow(const rctf *rct, float radius, float aspect, float alpha, int select);
@ -531,6 +523,7 @@ extern bool ui_button_is_active(struct ARegion *ar);
extern int ui_button_open_menu_direction(uiBut *but);
extern void ui_button_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but, int restore);
void ui_button_clipboard_free(void);
void ui_panel_menu(struct bContext *C, ARegion *ar, Panel *pa);
/* interface_widgets.c */
void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3);

View File

@ -41,6 +41,7 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
#include "BLI_utildefines.h"
#include "BLF_translation.h"
@ -53,12 +54,15 @@
#include "BIF_gl.h"
#include "BIF_glutil.h"
#include "BLF_api.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_screen.h"
#include "UI_interface.h"
#include "UI_interface_icons.h"
#include "UI_resources.h"
#include "interface_intern.h"
@ -75,6 +79,9 @@
#define PNL_NEW_ADDED 16
#define PNL_FIRST 32
/* only show pin header button for pinned panels */
#define USE_PIN_HIDDEN
typedef enum uiHandlePanelState {
PANEL_STATE_DRAG,
PANEL_STATE_DRAG_SCALE,
@ -188,23 +195,36 @@ static void ui_panel_copy_offset(Panel *pa, Panel *papar)
pa->ofsy = papar->ofsy + papar->sizey - pa->sizey;
}
Panel *uiBeginPanel(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, int *open)
Panel *uiPanelFindByType(ARegion *ar, PanelType *pt)
{
Panel *pa, *patab, *palast, *panext;
Panel *pa;
const char *idname = pt->idname;
const char *tabname = pt->idname;
for (pa = ar->panels.first; pa; pa = pa->next) {
if (STREQLEN(pa->panelname, idname, UI_MAX_NAME_STR)) {
if (STREQLEN(pa->tabname, tabname, UI_MAX_NAME_STR)) {
return pa;
}
}
}
return NULL;
}
/**
* \note \a pa should be return value from #uiPanelFindByType and can be NULL.
*/
Panel *uiBeginPanel(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, Panel *pa, bool *r_open)
{
Panel *patab, *palast, *panext;
const char *drawname = CTX_IFACE_(pt->translation_context, pt->label);
char *idname = pt->idname;
char *tabname = pt->idname;
char *hookname = NULL;
int newpanel;
const bool newpanel = (pa == NULL);
int align = panel_aligned(sa, ar);
/* check if Panel exists, then use that one */
for (pa = ar->panels.first; pa; pa = pa->next)
if (strncmp(pa->panelname, idname, UI_MAX_NAME_STR) == 0)
if (strncmp(pa->tabname, tabname, UI_MAX_NAME_STR) == 0)
break;
newpanel = (pa == NULL);
if (!newpanel) {
pa->type = pt;
@ -279,12 +299,12 @@ Panel *uiBeginPanel(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, int
block->panel = pa;
pa->runtime_flag |= PNL_ACTIVE | PNL_LAST_ADDED;
*open = 0;
*r_open = false;
if (pa->paneltab) return pa;
if (pa->flag & PNL_CLOSED) return pa;
*open = 1;
*r_open = true;
return pa;
}
@ -497,7 +517,7 @@ static void ui_draw_aligned_panel_header(uiStyle *style, uiBlock *block, const r
}
/* panel integrated in buttonswindow, tool/property lists etc */
void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect)
void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, const bool show_pin)
{
Panel *panel = block->panel;
rcti headrect;
@ -542,6 +562,21 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect)
glDisable(GL_BLEND);
}
/* draw optional pin icon */
#ifdef USE_PIN_HIDDEN
if (show_pin && (block->panel->flag & PNL_PIN))
#else
if (show_pin)
#endif
{
glEnable(GL_BLEND);
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 / U.pixelsize, 1.0f);
glDisable(GL_BLEND);
}
/* horizontal title */
if (!(panel->flag & PNL_CLOSEDX)) {
ui_draw_aligned_panel_header(style, block, &headrect, 'h');
@ -710,6 +745,7 @@ static int uiAlignPanelStep(ScrArea *sa, ARegion *ar, float fac, int drag)
PanelSort *ps, *panelsort, *psnext;
int a, tot = 0, done;
int align = panel_aligned(sa, ar);
bool has_category_tabs = UI_panel_category_is_visible(ar);
/* count active, not tabbed panels */
for (pa = ar->panels.first; pa; pa = pa->next)
@ -759,6 +795,12 @@ static int uiAlignPanelStep(ScrArea *sa, ARegion *ar, float fac, int drag)
ps->pa->ofsx = 0;
ps->pa->ofsy = -get_panel_size_y(ps->pa);
if (has_category_tabs) {
if (align == BUT_VERTICAL) {
ps->pa->ofsx += UI_PANEL_CATEGORY_MARGIN_WIDTH;
}
}
for (a = 0; a < tot - 1; a++, ps++) {
psnext = ps + 1;
@ -1047,13 +1089,32 @@ static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
/* this function is supposed to call general window drawing too */
/* also it supposes a block has panel, and isn't a menu */
static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, int my, int event, int ctrl)
static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, int my, int event, short ctrl, short alt)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
Panel *pa;
#ifdef USE_PIN_HIDDEN
const bool show_pin = UI_panel_category_is_visible(ar) && (block->panel->flag & PNL_PIN);
#else
const bool show_pin = UI_panel_category_is_visible(ar);
#endif
int align = panel_aligned(sa, ar), button = 0;
rctf rect_drag, rect_pin;
float rect_leftmost;
/* drag and pin rect's */
rect_drag = block->rect;
rect_drag.xmin = block->rect.xmax - (PNL_ICON * 1.5f);
rect_pin = rect_drag;
if (show_pin) {
BLI_rctf_translate(&rect_pin, -PNL_ICON, 0.0f);
}
rect_leftmost = rect_pin.xmin;
/* mouse coordinates in panel space! */
/* XXX weak code, currently it assumes layout style for location of widgets */
@ -1063,6 +1124,10 @@ static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, in
button = 1;
else if (event == AKEY)
button = 1;
else if (ELEM3(event, 0, RETKEY, LEFTMOUSE) && alt) {
block->panel->flag ^= PNL_PIN;
button = 2;
}
else if (block->panel->flag & PNL_CLOSEDX) {
if (my >= block->rect.ymax) button = 1;
}
@ -1071,7 +1136,7 @@ static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, in
if (mx <= block->rect.xmax - 8 - PNL_ICON) button = 2;
//else if (mx <= block->rect.xmin + 10 + 2 * PNL_ICON + 2) button = 1;
}
else if (mx <= block->rect.xmax - PNL_ICON - 12) {
else if (mx < rect_leftmost) {
button = 1;
}
@ -1112,9 +1177,402 @@ static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, in
else
ED_region_tag_redraw(ar);
}
else if (mx <= (block->rect.xmax - PNL_ICON - 12) + PNL_ICON + 2) {
else if (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)) {
block->panel->flag ^= PNL_PIN;
ED_region_tag_redraw(ar);
}
}
bool UI_panel_category_is_visible(ARegion *ar)
{
/* more then one */
return ar->panels_category.first && ar->panels_category.first != ar->panels_category.last;
}
PanelCategoryDyn *UI_panel_category_find(ARegion *ar, const char *idname)
{
return BLI_findstring(&ar->panels_category, idname, offsetof(PanelCategoryDyn, idname));
}
PanelCategoryStack *UI_panel_category_active_find(ARegion *ar, const char *idname)
{
return BLI_findstring(&ar->panels_category_active, idname, offsetof(PanelCategoryStack, idname));
}
const char *UI_panel_category_active_get(ARegion *ar, bool set_fallback)
{
PanelCategoryStack *pc_act;
for (pc_act = ar->panels_category_active.first; pc_act; pc_act = pc_act->next) {
if (UI_panel_category_find(ar, pc_act->idname)) {
return pc_act->idname;
}
}
if (set_fallback) {
PanelCategoryDyn *pc_dyn = ar->panels_category.first;
if (pc_dyn) {
UI_panel_category_active_set(ar, pc_dyn->idname);
return pc_dyn->idname;
}
}
return NULL;
}
void UI_panel_category_active_set(ARegion *ar, const char *idname)
{
ListBase *lb = &ar->panels_category_active;
PanelCategoryStack *pc_act = UI_panel_category_active_find(ar, idname);
if (pc_act) {
BLI_remlink(lb, pc_act);
}
else {
pc_act = MEM_callocN(sizeof(PanelCategoryStack), __func__);
BLI_strncpy(pc_act->idname, idname, sizeof(pc_act->idname));
}
BLI_addhead(lb, pc_act);
/* validate all active panels, we could do this on load,
* they are harmless - but we should remove somewhere.
* (addons could define own and gather cruft over time) */
{
PanelCategoryStack *pc_act_next;
/* intentionally skip first */
pc_act_next = pc_act->next;
while ((pc_act = pc_act_next)) {
pc_act_next = pc_act->next;
if (!BLI_findstring(&ar->type->paneltypes, pc_act->idname, offsetof(PanelType, category))) {
BLI_remlink(lb, pc_act);
}
}
}
}
PanelCategoryDyn *UI_panel_category_find_mouse_over_ex(ARegion *ar, const int x, const int y)
{
PanelCategoryDyn *ptd;
for (ptd = ar->panels_category.first; ptd; ptd = ptd->next) {
if (BLI_rcti_isect_pt(&ptd->rect, x, y)) {
return ptd;
}
}
return NULL;
}
PanelCategoryDyn *UI_panel_category_find_mouse_over(ARegion *ar, const wmEvent *event)
{
return UI_panel_category_find_mouse_over_ex(ar, event->mval[0], event->mval[1]);
}
void UI_panel_category_add(ARegion *ar, const char *name)
{
PanelCategoryDyn *pc_dyn = MEM_callocN(sizeof(*pc_dyn), __func__);
BLI_addtail(&ar->panels_category, pc_dyn);
BLI_strncpy(pc_dyn->idname, name, sizeof(pc_dyn->idname));
/* 'pc_dyn->rect' must be set on draw */
}
void UI_panel_category_clear_all(ARegion *ar)
{
BLI_freelistN(&ar->panels_category);
}
/* based on uiDrawBox, 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,
int roundboxtype,
const bool use_highlight, const bool use_shadow,
const unsigned char highlight_fade[3])
{
float vec[4][2] = {
{0.195, 0.02},
{0.55, 0.169},
{0.831, 0.45},
{0.98, 0.805}};
int a;
/* mult */
for (a = 0; a < 4; a++) {
mul_v2_fl(vec[a], rad);
}
(void)use_shadow;
(void)use_highlight;
glBegin(mode);
/* start with corner right-top */
if (use_highlight) {
if (roundboxtype & UI_CNR_TOP_RIGHT) {
glVertex2f(maxx, maxy - rad);
for (a = 0; a < 4; a++) {
glVertex2f(maxx - vec[a][1], maxy - rad + vec[a][0]);
}
glVertex2f(maxx - rad, maxy);
}
else {
glVertex2f(maxx, maxy);
}
/* corner left-top */
if (roundboxtype & UI_CNR_TOP_LEFT) {
glVertex2f(minx + rad, maxy);
for (a = 0; a < 4; a++) {
glVertex2f(minx + rad - vec[a][0], maxy - vec[a][1]);
}
glVertex2f(minx, maxy - rad);
}
else {
glVertex2f(minx, maxy);
}
}
if (use_highlight && !use_shadow) {
if (highlight_fade) {
glColor3ubv(highlight_fade);
}
glVertex2f(minx, miny + rad);
glEnd();
return;
}
/* corner left-bottom */
if (roundboxtype & UI_CNR_BOTTOM_LEFT) {
glVertex2f(minx, miny + rad);
for (a = 0; a < 4; a++) {
glVertex2f(minx + vec[a][1], miny + rad - vec[a][0]);
}
glVertex2f(minx + rad, miny);
}
else {
glVertex2f(minx, miny);
}
/* corner right-bottom */
if (roundboxtype & UI_CNR_BOTTOM_RIGHT) {
glVertex2f(maxx - rad, miny);
for (a = 0; a < 4; a++) {
glVertex2f(maxx - rad + vec[a][0], miny + vec[a][1]);
}
glVertex2f(maxx, miny + rad);
}
else {
glVertex2f(maxx, miny);
}
glEnd();
}
/**
* Draw vertical tabs on the left side of the region,
* one tab per category.
*/
void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active)
{
/* no tab outlines for */
#define USE_FLAT_INACTIVE
View2D *v2d = &ar->v2d;
uiStyle *style = UI_GetStyle();
const int fontid = style->widget.uifont_id;
PanelCategoryDyn *pc_dyn;
const float zoom = 1.0f / ((uiBlock *)ar->uiblocks.first)->aspect;
const int px = max_ii(1.0, (int)U.pixelsize + 0.5f);
const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom;
const float dpi_fac = UI_DPI_FAC;
const int tab_v_pad_text = ((px * 3) * dpi_fac) * zoom; /* pading of tabs around text */
const int tab_v_pad = (2 + (2 * px * dpi_fac)) * zoom; /* padding between tabs */
const float tab_curve_radius = (px * 2) * dpi_fac;
const int roundboxtype = UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT;
bool do_scaletabs = false;
#ifdef USE_FLAT_INACTIVE
bool is_active_prev = false;
#endif
float scaletabs = 1.0f;
/* same for all tabs */
const int rct_xmin = v2d->mask.xmin + (3 * px);
const int rct_xmax = v2d->mask.xmin + category_tabs_width;
const int text_v_ofs = (rct_xmax - rct_xmin) * 0.3f;
int y_ofs = tab_v_pad;
/* Primary theme colors */
unsigned char theme_col_back[4];
unsigned char theme_col_text[4];
unsigned char theme_col_text_hi[4];
/* Secondary theme colors */
unsigned char theme_col_tab_bg[4];
unsigned char theme_col_tab_inactive[4];
unsigned char theme_col_tab_outline[4];
unsigned char theme_col_tab_divider[4]; /* line that divides tabs from the main area */
unsigned char theme_col_tab_highlight[4];
unsigned char theme_col_tab_highlight_inactive[4];
UI_GetThemeColor4ubv(TH_BACK, theme_col_back);
UI_GetThemeColor4ubv(TH_TEXT, theme_col_text);
UI_GetThemeColor4ubv(TH_TEXT_HI, theme_col_text_hi);
blend_color_interpolate_byte(theme_col_tab_bg, theme_col_back, theme_col_text, 0.12f);
blend_color_interpolate_byte(theme_col_tab_inactive, theme_col_back, theme_col_text, 0.1f);
blend_color_interpolate_byte(theme_col_tab_outline, theme_col_back, theme_col_text, 0.3f);
blend_color_interpolate_byte(theme_col_tab_divider, theme_col_back, theme_col_text, 0.3f);
blend_color_interpolate_byte(theme_col_tab_highlight, theme_col_back, theme_col_text_hi, 0.2f);
blend_color_interpolate_byte(theme_col_tab_highlight_inactive, theme_col_tab_inactive, theme_col_text_hi, 0.12f);
BLF_enable(fontid, BLF_ROTATION);
BLF_rotation(fontid, M_PI / 2);
//uiStyleFontSet(&style->widget);
BLF_size(fontid, (style->widget.points * U.pixelsize) * zoom, U.dpi);
BLI_assert(UI_panel_category_is_visible(ar));
/* calculate tab rect's and check if we need to scale down */
for (pc_dyn = ar->panels_category.first; pc_dyn; pc_dyn = pc_dyn->next) {
rcti *rct = &pc_dyn->rect;
const char *category_id = pc_dyn->idname;
const char *category_id_draw = IFACE_(category_id);
const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
rct->xmin = rct_xmin;
rct->xmax = rct_xmax;
rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2));
rct->ymax = v2d->mask.ymax - (y_ofs);
y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2);
}
if (y_ofs > BLI_rcti_size_y(&v2d->mask)) {
scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs;
for (pc_dyn = ar->panels_category.first; pc_dyn; pc_dyn = pc_dyn->next) {
rcti *rct = &pc_dyn->rect;
rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
}
do_scaletabs = true;
}
/* begin drawing */
glEnable(GL_LINE_SMOOTH);
/* draw the background */
glColor3ubv(theme_col_tab_bg);
glRecti(v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax);
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;
const char *category_id_draw = IFACE_(category_id);
int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2);
size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX;
// int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
const bool is_active = STREQ(category_id, category_id_active);
glEnable(GL_BLEND);
#ifdef USE_FLAT_INACTIVE
if (is_active)
#endif
{
glColor3ubv(is_active ? theme_col_back : 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);
/* tab outline */
glColor3ubv(theme_col_tab_outline);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
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);
/* tab highlight (3d look) */
glShadeModel(GL_SMOOTH);
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,
tab_curve_radius, roundboxtype, true, false,
is_active ? theme_col_back : theme_col_tab_inactive);
glShadeModel(GL_FLAT);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
/* 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);
}
if (do_scaletabs) {
category_draw_len = BLF_width_to_strlen(fontid, category_id_draw, category_draw_len,
category_width, NULL);
}
BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f);
glColor3ubv(theme_col_text);
BLF_draw(fontid, category_id_draw, category_draw_len);
glDisable(GL_BLEND);
/* tab blackline remaining (last tab) */
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);
}
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);
}
#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));
}
is_active_prev = is_active;
#endif
}
glDisable(GL_LINE_SMOOTH);
BLF_disable(fontid, BLF_ROTATION);
#undef USE_FLAT_INACTIVE
}
/* XXX should become modal keymap */
@ -1125,8 +1583,27 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar)
uiBlock *block;
Panel *pa;
int retval, mx, my;
bool has_category_tabs = UI_panel_category_is_visible(ar);
retval = WM_UI_HANDLER_CONTINUE;
if (has_category_tabs) {
if (event->val == KM_PRESS) {
if (event->type == LEFTMOUSE) {
PanelCategoryDyn *pc_dyn = UI_panel_category_find_mouse_over(ar, event);
if (pc_dyn) {
UI_panel_category_active_set(ar, pc_dyn->idname);
ED_region_tag_redraw(ar);
retval = WM_UI_HANDLER_BREAK;
}
}
}
}
if (retval == WM_UI_HANDLER_BREAK) {
return retval;
}
for (block = ar->uiblocks.last; block; block = block->prev) {
bool inside = false, inside_header = false, inside_scale = false;
@ -1171,10 +1648,10 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar)
if (pa->flag & PNL_CLOSEDY) {
if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my))
ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl);
ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->alt);
}
else
ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl);
ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->alt);
retval = WM_UI_HANDLER_BREAK;
continue;
@ -1192,7 +1669,7 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar)
/* open close on header */
if (ELEM(event->type, RETKEY, PADENTER)) {
if (inside_header) {
ui_handle_panel_header(C, block, mx, my, RETKEY, event->ctrl);
ui_handle_panel_header(C, block, mx, my, RETKEY, event->ctrl, event->alt);
retval = WM_UI_HANDLER_BREAK;
break;
}
@ -1202,7 +1679,7 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar)
retval = WM_UI_HANDLER_BREAK;
if (inside_header) {
ui_handle_panel_header(C, block, mx, my, 0, event->ctrl);
ui_handle_panel_header(C, block, mx, my, 0, event->ctrl, event->alt);
retval = WM_UI_HANDLER_BREAK;
break;
}
@ -1213,6 +1690,13 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar)
}
}
else if (event->type == RIGHTMOUSE) {
if (inside_header) {
ui_panel_menu(C, ar, block->panel);
retval = WM_UI_HANDLER_BREAK;
break;
}
}
else if (event->type == ESCKEY) {
/*XXX 2.50*/
#if 0

View File

@ -1568,11 +1568,17 @@ void ED_region_panels(const bContext *C, ARegion *ar, int vertical, const char *
Panel *panel;
View2D *v2d = &ar->v2d;
View2DScrollers *scrollers;
int x, y, xco, yco, w, em, triangle, open;
int x, y, xco, yco, w, em, triangle;
bool is_context_new = 0;
int redo;
int scroll;
bool use_category_tabs = (ar->regiontype == RGN_TYPE_TOOLS); /* XXX, should use some better check? */
/* offset panels for small vertical tab area */
const char *category = NULL;
const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH;
int margin_x = 0;
BLI_SMALLSTACK_DECLARE(pt_stack, PanelType *);
if (contextnr >= 0)
@ -1612,6 +1618,31 @@ void ED_region_panels(const bContext *C, ARegion *ar, int vertical, const char *
}
/* collect categories */
if (use_category_tabs) {
UI_panel_category_clear_all(ar);
/* gather unique categories */
BLI_SMALLSTACK_ITER_BEGIN(pt_stack, pt)
{
if (pt->category[0]) {
if (!UI_panel_category_find(ar, pt->category)) {
UI_panel_category_add(ar, pt->category);
}
}
}
BLI_SMALLSTACK_ITER_END;
if (!UI_panel_category_is_visible(ar)) {
use_category_tabs = false;
}
else {
category = UI_panel_category_active_get(ar, true);
margin_x = category_tabs_width;
}
}
/* sortof hack - but we cannot predict the height of panels, until it's being generated */
/* the layout engine works with fixed width (from v2d->cur), which is being set at end of the loop */
/* in case scroller settings (hide flags) differ from previous, the whole loop gets done again */
@ -1625,6 +1656,8 @@ void ED_region_panels(const bContext *C, ARegion *ar, int vertical, const char *
w = UI_PANEL_WIDTH;
em = (ar->type->prefsizex) ? 10 : 20;
}
w -= margin_x;
/* create panels */
uiBeginPanels(C, ar);
@ -1634,8 +1667,19 @@ void ED_region_panels(const bContext *C, ARegion *ar, int vertical, const char *
BLI_SMALLSTACK_ITER_BEGIN(pt_stack, pt)
{
bool open;
panel = uiPanelFindByType(ar, pt);
if (pt->category[0] && !STREQ(category, pt->category)) {
if ((panel == NULL) || ((panel->flag & PNL_PIN) == 0)) {
continue;
}
}
/* draw panel */
block = uiBeginBlock(C, ar, pt->idname, UI_EMBOSS);
panel = uiBeginPanel(sa, ar, block, pt, &open);
panel = uiBeginPanel(sa, ar, block, pt, panel, &open);
/* bad fixed values */
triangle = (int)(UI_UNIT_Y * 1.1f);
@ -1745,6 +1789,10 @@ void ED_region_panels(const bContext *C, ARegion *ar, int vertical, const char *
/* restore view matrix */
UI_view2d_view_restore(C);
if (use_category_tabs) {
UI_panel_category_draw_all(ar, category);
}
/* scrollers */
scrollers = UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
UI_view2d_scrollers_draw(C, v2d, scrollers);

View File

@ -112,6 +112,36 @@ typedef struct Panel { /* the part from uiBlock that needs saved in file */
void *activedata; /* runtime for panel manipulation */
} Panel;
/* Notes on Panel Catogories:
*
* ar->panels_category (PanelCategoryDyn) is a runtime only list of categories collected during draw.
*
* ar->panels_category_active (PanelCategoryStack) is basically a list of strings (category id's).
*
* Clicking on a tab moves it to the front of ar->panels_category_active,
* If the context changes so this tab is no longer displayed,
* then the first-most tab in ar->panels_category_active is used.
*
* This way you can change modes and always have the tab you last clicked on.
*/
/* region level tabs */
#
#
typedef struct PanelCategoryDyn {
struct PanelCategoryDyn *next, *prev;
char idname[64];
rcti rect;
} PanelCategoryDyn;
/* region stack of active tabs */
typedef struct PanelCategoryStack {
struct PanelCategoryStack *next, *prev;
char idname[64];
} PanelCategoryStack;
/* uiList dynamic data... */
/* These two Lines with # tell makesdna this struct can be excluded. */
#
@ -209,8 +239,10 @@ typedef struct ARegion {
ListBase uiblocks; /* uiBlock */
ListBase panels; /* Panel */
ListBase panels_category_active; /* Stack of panel categories */
ListBase ui_lists; /* uiList */
ListBase handlers; /* wmEventHandler */
ListBase panels_category; /* Panel categories runtime */
struct wmTimer *regiontimer; /* blend in/out */
@ -244,6 +276,16 @@ typedef struct ARegion {
#define SCREENNORMAL 0
#define SCREENFULL 1
/* Panel->flag */
enum {
PNL_SELECT = (1 << 0),
PNL_CLOSEDX = (1 << 1),
PNL_CLOSEDY = (1 << 2),
PNL_CLOSED = (PNL_CLOSEDX | PNL_CLOSEDY),
/*PNL_TABBED = (1 << 3), */ /*UNUSED*/
PNL_OVERLAP = (1 << 4),
PNL_PIN = (1 << 5),
};
/* Panel->snap - for snapping to screen edges */
#define PNL_SNAP_NONE 0

View File

@ -980,6 +980,11 @@ static void rna_def_panel(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_define_verify_sdna(TRUE);
prop = RNA_def_property(srna, "bl_category", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->category");
RNA_def_property_string_default(prop, BLF_I18NCONTEXT_DEFAULT_BPYRNA);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
prop = RNA_def_property(srna, "bl_space_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->space_type");
RNA_def_property_enum_items(prop, space_type_items);
@ -1004,6 +1009,12 @@ static void rna_def_panel(BlenderRNA *brna)
RNA_def_property_enum_items(prop, panel_flag_items);
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, "use_pin", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PNL_PIN);
RNA_def_property_ui_text(prop, "Pin", "");
/* XXX, should only tag region for redraw */
RNA_def_property_update(prop, NC_WINDOW, NULL);
}
static void rna_def_uilist(BlenderRNA *brna)