diff options
author | Lukas Toenne <lukas.toenne@googlemail.com> | 2012-05-22 18:13:33 +0400 |
---|---|---|
committer | Lukas Toenne <lukas.toenne@googlemail.com> | 2012-05-22 18:13:33 +0400 |
commit | 53b01d90023a850b17ded5deb9cace354c8e298a (patch) | |
tree | ab8ce43ddf8046dc54e2cacc5ff46cbc5806910b /source/blender/editors/space_node | |
parent | 85923aff288da072750447b44e492ebe5c59bcce (diff) |
A number of new features for the node editor in general and the Frame node in particular.
For an detailed user-level description of new features see the following blogpost:
http://code.blender.org/index.php/2012/05/node-editing-tweaks/
TL;DR:
* Frame node gets more usable bounding-box behavior
* Node resizing has helpful mouse cursor indicators and works on all borders
* Node selection/active colors are themeable independently
* Customizable background colors for nodes (useful for frames visual
distinction).
Diffstat (limited to 'source/blender/editors/space_node')
-rw-r--r-- | source/blender/editors/space_node/drawnode.c | 204 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_buttons.c | 18 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_draw.c | 252 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_edit.c | 576 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_intern.h | 14 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_ops.c | 83 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_select.c | 121 | ||||
-rw-r--r-- | source/blender/editors/space_node/space_node.c | 13 |
8 files changed, 996 insertions, 285 deletions
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 528a48b60df..8b7e0dd0db1 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -53,6 +53,8 @@ #include "BKE_main.h" #include "BKE_node.h" +#include "BLF_api.h" + #include "NOD_composite.h" #include "NOD_shader.h" @@ -79,6 +81,9 @@ #include "node_intern.h" +// XXX interface.h +extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select); + /* ****************** SOCKET BUTTON DRAW FUNCTIONS ***************** */ static void node_sync_cb(bContext *UNUSED(C), void *snode_v, void *node_v) @@ -474,15 +479,21 @@ static int node_resize_area_default(bNode *node, int x, int y) rctf totr= node->totr; /* right part of node */ totr.xmin = node->totr.xmax-20.0f; - return BLI_in_rctf(&totr, x, y); + if (BLI_in_rctf(&totr, x, y)) + return NODE_RESIZE_RIGHT; + else + return 0; } else { - /* rect we're interested in is just the bottom right corner */ + const float size = 10.0f; rctf totr= node->totr; - /* bottom right corner */ - totr.xmin = totr.xmax-10.0f; - totr.ymax = totr.ymin+10.0f; - return BLI_in_rctf(&totr, x, y); + int dir = 0; + + if (x >= totr.xmax-size && x < totr.xmax && y >= totr.ymin && y < totr.ymax) + dir |= NODE_RESIZE_RIGHT; + if (x >= totr.xmin && x < totr.xmin+size && y >= totr.ymin && y < totr.ymax) + dir |= NODE_RESIZE_LEFT; + return dir; } } @@ -511,7 +522,7 @@ static void node_update_group(const bContext *C, bNodeTree *ntree, bNode *gnode) int dy; /* get "global" coords */ - nodeSpaceCoords(gnode, &locx, &locy); + nodeToView(gnode, 0.0f, 0.0f, &locx, &locy); /* center them, is a bit of abuse of locx and locy though */ node_update_nodetree(C, ngroup, locx, locy); @@ -906,20 +917,166 @@ static void node_common_buts_whileloop(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "max_iterations", 0, NULL, 0); } -static void node_update_frame(const bContext *UNUSED(C), bNodeTree *UNUSED(ntree), bNode *node) +/* XXX Does a bounding box update by iterating over all children. + * Not ideal to do this in every draw call, but doing as transform callback doesn't work, + * since the child node totr rects are not updated properly at that point. + */ +static void node_update_frame(const bContext *UNUSED(C), bNodeTree *ntree, bNode *node) +{ + const float margin = 30.0f; + NodeFrame *data = (NodeFrame *)node->storage; + int bbinit; + bNode *tnode; + rctf rect, noderect; + float xmax, ymax; + + /* init rect from current frame size */ + nodeToView(node, node->offsetx, node->offsety, &rect.xmin, &rect.ymax); + nodeToView(node, node->offsetx+node->width, node->offsety-node->height, &rect.xmax, &rect.ymin); + + /* frame can be resized manually only if shrinking is disabled or no children are attached */ + data->flag |= NODE_FRAME_RESIZEABLE; + /* for shrinking bbox, initialize the rect from first child node */ + bbinit = (data->flag & NODE_FRAME_SHRINK); + /* fit bounding box to all children */ + for (tnode=ntree->nodes.first; tnode; tnode=tnode->next) { + if (tnode->parent!=node) + continue; + + /* add margin to node rect */ + noderect = tnode->totr; + noderect.xmin -= margin; + noderect.xmax += margin; + noderect.ymin -= margin; + noderect.ymax += margin; + + /* first child initializes frame */ + if (bbinit) { + bbinit = 0; + rect = noderect; + data->flag &= ~NODE_FRAME_RESIZEABLE; + } + else + BLI_union_rctf(&rect, &noderect); + } + + /* now adjust the frame size from view-space bounding box */ + nodeFromView(node, rect.xmin, rect.ymax, &node->offsetx, &node->offsety); + nodeFromView(node, rect.xmax, rect.ymin, &xmax, &ymax); + node->width = xmax - node->offsetx; + node->height = -ymax + node->offsety; + + node->totr = rect; +} + +static void node_draw_frame_label(bNode *node) { - float locx, locy; + /* XXX font id is crap design */ + const int fontid = blf_mono_font; + NodeFrame *data = (NodeFrame *)node->storage; + rctf *rct= &node->totr; + int color_id= node_get_colorid(node); + char label[128]; + /* XXX a bit hacky, should use separate align values for x and y */ + float width, ascender; + float x, y; + + BLI_strncpy(label, nodeLabel(node), sizeof(label)); + BLF_size(fontid, data->label_size, U.dpi); + + /* title color */ + UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.8f, 10); + + width = BLF_width(fontid, label); + ascender = BLF_ascender(fontid); + + x = 0.5f*(rct->xmin + rct->xmax) - 0.5f*width; + y = rct->ymax - NODE_DYS - ascender; + + BLF_position(fontid, x, y, 0); + BLF_draw(fontid, label, BLF_DRAW_STR_DUMMY_MAX); +} + +static void node_draw_frame(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *UNUSED(ntree), bNode *node) +{ + rctf *rct= &node->totr; + int color_id= node_get_colorid(node); + + /* skip if out of view */ + if (node->totr.xmax < ar->v2d.cur.xmin || node->totr.xmin > ar->v2d.cur.xmax || + node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax) { + + uiEndBlock(C, node->block); + node->block= NULL; + return; + } + + /* shadow */ + node_draw_shadow(snode, node, BASIS_RAD); + + /* body */ + if (node->flag & NODE_CUSTOM_COLOR) + glColor3fv(node->color); + else + UI_ThemeColor4(TH_NODE); + glEnable(GL_BLEND); + uiSetRoundBox(UI_CNR_ALL); + uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD); + glDisable(GL_BLEND); - /* get "global" coords */ - nodeSpaceCoords(node, &locx, &locy); + /* outline active and selected emphasis */ + if ( node->flag & (NODE_ACTIVE|SELECT) ) { + glEnable(GL_BLEND); + glEnable( GL_LINE_SMOOTH ); + + if (node->flag & NODE_ACTIVE) + UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40); + else + UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40); + uiSetRoundBox(UI_CNR_ALL); + uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD); + + glDisable( GL_LINE_SMOOTH ); + glDisable(GL_BLEND); + } + + /* label */ + node_draw_frame_label(node); + + UI_ThemeClearColor(color_id); + + uiEndBlock(C, node->block); + uiDrawBlock(C, node->block); + node->block= NULL; +} - node->prvr.xmin = locx + NODE_DYS; - node->prvr.xmax = locx + node->width- NODE_DYS; +static int node_resize_area_frame(bNode *node, int x, int y) +{ + const float size = 10.0f; + NodeFrame *data = (NodeFrame *)node->storage; + rctf totr= node->totr; + int dir = 0; + + /* shrinking frame size is determined by child nodes */ + if (!(data->flag & NODE_FRAME_RESIZEABLE)) + return 0; + + if (x >= totr.xmax-size && x < totr.xmax && y >= totr.ymin && y < totr.ymax) + dir |= NODE_RESIZE_RIGHT; + if (x >= totr.xmin && x < totr.xmin+size && y >= totr.ymin && y < totr.ymax) + dir |= NODE_RESIZE_LEFT; + if (x >= totr.xmin && x < totr.xmax && y >= totr.ymax-size && y < totr.ymax) + dir |= NODE_RESIZE_TOP; + if (x >= totr.xmin && x < totr.xmax && y >= totr.ymin && y < totr.ymin+size) + dir |= NODE_RESIZE_BOTTOM; + + return dir; +} - node->totr.xmin = locx; - node->totr.xmax = locx + node->width; - node->totr.ymax = locy; - node->totr.ymin = locy - node->height; +static void node_buts_frame_details(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "label_size", 0, "Label Size", ICON_NONE); + uiItemR(layout, ptr, "shrink", 0, "Shrink", ICON_NONE); } static void node_common_set_butfunc(bNodeType *ntype) @@ -941,7 +1098,10 @@ static void node_common_set_butfunc(bNodeType *ntype) ntype->drawupdatefunc= node_update_group; break; case NODE_FRAME: + ntype->drawfunc= node_draw_frame; ntype->drawupdatefunc= node_update_frame; + ntype->uifuncbut= node_buts_frame_details; + ntype->resize_area_func= node_resize_area_frame; break; } } @@ -1136,7 +1296,6 @@ static void node_shader_buts_dynamic(uiLayout *layout, bContext *C, PointerRNA * /* only once called */ static void node_shader_set_butfunc(bNodeType *ntype) { - ntype->uifuncbut = NULL; switch (ntype->type) { /* case NODE_GROUP: note, typeinfo for group is generated... see "XXX ugly hack" */ @@ -1215,7 +1374,6 @@ static void node_shader_set_butfunc(bNodeType *ntype) ntype->uifunc= node_shader_buts_dynamic; break; } - if (ntype->uifuncbut == NULL) ntype->uifuncbut = ntype->uifunc; } /* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */ @@ -2233,7 +2391,6 @@ static void node_composit_buts_viewer_but(uiLayout *layout, bContext *UNUSED(C), /* only once called */ static void node_composit_set_butfunc(bNodeType *ntype) { - ntype->uifuncbut = NULL; switch (ntype->type) { /* case NODE_GROUP: note, typeinfo for group is generated... see "XXX ugly hack" */ @@ -2424,8 +2581,6 @@ static void node_composit_set_butfunc(bNodeType *ntype) default: ntype->uifunc= NULL; } - if (ntype->uifuncbut == NULL) ntype->uifuncbut = ntype->uifunc; - } /* ****************** BUTTON CALLBACKS FOR TEXTURE NODES ***************** */ @@ -2536,7 +2691,6 @@ static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), Poin /* only once called */ static void node_texture_set_butfunc(bNodeType *ntype) { - ntype->uifuncbut = NULL; if ( ntype->type >= TEX_NODE_PROC && ntype->type < TEX_NODE_PROC_MAX ) { ntype->uifunc = node_texture_buts_proc; } @@ -2580,10 +2734,6 @@ static void node_texture_set_butfunc(bNodeType *ntype) break; } } - - if (ntype->uifuncbut == NULL) { - ntype->uifuncbut = ntype->uifunc; - } } /* ******* init draw callbacks for all tree types, only called in usiblender.c, once ************* */ diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c index c92abf116c4..16810e0fdec 100644 --- a/source/blender/editors/space_node/node_buttons.c +++ b/source/blender/editors/space_node/node_buttons.c @@ -89,8 +89,8 @@ static void active_node_panel(const bContext *C, Panel *pa) SpaceNode *snode= CTX_wm_space_node(C); bNodeTree *ntree= (snode) ? snode->edittree : NULL; bNode *node = (ntree) ? nodeGetActive(ntree) : NULL; // xxx... for editing group nodes - uiLayout *layout; - PointerRNA ptr; + uiLayout *layout, *row, *col; + PointerRNA ptr, opptr; /* verify pointers, and create RNA pointer for the node */ if (ELEM(NULL, ntree, node)) @@ -113,6 +113,20 @@ static void active_node_panel(const bContext *C, Panel *pa) uiItemO(layout, NULL, 0, "NODE_OT_hide_socket_toggle"); uiItemS(layout); + row = uiLayoutRow(layout, 0); + uiItemM(row, (bContext *)C, "NODE_MT_node_color_presets", NULL, 0); + uiItemM(row, (bContext *)C, "NODE_MT_node_color_specials", "", ICON_DOWNARROW_HLT); + uiItemO(row, "", ICON_ZOOMIN, "node.node_color_preset_add"); + opptr = uiItemFullO(row, "node.node_color_preset_add", "", ICON_ZOOMOUT, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); + RNA_boolean_set(&opptr, "remove_active", 1); + + row = uiLayoutRow(layout, 0); + uiItemR(row, &ptr, "use_custom_color", UI_ITEM_R_ICON_ONLY, NULL, ICON_NONE); + col = uiLayoutColumn(row, 0); + if (!(node->flag & NODE_CUSTOM_COLOR)) + uiLayoutSetEnabled(col, 0); + uiItemR(col, &ptr, "color", 0, "", 0); + /* draw this node's settings */ if (node->typeinfo && node->typeinfo->uifuncbut) node->typeinfo->uifuncbut(layout, (bContext *)C, &ptr); diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index d93b1b1fcc8..7011d605a7e 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -167,6 +167,110 @@ void ED_node_generic_update(Main *bmain, bNodeTree *ntree, bNode *node) ntreeTexCheckCyclics(ntree); } +static int compare_nodes(bNode *a, bNode *b) +{ + bNode *parent; + /* These tell if either the node or any of the parent nodes is selected. + * A selected parent means an unselected node is also in foreground! + */ + int a_select=(a->flag & NODE_SELECT), b_select=(b->flag & NODE_SELECT); + int a_active=(a->flag & NODE_ACTIVE), b_active=(b->flag & NODE_ACTIVE); + + /* if one is an ancestor of the other */ + /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */ + for (parent = a->parent; parent; parent=parent->parent) { + /* if b is an ancestor, it is always behind a */ + if (parent==b) + return 1; + /* any selected ancestor moves the node forward */ + if (parent->flag & NODE_ACTIVE) + a_active = 1; + if (parent->flag & NODE_SELECT) + a_select = 1; + } + for (parent = b->parent; parent; parent=parent->parent) { + /* if a is an ancestor, it is always behind b */ + if (parent==a) + return 0; + /* any selected ancestor moves the node forward */ + if (parent->flag & NODE_ACTIVE) + b_active = 1; + if (parent->flag & NODE_SELECT) + b_select = 1; + } + + /* if one of the nodes is in the background and the other not */ + if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND)) + return 0; + else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND)) + return 1; + + /* if one has a higher selection state (active > selected > nothing) */ + if (!b_active && a_active) + return 1; + else if (!b_select && (a_active || a_select)) + return 1; + + return 0; +} + +/* Sorts nodes by selection: unselected nodes first, then selected, + * then the active node at the very end. Relative order is kept intact! + */ +void ED_node_sort(bNodeTree *ntree) +{ + /* merge sort is the algorithm of choice here */ + bNode *first_a, *first_b, *node_a, *node_b, *tmp; + int totnodes= BLI_countlist(&ntree->nodes); + int k, a, b; + + k = 1; + while (k < totnodes) { + first_a = first_b = ntree->nodes.first; + + do { + /* setup first_b pointer */ + for (b=0; b < k && first_b; ++b) { + first_b = first_b->next; + } + /* all batches merged? */ + if (first_b==NULL) + break; + + /* merge batches */ + node_a = first_a; + node_b = first_b; + a = b = 0; + while (a < k && b < k && node_b) { + if (compare_nodes(node_a, node_b)==0) { + node_a = node_a->next; + ++a; + } + else { + tmp = node_b; + node_b = node_b->next; + ++b; + BLI_remlink(&ntree->nodes, tmp); + BLI_insertlinkbefore(&ntree->nodes, node_a, tmp); + } + } + + /* setup first pointers for next batch */ + first_b = node_b; + for (; b < k; ++b) { + /* all nodes sorted? */ + if (first_b==NULL) + break; + first_b = first_b->next; + } + first_a = first_b; + } while (first_b); + + k = k << 1; + } +} + + static void do_node_internal_buttons(bContext *C, void *node_v, int event) { if (event==B_NODE_EXEC) { @@ -176,24 +280,6 @@ static void do_node_internal_buttons(bContext *C, void *node_v, int event) } } - -static void node_scaling_widget(int color_id, float aspect, float xmin, float ymin, float xmax, float ymax) -{ - float dx; - float dy; - - dx= 0.5f*(xmax-xmin); - dy= 0.5f*(ymax-ymin); - - UI_ThemeColorShade(color_id, +30); - fdrawline(xmin, ymin, xmax, ymax); - fdrawline(xmin+dx, ymin, xmax, ymax-dy); - - UI_ThemeColorShade(color_id, -10); - fdrawline(xmin, ymin+aspect, xmax, ymax+aspect); - fdrawline(xmin+dx, ymin+aspect, xmax, ymax-dy+aspect); -} - static void node_uiblocks_init(const bContext *C, bNodeTree *ntree) { bNode *node; @@ -223,7 +309,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) int buty; /* get "global" coords */ - nodeSpaceCoords(node, &locx, &locy); + nodeToView(node, 0.0f, 0.0f, &locx, &locy); dy= locy; /* header */ @@ -350,7 +436,7 @@ static void node_update_hidden(bNode *node) int totin=0, totout=0, tot; /* get "global" coords */ - nodeSpaceCoords(node, &locx, &locy); + nodeToView(node, 0.0f, 0.0f, &locx, &locy); /* calculate minimal radius */ for (nsock= node->inputs.first; nsock; nsock= nsock->next) @@ -410,7 +496,7 @@ void node_update_default(const bContext *C, bNodeTree *ntree, bNode *node) node_update_basis(C, ntree, node); } -static int node_get_colorid(bNode *node) +int node_get_colorid(bNode *node) { if (node->typeinfo->nclass==NODE_CLASS_INPUT) return TH_NODE_IN_OUT; @@ -573,6 +659,24 @@ static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_ WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, NULL); } +void node_draw_shadow(SpaceNode *snode, bNode *node, float radius) +{ + rctf *rct = &node->totr; + + uiSetRoundBox(UI_CNR_ALL); + if (node->parent==NULL) + ui_dropshadow(rct, radius, snode->aspect, node->flag & SELECT); + else { + const float margin = 3.0f; + + glColor4f(0.0f, 0.0f, 0.0f, 0.33f); + glEnable(GL_BLEND); + uiRoundBox(rct->xmin-margin, rct->ymin-margin, + rct->xmax+margin, rct->ymax+margin, radius+margin); + glDisable(GL_BLEND); + } +} + static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node) { bNodeSocket *sock; @@ -597,8 +701,8 @@ static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bN return; } - uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT); - ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT); + /* shadow */ + node_draw_shadow(snode, node, BASIS_RAD); /* header */ if (color_id==TH_NODE) @@ -644,7 +748,7 @@ static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bN /* title */ if (node->flag & SELECT) - UI_ThemeColor(TH_TEXT_HI); + UI_ThemeColor(TH_SELECT); else UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10); @@ -680,27 +784,27 @@ static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bN (int)(iconofs - rct->xmin-18.0f), NODE_DY, NULL, 0, 0, 0, 0, ""); /* body */ - UI_ThemeColor4(TH_NODE); + if (node->flag & NODE_CUSTOM_COLOR) + glColor3fv(node->color); + else + UI_ThemeColor4(TH_NODE); glEnable(GL_BLEND); - uiSetRoundBox(UI_CNR_BOTTOM_LEFT); + uiSetRoundBox(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT); uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD); glDisable(GL_BLEND); - /* scaling indicator */ - node_scaling_widget(TH_NODE, snode->aspect, rct->xmax-BASIS_RAD*snode->aspect, rct->ymin, rct->xmax, rct->ymin+BASIS_RAD*snode->aspect); - /* outline active and selected emphasis */ if ( node->flag & (NODE_ACTIVE|SELECT) ) { glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); - /* using different shades of TH_TEXT_HI for the empasis, like triangle */ - if ( node->flag & NODE_ACTIVE ) - UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40); - else - UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120); - uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT); // round all corners except lower right - uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD); - + + if (node->flag & NODE_ACTIVE) + UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40); + else + UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40); + uiSetRoundBox(UI_CNR_ALL); + uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD); + glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); } @@ -758,8 +862,7 @@ static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, b char showname[128]; /* 128 is used below */ /* shadow */ - uiSetRoundBox(UI_CNR_ALL); - ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT); + node_draw_shadow(snode, node, hiddenrad); /* body */ UI_ThemeColor(color_id); @@ -771,19 +874,20 @@ static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, b if ( node->flag & (NODE_ACTIVE|SELECT) ) { glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); - /* using different shades of TH_TEXT_HI for the empasis, like triangle */ - if ( node->flag & NODE_ACTIVE ) - UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40); - else - UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120); - uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad); + + if (node->flag & NODE_ACTIVE) + UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40); + else + UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40); + uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad); + glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); } /* title */ if (node->flag & SELECT) - UI_ThemeColor(TH_TEXT_HI); + UI_ThemeColor(TH_SELECT); else UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10); @@ -807,7 +911,7 @@ static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, b node_draw_mute_line(&ar->v2d, snode, node); if (node->flag & SELECT) - UI_ThemeColor(TH_TEXT_HI); + UI_ThemeColor(TH_SELECT); else UI_ThemeColor(TH_TEXT); @@ -848,6 +952,45 @@ static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, b node->block= NULL; } +int node_get_resize_cursor(int directions) +{ + if (directions==0) + return CURSOR_STD; + else if ((directions & ~(NODE_RESIZE_TOP|NODE_RESIZE_BOTTOM))==0) + return CURSOR_Y_MOVE; + else if ((directions & ~(NODE_RESIZE_RIGHT|NODE_RESIZE_LEFT))==0) + return CURSOR_X_MOVE; + else + return CURSOR_EDIT; +} + +void node_set_cursor(wmWindow *win, SpaceNode *snode) +{ + bNodeTree *ntree = snode->edittree; + bNode *node; + bNodeSocket *sock; + int cursor = CURSOR_STD; + + if (ntree) { + if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN|SOCK_OUT)) { + /* pass */ + } + else { + /* check nodes front to back */ + for (node=ntree->nodes.last; node; node=node->prev) { + if (BLI_in_rctf(&node->totr, snode->mx, snode->my)) + break; /* first hit on node stops */ + } + if (node) { + int dir = node->typeinfo->resize_area_func(node, snode->mx, snode->my); + cursor = node_get_resize_cursor(dir); + } + } + } + + WM_cursor_set(win, cursor); +} + void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node) { if (node->flag & NODE_HIDDEN) @@ -866,7 +1009,8 @@ void node_update_nodetree(const bContext *C, bNodeTree *ntree, float offsetx, fl { bNode *node; - for (node= ntree->nodes.first; node; node= node->next) { + /* update nodes front to back, so children sizes get updated before parents */ + for (node= ntree->nodes.last; node; node= node->prev) { /* XXX little hack */ node->locx += offsetx; node->locy += offsety; @@ -892,6 +1036,14 @@ void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeT if (ntree==NULL) return; /* groups... */ + /* draw background nodes, last nodes in front */ + for (a=0, node= ntree->nodes.first; node; node=node->next, a++) { + if (!(node->flag & NODE_BACKGROUND)) + continue; + node->nr= a; /* index of node in list, used for exec event code */ + node_draw(C, ar, snode, ntree, node); + } + /* node lines */ glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); @@ -900,8 +1052,10 @@ void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeT glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); - /* draw nodes, last nodes in front */ + /* draw foreground nodes, last nodes in front */ for (a=0, node= ntree->nodes.first; node; node=node->next, a++) { + if (node->flag & NODE_BACKGROUND) + continue; node->nr= a; /* index of node in list, used for exec event code */ node_draw(C, ar, snode, ntree, node); } diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 8847dc3650a..5000e0f2163 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -711,55 +711,13 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node) } } -static int inside_rctf(rctf *bounds, rctf *rect) +void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree)) { - return (bounds->xmin <= rect->xmin && - bounds->xmax >= rect->xmax && - bounds->ymin <= rect->ymin && - bounds->ymax >= rect->ymax); -} - -static void node_frame_attach_nodes(bNodeTree *UNUSED(ntree), bNode *frame) -{ - bNode *node; - - /* only check nodes on top of the frame for attaching */ - for (node=frame->next; node; node=node->next) { - if (node->parent==frame) { - /* detach nodes that went outside the frame */ - if (!inside_rctf(&frame->totr, &node->totr)) - nodeDetachNode(node); - } - else if (node->flag & NODE_SELECT && node->parent==NULL) { - /* attach selected, still unparented nodes */ - if (inside_rctf(&frame->totr, &node->totr)) - nodeAttachNode(node, frame); - } - } -} - -void ED_node_update_hierarchy(bContext *UNUSED(C), bNodeTree *ntree) -{ - bNode *node; - /* XXX This does not work due to layout functions relying on node->block, * which only exists during actual drawing. Can we rely on valid totr rects? */ /* make sure nodes have correct bounding boxes after transform */ -// node_update_nodetree(C, ntree, 0.0f, 0.0f); - - /* all selected nodes are re-parented */ - for (node=ntree->nodes.last; node; node=node->prev) { - if (node->flag & NODE_SELECT && node->parent) - nodeDetachNode(node); - } - - /* update higher Z-level nodes first */ - for (node=ntree->nodes.last; node; node=node->prev) { - /* XXX callback? */ - if (node->type==NODE_FRAME) - node_frame_attach_nodes(ntree, node); - } + /* node_update_nodetree(C, ntree, 0.0f, 0.0f); */ } /* ***************** generic operator functions for nodes ***************** */ @@ -1517,36 +1475,130 @@ void NODE_OT_backimage_sample(wmOperatorType *ot) typedef struct NodeSizeWidget { float mxstart, mystart; + float oldlocx, oldlocy; + float oldoffsetx, oldoffsety; float oldwidth, oldheight; float oldminiwidth; + int directions; } NodeSizeWidget; +static void node_resize_init(bContext *C, wmOperator *op, wmEvent *UNUSED(event), bNode *node, int dir) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data"); + + op->customdata= nsw; + nsw->mxstart= snode->mx; + nsw->mystart= snode->my; + + /* store old */ + nsw->oldlocx= node->locx; + nsw->oldlocy= node->locy; + nsw->oldoffsetx= node->offsetx; + nsw->oldoffsety= node->offsety; + nsw->oldwidth= node->width; + nsw->oldheight= node->height; + nsw->oldminiwidth= node->miniwidth; + nsw->directions = dir; + + WM_cursor_modal(CTX_wm_window(C), node_get_resize_cursor(dir)); + /* add modal handler */ + WM_event_add_modal_handler(C, op); +} + +static void node_resize_exit(bContext *C, wmOperator *op, int UNUSED(cancel)) +{ + WM_cursor_restore(CTX_wm_window(C)); + + MEM_freeN(op->customdata); + op->customdata= NULL; +} + static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event) { SpaceNode *snode= CTX_wm_space_node(C); ARegion *ar= CTX_wm_region(C); bNode *node= editnode_get_active(snode->edittree); NodeSizeWidget *nsw= op->customdata; - float mx, my; + float mx, my, dx, dy; switch (event->type) { case MOUSEMOVE: - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], - &mx, &my); + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &mx, &my); + dx = mx - nsw->mxstart; + dy = my - nsw->mystart; if (node) { if (node->flag & NODE_HIDDEN) { - node->miniwidth= nsw->oldminiwidth + mx - nsw->mxstart; - CLAMP(node->miniwidth, 0.0f, 100.0f); + float widthmin = 0.0f; + float widthmax = 100.0f; + if (nsw->directions & NODE_RESIZE_RIGHT) { + node->miniwidth= nsw->oldminiwidth + dx; + CLAMP(node->miniwidth, widthmin, widthmax); + } + if (nsw->directions & NODE_RESIZE_LEFT) { + float locmax = nsw->oldlocx + nsw->oldminiwidth; + + node->locx= nsw->oldlocx + dx; + CLAMP(node->locx, locmax - widthmax, locmax - widthmin); + node->miniwidth= locmax - node->locx; + } } else { - node->width= nsw->oldwidth + mx - nsw->mxstart; - CLAMP(node->width, UI_DPI_FAC*node->typeinfo->minwidth, UI_DPI_FAC*node->typeinfo->maxwidth); + float widthmin = UI_DPI_FAC*node->typeinfo->minwidth; + float widthmax = UI_DPI_FAC*node->typeinfo->maxwidth; + if (nsw->directions & NODE_RESIZE_RIGHT) { + node->width= nsw->oldwidth + dx; + CLAMP(node->width, widthmin, widthmax); + } + if (nsw->directions & NODE_RESIZE_LEFT) { + float locmax = nsw->oldlocx + nsw->oldwidth; + + node->locx= nsw->oldlocx + dx; + CLAMP(node->locx, locmax - widthmax, locmax - widthmin); + node->width= locmax - node->locx; + } } + /* height works the other way round ... */ - node->height= nsw->oldheight - my + nsw->mystart; - CLAMP(node->height, node->typeinfo->minheight, node->typeinfo->maxheight); + { + float heightmin = UI_DPI_FAC*node->typeinfo->minheight; + float heightmax = UI_DPI_FAC*node->typeinfo->maxheight; + if (nsw->directions & NODE_RESIZE_TOP) { + float locmin = nsw->oldlocy - nsw->oldheight; + + node->locy= nsw->oldlocy + dy; + CLAMP(node->locy, locmin + heightmin, locmin + heightmax); + node->height= node->locy - locmin; + } + if (nsw->directions & NODE_RESIZE_BOTTOM) { + node->height= nsw->oldheight - dy; + CLAMP(node->height, heightmin, heightmax); + } + } + + /* XXX make callback? */ + if (node->type == NODE_FRAME) { + /* keep the offset symmetric around center point */ + if (nsw->directions & NODE_RESIZE_LEFT) { + node->locx = nsw->oldlocx + 0.5f*dx; + node->offsetx = nsw->oldoffsetx + 0.5f*dx; + } + if (nsw->directions & NODE_RESIZE_RIGHT) { + node->locx = nsw->oldlocx + 0.5f*dx; + node->offsetx = nsw->oldoffsetx - 0.5f*dx; + } + if (nsw->directions & NODE_RESIZE_TOP) { + node->locy = nsw->oldlocy + 0.5f*dy; + node->offsety = nsw->oldoffsety + 0.5f*dy; + } + if (nsw->directions & NODE_RESIZE_BOTTOM) { + node->locy = nsw->oldlocy + 0.5f*dy; + node->offsety = nsw->oldoffsety - 0.5f*dy; + } + } } ED_region_tag_redraw(ar); @@ -1557,10 +1609,8 @@ static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event) case MIDDLEMOUSE: case RIGHTMOUSE: - MEM_freeN(nsw); - op->customdata= NULL; - - ED_node_update_hierarchy(C, snode->edittree); + node_resize_exit(C, op, 0); + ED_node_post_apply_transform(C, snode->edittree); return OPERATOR_FINISHED; } @@ -1573,37 +1623,24 @@ static int node_resize_invoke(bContext *C, wmOperator *op, wmEvent *event) SpaceNode *snode= CTX_wm_space_node(C); ARegion *ar= CTX_wm_region(C); bNode *node= editnode_get_active(snode->edittree); + int dir; if (node) { /* convert mouse coordinates to v2d space */ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], - &snode->mx, &snode->my); - - if (node->typeinfo->resize_area_func(node, snode->mx, snode->my)) { - NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data"); - - op->customdata= nsw; - nsw->mxstart= snode->mx; - nsw->mystart= snode->my; - - /* store old */ - nsw->oldwidth= node->width; - nsw->oldheight= node->height; - nsw->oldminiwidth= node->miniwidth; - - /* add modal handler */ - WM_event_add_modal_handler(C, op); - + &snode->mx, &snode->my); + dir = node->typeinfo->resize_area_func(node, snode->mx, snode->my); + if (dir != 0) { + node_resize_init(C, op, event, node, dir); return OPERATOR_RUNNING_MODAL; } } return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH; } -static int node_resize_cancel(bContext *UNUSED(C), wmOperator *op) +static int node_resize_cancel(bContext *C, wmOperator *op) { - MEM_freeN(op->customdata); - op->customdata= NULL; + node_resize_exit(C, op, 1); return OPERATOR_CANCELLED; } @@ -2180,6 +2217,27 @@ bNode *node_add_node(SpaceNode *snode, Main *bmain, Scene *scene, bNodeTemplate /* ****************** Duplicate *********************** */ +static void node_duplicate_reparent_recursive(bNode *node) +{ + bNode *parent; + + node->flag |= NODE_TEST; + + /* find first selected parent */ + for (parent=node->parent; parent; parent=parent->parent) { + if (parent->flag & SELECT) { + if (!(parent->flag & NODE_TEST)) + node_duplicate_reparent_recursive(parent); + break; + } + } + /* reparent node copy to parent copy */ + if (parent) { + nodeDetachNode(node->new_node); + nodeAttachNode(node->new_node, parent->new_node); + } +} + static int node_duplicate_exec(bContext *C, wmOperator *op) { SpaceNode *snode= CTX_wm_space_node(C); @@ -2242,6 +2300,19 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) break; } + /* clear flags for recursive depth-first iteration */ + for (node= ntree->nodes.first; node; node= node->next) + node->flag &= ~NODE_TEST; + /* reparent copied nodes */ + for (node= ntree->nodes.first; node; node= node->next) { + if ((node->flag & SELECT) && !(node->flag & NODE_TEST)) + node_duplicate_reparent_recursive(node); + + /* only has to check old nodes */ + if (node==lastnode) + break; + } + /* deselect old nodes, select the copies instead */ for (node= ntree->nodes.first; node; node= node->next) { if (node->flag & SELECT) { @@ -3782,3 +3853,354 @@ void NODE_OT_output_file_move_active_socket(wmOperatorType *ot) RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", ""); } + +/* ****************** Copy Node Color ******************* */ + +static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNode *node, *tnode; + + if (!ntree) + return OPERATOR_CANCELLED; + node = nodeGetActive(ntree); + if (!node) + return OPERATOR_CANCELLED; + + for (tnode=ntree->nodes.first; tnode; tnode=tnode->next) { + if (tnode->flag & NODE_SELECT && tnode != node) { + if (node->flag & NODE_CUSTOM_COLOR) { + tnode->flag |= NODE_CUSTOM_COLOR; + copy_v3_v3(tnode->color, node->color); + } + else + tnode->flag &= ~NODE_CUSTOM_COLOR; + } + } + + ED_node_sort(ntree); + WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL); + + return OPERATOR_FINISHED; +} + +void NODE_OT_node_copy_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Color"; + ot->description = "Copy color to all selected nodes"; + ot->idname = "NODE_OT_node_copy_color"; + + /* api callbacks */ + ot->exec = node_copy_color_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ****************** Set Parent ******************* */ + +static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNode *frame = nodeGetActive(ntree), *node; + if (!frame || frame->type != NODE_FRAME) + return OPERATOR_CANCELLED; + + for (node=ntree->nodes.first; node; node=node->next) { + if (node == frame) + continue; + if (node->flag & NODE_SELECT) { + nodeDetachNode(node); + nodeAttachNode(node, frame); + } + } + + ED_node_sort(ntree); + WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL); + + return OPERATOR_FINISHED; +} + +void NODE_OT_parent_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Make Parent"; + ot->description = "Attach selected nodes"; + ot->idname = "NODE_OT_parent_set"; + + /* api callbacks */ + ot->exec = node_parent_set_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ****************** Clear Parent ******************* */ + +static int node_parent_clear_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNode *node; + + for (node=ntree->nodes.first; node; node=node->next) { + if (node->flag & NODE_SELECT) { + nodeDetachNode(node); + } + } + + WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL); + + return OPERATOR_FINISHED; +} + +void NODE_OT_parent_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Parent"; + ot->description = "Detach selected nodes"; + ot->idname = "NODE_OT_parent_clear"; + + /* api callbacks */ + ot->exec = node_parent_clear_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ****************** Join Nodes ******************* */ + +/* tags for depth-first search */ +#define NODE_JOIN_DONE 1 +#define NODE_JOIN_IS_DESCENDANT 2 + +static void node_join_attach_recursive(bNode *node, bNode *frame) +{ + node->done |= NODE_JOIN_DONE; + + if (node == frame) { + node->done |= NODE_JOIN_IS_DESCENDANT; + } + else if (node->parent) { + /* call recursively */ + if (!(node->parent->done & NODE_JOIN_DONE)) + node_join_attach_recursive(node->parent, frame); + + /* in any case: if the parent is a descendant, so is the child */ + if (node->parent->done & NODE_JOIN_IS_DESCENDANT) + node->done |= NODE_JOIN_IS_DESCENDANT; + else if (node->flag & NODE_TEST) { + /* if parent is not an decendant of the frame, reattach the node */ + nodeDetachNode(node); + nodeAttachNode(node, frame); + node->done |= NODE_JOIN_IS_DESCENDANT; + } + } + else if (node->flag & NODE_TEST) { + nodeAttachNode(node, frame); + node->done |= NODE_JOIN_IS_DESCENDANT; + } +} + +static int node_join_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + bNodeTree *ntree = snode->edittree; + bNode *node, *frame; + bNodeTemplate ntemp; + + /* XXX save selection: node_add_node call below sets the new frame as single active+selected node */ + for (node=ntree->nodes.first; node; node=node->next) { + if (node->flag & NODE_SELECT) + node->flag |= NODE_TEST; + else + node->flag &= ~NODE_TEST; + } + + ntemp.main = bmain; + ntemp.scene = scene; + ntemp.type = NODE_FRAME; + frame = node_add_node(snode, bmain, scene, &ntemp, 0.0f, 0.0f); + + /* reset tags */ + for (node=ntree->nodes.first; node; node=node->next) + node->done = 0; + + for (node=ntree->nodes.first; node; node=node->next) { + if (!(node->done & NODE_JOIN_DONE)) + node_join_attach_recursive(node, frame); + } + + /* restore selection */ + for (node=ntree->nodes.first; node; node=node->next) { + if (node->flag & NODE_TEST) + node->flag |= NODE_SELECT; + } + + ED_node_sort(ntree); + WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL); + + return OPERATOR_FINISHED; +} + +void NODE_OT_join(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Join Nodes"; + ot->description = "Attaches selected nodes to a new common frame"; + ot->idname = "NODE_OT_join"; + + /* api callbacks */ + ot->exec = node_join_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ****************** Attach ******************* */ + +static int node_attach_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNode *frame; + + /* check nodes front to back */ + for (frame=ntree->nodes.last; frame; frame=frame->prev) { + /* skip selected, those are the nodes we want to attach */ + if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) + continue; + if (BLI_in_rctf(&frame->totr, snode->mx, snode->my)) + break; + } + if (frame) { + bNode *node, *parent; + for (node=ntree->nodes.last; node; node=node->prev) { + if (node->flag & NODE_SELECT) { + if (node->parent == NULL) { + /* attach all unparented nodes */ + nodeAttachNode(node, frame); + } + else { + /* attach nodes which share parent with the frame */ + for (parent=frame->parent; parent; parent=parent->parent) + if (parent == node->parent) + break; + if (parent) { + nodeDetachNode(node); + nodeAttachNode(node, frame); + } + } + } + } + } + + ED_node_sort(ntree); + WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL); + + return OPERATOR_FINISHED; +} + +static int node_attach_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + ARegion *ar= CTX_wm_region(C); + SpaceNode *snode= CTX_wm_space_node(C); + + /* convert mouse coordinates to v2d space */ + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &snode->mx, &snode->my); + + return node_attach_exec(C, op); +} + +void NODE_OT_attach(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Attach Nodes"; + ot->description = "Attaches active node to a frame"; + ot->idname = "NODE_OT_attach"; + + /* api callbacks */ + ot->exec = node_attach_exec; + ot->invoke = node_attach_invoke; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ****************** Detach ******************* */ + +/* tags for depth-first search */ +#define NODE_DETACH_DONE 1 +#define NODE_DETACH_IS_DESCENDANT 2 + +static void node_detach_recursive(bNode *node) +{ + node->done |= NODE_DETACH_DONE; + + if (node->parent) { + /* call recursively */ + if (!(node->parent->done & NODE_DETACH_DONE)) + node_detach_recursive(node->parent); + + /* in any case: if the parent is a descendant, so is the child */ + if (node->parent->done & NODE_DETACH_IS_DESCENDANT) + node->done |= NODE_DETACH_IS_DESCENDANT; + else if (node->flag & NODE_SELECT) { + /* if parent is not a decendant of a selected node, detach */ + nodeDetachNode(node); + node->done |= NODE_DETACH_IS_DESCENDANT; + } + } + else if (node->flag & NODE_SELECT) { + node->done |= NODE_DETACH_IS_DESCENDANT; + } +} + +/* detach the root nodes in the current selection */ +static int node_detach_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNode *node; + + /* reset tags */ + for (node=ntree->nodes.first; node; node=node->next) + node->done = 0; + /* detach nodes recursively + * relative order is preserved here! + */ + for (node=ntree->nodes.first; node; node=node->next) { + if (!(node->done & NODE_DETACH_DONE)) + node_detach_recursive(node); + } + + ED_node_sort(ntree); + WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL); + + return OPERATOR_FINISHED; +} + +void NODE_OT_detach(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Detach Nodes"; + ot->description = "Detaches selected nodes from parents"; + ot->idname = "NODE_OT_detach"; + + /* api callbacks */ + ot->exec = node_detach_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; +} diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 802c471c7aa..60177192e2a 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -40,7 +40,9 @@ struct ARegion; struct ARegionType; struct View2D; struct bContext; +struct wmWindow; struct wmWindowManager; +struct wmEvent; struct bNodeTemplate; struct bNode; struct bNodeSocket; @@ -67,13 +69,18 @@ ARegion *node_has_buttons_region(ScrArea *sa); void node_menus_register(void); /* node_draw.c */ +int node_get_colorid(struct bNode *node); void node_socket_circle_draw(struct bNodeTree *ntree, struct bNodeSocket *sock, float size); +int node_get_resize_cursor(int directions); +void node_draw_shadow(struct SpaceNode *snode, struct bNode *node, float radius); void node_draw_default(const struct bContext *C, struct ARegion *ar, struct SpaceNode *snode, struct bNodeTree *ntree, struct bNode *node); void node_update_default(const struct bContext *C, struct bNodeTree *ntree, struct bNode *node); void node_update_nodetree(const struct bContext *C, struct bNodeTree *ntree, float offsetx, float offsety); void node_draw_nodetree(const struct bContext *C, struct ARegion *ar, struct SpaceNode *snode, struct bNodeTree *ntree); void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d); +void node_set_cursor(struct wmWindow *win, struct SpaceNode *snode); + /* node_buttons.c */ void node_buttons_register(struct ARegionType *art); void NODE_OT_properties(struct wmOperatorType *ot); @@ -151,6 +158,7 @@ void NODE_OT_hide_toggle(struct wmOperatorType *ot); void NODE_OT_hide_socket_toggle(struct wmOperatorType *ot); void NODE_OT_preview_toggle(struct wmOperatorType *ot); void NODE_OT_options_toggle(struct wmOperatorType *ot); +void NODE_OT_node_copy_color(struct wmOperatorType *ot); void NODE_OT_show_cyclic_dependencies(struct wmOperatorType *ot); void NODE_OT_link_viewer(struct wmOperatorType *ot); @@ -170,6 +178,12 @@ void NODE_OT_output_file_add_socket(struct wmOperatorType *ot); void NODE_OT_output_file_remove_active_socket(struct wmOperatorType *ot); void NODE_OT_output_file_move_active_socket(struct wmOperatorType *ot); +void NODE_OT_parent_set(struct wmOperatorType *ot); +void NODE_OT_parent_clear(struct wmOperatorType *ot); +void NODE_OT_join(struct wmOperatorType *ot); +void NODE_OT_attach(struct wmOperatorType *ot); +void NODE_OT_detach(struct wmOperatorType *ot); + extern const char *node_context_dir[]; // XXXXXX diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index 5d586f08eb0..781a14fca59 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -68,6 +68,7 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_options_toggle); WM_operatortype_append(NODE_OT_hide_socket_toggle); WM_operatortype_append(NODE_OT_show_cyclic_dependencies); + WM_operatortype_append(NODE_OT_node_copy_color); WM_operatortype_append(NODE_OT_duplicate); WM_operatortype_append(NODE_OT_delete); @@ -104,6 +105,12 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_output_file_add_socket); WM_operatortype_append(NODE_OT_output_file_remove_active_socket); WM_operatortype_append(NODE_OT_output_file_move_active_socket); + + WM_operatortype_append(NODE_OT_parent_set); + WM_operatortype_append(NODE_OT_parent_clear); + WM_operatortype_append(NODE_OT_join); + WM_operatortype_append(NODE_OT_attach); + WM_operatortype_append(NODE_OT_detach); } void ED_operatormacros_node(void) @@ -111,10 +118,32 @@ void ED_operatormacros_node(void) wmOperatorType *ot; wmOperatorTypeMacro *mot; - ot = WM_operatortype_append_macro("NODE_OT_duplicate_move", "Duplicate", "Duplicate selected nodes and move them", + ot = WM_operatortype_append_macro("NODE_OT_select_link_viewer", "Link Viewer", + "Select node and link it to a viewer node", + OPTYPE_UNDO); + WM_operatortype_macro_define(ot, "NODE_OT_select"); + WM_operatortype_macro_define(ot, "NODE_OT_link_viewer"); + + ot = WM_operatortype_append_macro("NODE_OT_translate_attach", "Move and Attach", + "Move nodes and attach to frame", + OPTYPE_UNDO|OPTYPE_REGISTER); + mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(mot->ptr, "release_confirm", TRUE); + WM_operatortype_macro_define(ot, "NODE_OT_attach"); + + ot = WM_operatortype_append_macro("NODE_OT_detach_translate_attach", "Detach and Move", + "Detach nodes, move and attach to frame", + OPTYPE_UNDO|OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "NODE_OT_detach"); + mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(mot->ptr, "release_confirm", TRUE); + WM_operatortype_macro_define(ot, "NODE_OT_attach"); + + ot = WM_operatortype_append_macro("NODE_OT_duplicate_move", "Duplicate", + "Duplicate selected nodes and move them", OPTYPE_UNDO|OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "NODE_OT_duplicate"); - WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + WM_operatortype_macro_define(ot, "NODE_OT_translate_attach"); /* modified operator call for duplicating with input links */ ot = WM_operatortype_append_macro("NODE_OT_duplicate_move_keep_inputs", "Duplicate", @@ -122,12 +151,7 @@ void ED_operatormacros_node(void) OPTYPE_UNDO|OPTYPE_REGISTER); mot = WM_operatortype_macro_define(ot, "NODE_OT_duplicate"); RNA_boolean_set(mot->ptr, "keep_inputs", TRUE); - WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); - - ot = WM_operatortype_append_macro("NODE_OT_select_link_viewer", "Link Viewer", - "Select node and link it to a viewer node", OPTYPE_UNDO); - WM_operatortype_macro_define(ot, "NODE_OT_select"); - WM_operatortype_macro_define(ot, "NODE_OT_link_viewer"); + WM_operatortype_macro_define(ot, "NODE_OT_translate_attach"); ot = WM_operatortype_append_macro("NODE_OT_move_detach_links", "Detach", "Move a node to detach links", OPTYPE_UNDO|OPTYPE_REGISTER); @@ -137,8 +161,30 @@ void ED_operatormacros_node(void) ot = WM_operatortype_append_macro("NODE_OT_move_detach_links_release", "Detach", "Move a node to detach links", OPTYPE_UNDO|OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "NODE_OT_links_detach"); - mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); - RNA_boolean_set(mot->ptr, "release_confirm", TRUE); + mot = WM_operatortype_macro_define(ot, "NODE_OT_translate_attach"); +} + +/* helper function for repetitive select operator keymap */ +static void node_select_keymap(wmKeyMap *keymap, int extend) +{ + /* modifier combinations */ + const int mod_single[] = { 0, KM_CTRL, KM_ALT, KM_CTRL|KM_ALT, + -1 /* terminator */ + }; + const int mod_extend[] = { KM_SHIFT, KM_SHIFT|KM_CTRL, + KM_SHIFT|KM_ALT, KM_SHIFT|KM_CTRL|KM_ALT, + -1 /* terminator */ + }; + const int *mod = (extend ? mod_extend : mod_single); + wmKeyMapItem *kmi; + int i; + + for (i=0; mod[i] >= 0; ++i) { + kmi = WM_keymap_add_item(keymap, "NODE_OT_select", ACTIONMOUSE, KM_PRESS, mod[i], 0); + RNA_boolean_set(kmi->ptr, "extend", extend); + kmi = WM_keymap_add_item(keymap, "NODE_OT_select", SELECTMOUSE, KM_PRESS, mod[i], 0); + RNA_boolean_set(kmi->ptr, "extend", extend); + } } void node_keymap(struct wmKeyConfig *keyconf) @@ -157,15 +203,12 @@ void node_keymap(struct wmKeyConfig *keyconf) /* mouse select in nodes used to be both keys, but perhaps this should be reduced? * NOTE: mouse-clicks on left-mouse will fall through to allow transform-tweak, but also link/resize * NOTE 2: socket select is part of the node select operator, to handle overlapping cases + * NOTE 3: select op is registered for various combinations of modifier key, so the specialized + * grab operators (unlink, attach, etc.) can work easily on single nodes. */ - kmi = WM_keymap_add_item(keymap, "NODE_OT_select", ACTIONMOUSE, KM_PRESS, 0, 0); - RNA_boolean_set(kmi->ptr, "extend", FALSE); - kmi = WM_keymap_add_item(keymap, "NODE_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); - RNA_boolean_set(kmi->ptr, "extend", FALSE); - kmi = WM_keymap_add_item(keymap, "NODE_OT_select", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0); - RNA_boolean_set(kmi->ptr, "extend", TRUE); - kmi = WM_keymap_add_item(keymap, "NODE_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); - RNA_boolean_set(kmi->ptr, "extend", TRUE); + node_select_keymap(keymap, FALSE); + node_select_keymap(keymap, TRUE); + kmi = WM_keymap_add_item(keymap, "NODE_OT_select_border", EVT_TWEAK_S, KM_ANY, 0, 0); RNA_boolean_set(kmi->ptr, "tweak", TRUE); @@ -195,6 +238,10 @@ void node_keymap(struct wmKeyConfig *keyconf) /* modified operator call for duplicating with input links */ WM_keymap_add_item(keymap, "NODE_OT_duplicate_move_keep_inputs", DKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0); + WM_keymap_add_item(keymap, "NODE_OT_parent_set", PKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "NODE_OT_parent_clear", PKEY, KM_PRESS, KM_ALT, 0); + WM_keymap_add_item(keymap, "NODE_OT_join", JKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "NODE_OT_hide_toggle", HKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "NODE_OT_mute_toggle", MKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "NODE_OT_preview_toggle", HKEY, KM_PRESS, KM_SHIFT, 0); diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index a540f18b3a5..9098c8a4255 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -70,109 +70,6 @@ static bNode *node_under_mouse(bNodeTree *ntree, int mx, int my) return NULL; } -static int compare_nodes(bNode *a, bNode *b) -{ - bNode *parent; - /* These tell if either the node or any of the parent nodes is selected. - * A selected parent means an unselected node is also in foreground! - */ - int a_select=(a->flag & NODE_SELECT), b_select=(b->flag & NODE_SELECT); - int a_active=(a->flag & NODE_ACTIVE), b_active=(b->flag & NODE_ACTIVE); - - /* if one is an ancestor of the other */ - /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */ - for (parent = a->parent; parent; parent=parent->parent) { - /* if b is an ancestor, it is always behind a */ - if (parent==b) - return 1; - /* any selected ancestor moves the node forward */ - if (parent->flag & NODE_ACTIVE) - a_active = 1; - if (parent->flag & NODE_SELECT) - a_select = 1; - } - for (parent = b->parent; parent; parent=parent->parent) { - /* if a is an ancestor, it is always behind b */ - if (parent==a) - return 0; - /* any selected ancestor moves the node forward */ - if (parent->flag & NODE_ACTIVE) - b_active = 1; - if (parent->flag & NODE_SELECT) - b_select = 1; - } - - /* if one of the nodes is in the background and the other not */ - if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND)) - return 0; - else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND)) - return 1; - - /* if one has a higher selection state (active > selected > nothing) */ - if (!b_active && a_active) - return 1; - else if (!b_select && (a_active || a_select)) - return 1; - - return 0; -} - -/* Sorts nodes by selection: unselected nodes first, then selected, - * then the active node at the very end. Relative order is kept intact! - */ -static void node_sort(bNodeTree *ntree) -{ - /* merge sort is the algorithm of choice here */ - bNode *first_a, *first_b, *node_a, *node_b, *tmp; - int totnodes= BLI_countlist(&ntree->nodes); - int k, a, b; - - k = 1; - while (k < totnodes) { - first_a = first_b = ntree->nodes.first; - - do { - /* setup first_b pointer */ - for (b=0; b < k && first_b; ++b) { - first_b = first_b->next; - } - /* all batches merged? */ - if (first_b==NULL) - break; - - /* merge batches */ - node_a = first_a; - node_b = first_b; - a = b = 0; - while (a < k && b < k && node_b) { - if (compare_nodes(node_a, node_b)==0) { - node_a = node_a->next; - ++a; - } - else { - tmp = node_b; - node_b = node_b->next; - ++b; - BLI_remlink(&ntree->nodes, tmp); - BLI_insertlinkbefore(&ntree->nodes, node_a, tmp); - } - } - - /* setup first pointers for next batch */ - first_b = node_b; - for (; b < k; ++b) { - /* all nodes sorted? */ - if (first_b==NULL) - break; - first_b = first_b->next; - } - first_a = first_b; - } while (first_b); - - k = k << 1; - } -} - void node_select(bNode *node) { node->flag |= SELECT; @@ -407,7 +304,7 @@ void node_select_single(bContext *C, bNode *node) ED_node_set_active(bmain, snode->edittree, node); - node_sort(snode->edittree); + ED_node_sort(snode->edittree); WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL); } @@ -479,7 +376,7 @@ static int node_mouse_select(Main *bmain, SpaceNode *snode, ARegion *ar, const i /* update node order */ if (selected) - node_sort(snode->edittree); + ED_node_sort(snode->edittree); return selected; } @@ -573,7 +470,7 @@ static int node_borderselect_exec(bContext *C, wmOperator *op) } } - node_sort(snode->edittree); + ED_node_sort(snode->edittree); WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL); @@ -645,7 +542,7 @@ static int node_select_all_exec(bContext *C, wmOperator *UNUSED(op)) node_select(node); } - node_sort(snode->edittree); + ED_node_sort(snode->edittree); WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL); return OPERATOR_FINISHED; @@ -687,7 +584,7 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op)) node_select(node); } - node_sort(snode->edittree); + ED_node_sort(snode->edittree); WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL); return OPERATOR_FINISHED; @@ -729,7 +626,7 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op)) node_select(node); } - node_sort(snode->edittree); + ED_node_sort(snode->edittree); WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL); return OPERATOR_FINISHED; @@ -758,7 +655,7 @@ static int node_select_same_type_exec(bContext *C, wmOperator *UNUSED(op)) node_select_same_type(snode); - node_sort(snode->edittree); + ED_node_sort(snode->edittree); WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL); return OPERATOR_FINISHED; @@ -787,7 +684,7 @@ static int node_select_same_type_next_exec(bContext *C, wmOperator *UNUSED(op)) node_select_same_type_np(snode, 0); - node_sort(snode->edittree); + ED_node_sort(snode->edittree); WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL); @@ -815,7 +712,7 @@ static int node_select_same_type_prev_exec(bContext *C, wmOperator *UNUSED(op)) node_select_same_type_np(snode, 1); - node_sort(snode->edittree); + ED_node_sort(snode->edittree); WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index 3540c20e515..4d5964c72e5 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -339,6 +339,17 @@ static void node_buttons_area_draw(const bContext *C, ARegion *ar) ED_region_panels(C, ar, 1, NULL, -1); } +static void node_cursor(wmWindow *win, ScrArea *sa, ARegion *ar) +{ + SpaceNode *snode= sa->spacedata.first; + + /* convert mouse coordinates to v2d space */ + UI_view2d_region_to_view(&ar->v2d, win->eventstate->x - ar->winrct.xmin, win->eventstate->y - ar->winrct.ymin, + &snode->mx, &snode->my); + + node_set_cursor(win, snode); +} + /* Initialize main area, setting handlers. */ static void node_main_area_init(wmWindowManager *wm, ARegion *ar) { @@ -522,6 +533,8 @@ void ED_spacetype_node(void) art->init= node_main_area_init; art->draw= node_main_area_draw; art->listener= node_region_listener; + art->cursor = node_cursor; + art->event_cursor = TRUE; art->keymapflag= ED_KEYMAP_UI|ED_KEYMAP_VIEW2D|ED_KEYMAP_FRAMES|ED_KEYMAP_GPENCIL; BLI_addhead(&st->regiontypes, art); |