Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2013-12-16 20:21:55 +0400
committerCampbell Barton <ideasman42@gmail.com>2013-12-16 20:55:45 +0400
commita621d1e48828d02a5e097dcfc218cc11d149e75f (patch)
tree3cb9594e0b70fe0ac46d7fc22c05b1ec9b977c72
parent2231b565f011e984d4ee6eeb33a80567b404e032 (diff)
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.
-rw-r--r--source/blender/blenkernel/BKE_screen.h1
-rw-r--r--source/blender/blenkernel/intern/screen.c4
-rw-r--r--source/blender/blenloader/intern/readfile.c3
-rw-r--r--source/blender/blenloader/intern/writefile.c4
-rw-r--r--source/blender/editors/include/UI_interface.h18
-rw-r--r--source/blender/editors/interface/interface.c2
-rw-r--r--source/blender/editors/interface/interface_handlers.c19
-rw-r--r--source/blender/editors/interface/interface_intern.h11
-rw-r--r--source/blender/editors/interface/interface_panel.c526
-rw-r--r--source/blender/editors/screen/area.c52
-rw-r--r--source/blender/makesdna/DNA_screen_types.h42
-rw-r--r--source/blender/makesrna/intern/rna_ui.c11
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)