diff options
-rw-r--r-- | source/blender/blenkernel/BKE_screen.h | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/screen.c | 4 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 3 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 4 | ||||
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 18 | ||||
-rw-r--r-- | source/blender/editors/interface/interface.c | 2 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_handlers.c | 19 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_intern.h | 11 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_panel.c | 526 | ||||
-rw-r--r-- | source/blender/editors/screen/area.c | 52 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_screen_types.h | 42 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_ui.c | 11 |
12 files changed, 659 insertions, 34 deletions
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 9702b2ee30f..2aaf9198af0 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -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; diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 5b1bb7854a5..5bfd5e67eb1 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -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 */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index e5304c7e9d8..ac34f81907a 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -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; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 398847348cc..be81f355e26 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -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); } diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index e931aad2722..b1fa36c8e3a 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -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 diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 0b86743b622..b57b293e9ef 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -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) { diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 61761d7db41..f0d064a7a77 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -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) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index cbc1cd2cbbd..13998e03a24 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -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); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 9759614dd4c..be6cbaf7b72 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -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 diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index b963d2a59f5..7b758122ac1 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -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); diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 6b90557113a..005b0bda041 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -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 diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 39f305f7d47..a82763994f8 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -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) |