From ef71a1da33a3c68cad782029cacbc1d01328b4d6 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 21 Feb 2015 23:32:24 +0000 Subject: Add API to specify module port order. Also fix various redundant resize/update issues, improve performance. git-svn-id: http://svn.drobilla.net/lad/trunk/ganv@5592 a436a847-0d15-0410-975c-d299462d15a1 --- NEWS | 6 ++- ganv/Canvas.hpp | 16 ++++++- ganv/Item.hpp | 3 +- ganv/canvas.h | 33 ++++++++++++++ ganv/item.h | 4 ++ ganv/wrap.hpp | 9 +++- src/Canvas.cpp | 48 ++++++++++++++++----- src/box.c | 21 ++++----- src/circle.c | 7 +-- src/edge.c | 6 +-- src/ganv-private.h | 14 +++++- src/group.c | 4 +- src/item.c | 30 ++++++++----- src/module.c | 123 +++++++++++++++++++++++++++++------------------------ src/node.c | 50 +++++++++++++++------- src/port.c | 22 ++++++---- src/text.c | 40 ++++++++++------- wscript | 2 +- 18 files changed, 289 insertions(+), 149 deletions(-) diff --git a/NEWS b/NEWS index bf7bda2..b20cc3a 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -ganv (1.5.1) unstable; +ganv (1.5.2) unstable; * Fix positioning of embedded widgets when changing layout. * Fix unexpected node jumping when dragging new connections. @@ -7,12 +7,14 @@ ganv (1.5.1) unstable; * Distinguish edge color from port color slighly. * Add support for PDF and PS export. * Improve text rendering at high zoom. + * Improve update performance. + * Add API to specify module port order. * Fix various minor visual alignment/sizing issues. * Fix size of vertical flow modules. * Fix compilation with --no-fdgl (patch from Vlad Glagolev). * Fix crash when destroying canvas. - -- David Robillard Fri, 20 Feb 2015 09:00:29 -0500 + -- David Robillard Sat, 21 Feb 2015 17:21:25 -0500 ganv (1.4.2) stable; diff --git a/ganv/Canvas.hpp b/ganv/Canvas.hpp index e22c691..46c2d5e 100644 --- a/ganv/Canvas.hpp +++ b/ganv/Canvas.hpp @@ -25,8 +25,6 @@ #include "ganv/canvas.h" #include "ganv/wrap.hpp" -GANV_GLIB_WRAP(Canvas) - /** Ganv namespace, everything is defined under this. * * @ingroup Ganv @@ -121,6 +119,10 @@ public: RW_PROPERTY(double, height) RW_PROPERTY(GanvDirection, direction); + void set_port_order(GanvPortOrderFunc port_cmp, void* data) { + ganv_canvas_set_port_order(gobj(), port_cmp, data); + } + Gtk::Layout& widget() { return *Glib::wrap(&_gobj->layout); } @@ -143,4 +145,14 @@ private: } // namespace Ganv +namespace Glib { + +static inline Ganv::Canvas* +wrap(GanvCanvas* canvas) +{ + return (Ganv::Canvas*)ganv_canvas_get_wrapper(canvas); +} + +} // namespace Glib + #endif // GANV_CANVAS_HPP diff --git a/ganv/Item.hpp b/ganv/Item.hpp index fb2667d..a2dbadc 100644 --- a/ganv/Item.hpp +++ b/ganv/Item.hpp @@ -40,9 +40,8 @@ public: Item(GanvItem* gobj) : _gobj(gobj) { - GQuark wrapper_key = g_quark_from_string("ganvmm"); + ganv_item_set_wrapper(gobj, this); if (gobj && ganv_item_get_parent(gobj)) { - g_object_set_qdata(G_OBJECT(_gobj), wrapper_key, this); g_signal_connect( G_OBJECT(_gobj), "event", G_CALLBACK(on_item_event), this); } diff --git a/ganv/canvas.h b/ganv/canvas.h index e923f65..e2a9867 100644 --- a/ganv/canvas.h +++ b/ganv/canvas.h @@ -91,6 +91,8 @@ typedef void (*GanvEdgeFunc)(GanvEdge* edge, void* data); */ typedef void (*GanvNodeFunc)(GanvNode* node, void* data); +typedef int (*GanvPortOrderFunc)(const GanvPort*, const GanvPort*, void* data); + /** * ganv_canvas_new: * @@ -99,6 +101,22 @@ typedef void (*GanvNodeFunc)(GanvNode* node, void* data); GanvCanvas* ganv_canvas_new(double width, double height); +/** + * ganv_canvas_set_wrapper: + * + * Set the opaque wrapper object for the canvas. + */ +void +ganv_canvas_set_wrapper(GanvCanvas* canvas, void* wrapper); + +/** + * ganv_canvas_get_wrapper: + * + * Return an opaque pointer to the wrapper for the canvas. + */ +void* +ganv_canvas_get_wrapper(GanvCanvas* canvas); + /** * ganv_canvas_clear: * @@ -592,6 +610,21 @@ ganv_canvas_get_move_cursor(const GanvCanvas* canvas); void ganv_canvas_move_contents_to(GanvCanvas* canvas, double x, double y); +/** + * ganv_canvas_set_port_order: + * @canvas The canvas to set the default port order on. + * @port_cmp Port comparison function. + * @data Data to be passed to order. + * + * Set a comparator function to use as the default order for ports on modules. + * If left unset, ports are shown in the order they are added. + */ +void +ganv_canvas_set_port_order(GanvCanvas* canvas, + GanvPortOrderFunc port_cmp, + void* data); + + G_END_DECLS #endif /* GANV_CANVAS_H */ diff --git a/ganv/item.h b/ganv/item.h index d6e9cc5..c538e5a 100644 --- a/ganv/item.h +++ b/ganv/item.h @@ -175,6 +175,10 @@ void ganv_item_get_bounds(GanvItem* item, void ganv_item_request_update(GanvItem* item); +void ganv_item_set_wrapper(GanvItem* item, void* wrapper); + +void* ganv_item_get_wrapper(GanvItem* item); + G_END_DECLS #endif /* GANV_ITEM_H */ diff --git a/ganv/wrap.hpp b/ganv/wrap.hpp index f907dea..c444e00 100644 --- a/ganv/wrap.hpp +++ b/ganv/wrap.hpp @@ -115,12 +115,17 @@ private: \ wrap(Ganv##Name* gobj) \ { \ if (gobj) { \ - GQuark key = g_quark_from_string("ganvmm"); \ - return (Ganv::Name*)g_object_get_qdata(G_OBJECT(gobj), key); \ + return (Ganv::Name*)ganv_item_get_wrapper(GANV_ITEM(gobj)); \ } else { \ return NULL; \ } \ } \ + /** Return a Ganv::CPPType wrapper for a CType. */ \ + static inline const Ganv::Name* \ + wrap(const Ganv##Name* gobj) \ + { \ + return wrap((Ganv##Name*)gobj); \ + } \ } #endif // GANV_WRAP_HPP diff --git a/src/Canvas.cpp b/src/Canvas.cpp index daacc1d..3dbf8df 100644 --- a/src/Canvas.cpp +++ b/src/Canvas.cpp @@ -186,6 +186,7 @@ panic_root_destroyed(GtkObject* object, gpointer data) struct GanvCanvasImpl { GanvCanvasImpl(GanvCanvas* canvas) : _gcanvas(canvas) + , _wrapper(NULL) , _connect_port(NULL) , _last_selected_port(NULL) , _drag_edge(NULL) @@ -255,10 +256,12 @@ struct GanvCanvasImpl { _animate_idle_id = 0; + _port_order.port_cmp = NULL; + _port_order.data = NULL; + gtk_layout_set_hadjustment(GTK_LAYOUT(canvas), NULL); gtk_layout_set_vadjustment(GTK_LAYOUT(canvas), NULL); - _wrapper_key = g_quark_from_string("ganvmm"); _move_cursor = gdk_cursor_new(GDK_FLEUR); } @@ -327,7 +330,8 @@ struct GanvCanvasImpl { void move_contents_to_internal(double x, double y, double min_x, double min_y); - GanvCanvas* _gcanvas; + GanvCanvas* _gcanvas; + Ganv::Canvas* _wrapper; Items _items; ///< Items on this canvas Edges _edges; ///< Edges ordered (src, dst) @@ -348,10 +352,11 @@ struct GanvCanvasImpl { enum DragState { NOT_DRAGGING, EDGE, SCROLL, SELECT }; DragState _drag_state; - GQuark _wrapper_key; GdkCursor* _move_cursor; guint _animate_idle_id; + PortOrderCtx _port_order; + /* Root canvas item */ GanvItem* root; @@ -1693,7 +1698,7 @@ on_disconnect(GanvCanvas* canvas, GanvNode* tail, GanvNode* head, void* data) Canvas::Canvas(double width, double height) : _gobj(GANV_CANVAS(ganv_canvas_new(width, height))) { - g_object_set_qdata(G_OBJECT(_gobj->impl->_gcanvas), wrapper_key(), this); + ganv_canvas_set_wrapper(_gobj, this); g_signal_connect_after(ganv_canvas_root(_gobj), "event", G_CALLBACK(on_event_after), this); @@ -1743,12 +1748,6 @@ Canvas::get_edge(Node* tail, Node* head) const return NULL; } -GQuark -Canvas::wrapper_key() -{ - return _gobj->impl->_wrapper_key; -} - } // namespace Ganv extern "C" { @@ -2758,6 +2757,18 @@ ganv_canvas_new(double width, double height) return canvas; } +void +ganv_canvas_set_wrapper(GanvCanvas* canvas, void* wrapper) +{ + canvas->impl->_wrapper = (Ganv::Canvas*)wrapper; +} + +void* +ganv_canvas_get_wrapper(GanvCanvas* canvas) +{ + return canvas->impl->_wrapper; +} + /* Map handler for the canvas */ static void ganv_canvas_map(GtkWidget* widget) @@ -4103,4 +4114,21 @@ ganv_canvas_world_to_window(GanvCanvas* canvas, double worldx, double worldy, } } +void +ganv_canvas_set_port_order(GanvCanvas* canvas, + GanvPortOrderFunc port_cmp, + void* data) +{ + g_return_if_fail(GANV_IS_CANVAS(canvas)); + + canvas->impl->_port_order.port_cmp = port_cmp; + canvas->impl->_port_order.data = data; +} + +PortOrderCtx +ganv_canvas_get_port_order(GanvCanvas* canvas) +{ + return canvas->impl->_port_order; +} + } // extern "C" diff --git a/src/box.c b/src/box.c index 1210104..a1a9315 100644 --- a/src/box.c +++ b/src/box.c @@ -182,9 +182,6 @@ ganv_box_update(GanvItem* item, int flags) // Request redraw of old location ganv_box_request_redraw(item, &impl->old_coords, TRUE); - GanvItemClass* item_class = GANV_ITEM_CLASS(parent_class); - item_class->update(item, flags); - // Store old coordinates in world relative coordinates in case the // group we are in moves between now and the next update impl->old_coords = impl->coords; @@ -198,6 +195,8 @@ ganv_box_update(GanvItem* item, int flags) // Request redraw of new location ganv_box_request_redraw(item, &impl->coords, FALSE); + + GANV_ITEM_CLASS(parent_class)->update(item, flags); } void @@ -343,21 +342,17 @@ ganv_box_is_within(const GanvNode* self, } static void -ganv_box_default_set_width(GanvBox* box, - double width) +ganv_box_default_set_width(GanvBox* box, double width) { - ganv_item_set(GANV_ITEM(box), - "x2", ganv_box_get_x1(box) + width, - NULL); + box->impl->coords.x2 = ganv_box_get_x1(box) + width; + ganv_item_request_update(GANV_ITEM(box)); } static void -ganv_box_default_set_height(GanvBox* box, - double height) +ganv_box_default_set_height(GanvBox* box, double height) { - ganv_item_set(GANV_ITEM(box), - "y2", ganv_box_get_y1(box) + height, - NULL); + box->impl->coords.y2 = ganv_box_get_y1(box) + height; + ganv_item_request_update(GANV_ITEM(box)); } static void diff --git a/src/circle.c b/src/circle.c index 7230abb..fe2a54b 100644 --- a/src/circle.c +++ b/src/circle.c @@ -260,11 +260,6 @@ ganv_circle_update(GanvItem* item, int flags) GanvCircleImpl* impl = circle->impl; impl->coords.width = circle->node.impl->border_width; - GanvItemClass* item_class = GANV_ITEM_CLASS(parent_class); - if (item_class->update) { - (*item_class->update)(item, flags); - } - // Request redraw of old location request_redraw(item, &impl->old_coords, TRUE); @@ -279,6 +274,8 @@ ganv_circle_update(GanvItem* item, int flags) // Request redraw of new location request_redraw(item, &impl->coords, FALSE); + + GANV_ITEM_CLASS(parent_class)->update(item, flags); } static void diff --git a/src/edge.c b/src/edge.c index 449f884..d2833e4 100644 --- a/src/edge.c +++ b/src/edge.c @@ -282,10 +282,6 @@ ganv_edge_update(GanvItem* item, int flags) GanvEdge* edge = GANV_EDGE(item); GanvEdgeImpl* impl = edge->impl; - if (parent_class->update) { - (*parent_class->update)(item, flags); - } - // Request redraw of old location ganv_edge_request_redraw(item, &impl->old_coords); @@ -316,6 +312,8 @@ ganv_edge_update(GanvItem* item, int flags) // Request redraw of new location ganv_edge_request_redraw(item, &impl->coords); + + parent_class->update(item, flags); } static void diff --git a/src/ganv-private.h b/src/ganv-private.h index eefeaa8..4d892d9 100644 --- a/src/ganv-private.h +++ b/src/ganv-private.h @@ -95,7 +95,7 @@ struct _GanvModuleImpl int embed_height; double widest_input; double widest_output; - gboolean must_resize; + gboolean must_reorder; }; /* Node */ @@ -123,6 +123,7 @@ struct _GanvNodeImpl { gboolean draggable; gboolean show_label; gboolean grabbed; + gboolean must_resize; #ifdef GANV_FDGL Vector force; Vector vel; @@ -162,6 +163,9 @@ struct _GanvItemImpl { /* Parent for this item */ GanvItem* parent; + /* Wrapper object for this item, if any */ + void* wrapper; + /* Layer (z order), higher values are on top */ guint layer; @@ -248,6 +252,11 @@ struct _GanvTextImpl /* Canvas */ +typedef struct { + GanvPortOrderFunc port_cmp; + void* data; +} PortOrderCtx; + void ganv_canvas_move_selected_items(GanvCanvas* canvas, double dx, @@ -342,6 +351,9 @@ void ganv_canvas_request_redraw_w(GanvCanvas* canvas, double x1, double y1, double x2, double y2); +PortOrderCtx +ganv_canvas_get_port_order(GanvCanvas* canvas); + /* Edge */ void diff --git a/src/group.c b/src/group.c index e867cef..3a37c80 100644 --- a/src/group.c +++ b/src/group.c @@ -94,8 +94,6 @@ ganv_group_update(GanvItem* item, int flags) { GanvGroup* group = GANV_GROUP(item); - (*group_parent_class->update)(item, flags); - double min_x = 0.0; double min_y = 0.0; double max_x = 0.0; @@ -115,6 +113,8 @@ ganv_group_update(GanvItem* item, int flags) item->impl->y1 = min_y; item->impl->x2 = max_x; item->impl->y2 = max_y; + + (*group_parent_class->update)(item, flags); } static void diff --git a/src/item.c b/src/item.c index f931e94..d645e23 100644 --- a/src/item.c +++ b/src/item.c @@ -78,6 +78,7 @@ ganv_item_init(GanvItem* item) item->object.flags |= GANV_ITEM_VISIBLE; item->impl = impl; item->impl->managed = FALSE; + item->impl->wrapper = NULL; } /** @@ -218,9 +219,10 @@ ganv_item_construct(GanvItem* item, GanvItem* parent, { g_return_if_fail(GANV_IS_ITEM(item)); - item->impl->parent = parent; - item->impl->canvas = item->impl->parent->impl->canvas; - item->impl->layer = 0; + item->impl->parent = parent; + item->impl->wrapper = NULL; + item->impl->canvas = item->impl->parent->impl->canvas; + item->impl->layer = 0; g_object_set_valist(G_OBJECT(item), first_arg_name, args); @@ -343,6 +345,7 @@ ganv_item_invoke_update(GanvItem* item, int flags) if (child_flags & GCI_UPDATE_MASK) { if (GANV_ITEM_GET_CLASS(item)->update) { GANV_ITEM_GET_CLASS(item)->update(item, child_flags); + g_assert(!(GTK_OBJECT_FLAGS(item) & GANV_ITEM_NEED_UPDATE)); } } } @@ -595,12 +598,6 @@ ganv_item_get_bounds(GanvItem* item, double* x1, double* y1, double* x2, double* void ganv_item_request_update(GanvItem* item) { - /* Note: At some point, if this short-circuit was enabled, the canvas stopped - updating items entirely when connected modules are removed. */ - if (item->object.flags & GANV_ITEM_NEED_UPDATE) { - return; - } - if (!item->impl->canvas) { /* Item is being / has been destroyed, ignore */ return; @@ -608,7 +605,8 @@ ganv_item_request_update(GanvItem* item) item->object.flags |= GANV_ITEM_NEED_UPDATE; - if (item->impl->parent != NULL) { + if (item->impl->parent != NULL && + !(item->impl->parent->object.flags & GANV_ITEM_NEED_UPDATE)) { /* Recurse up the tree */ ganv_item_request_update(item->impl->parent); } else { @@ -617,6 +615,18 @@ ganv_item_request_update(GanvItem* item) } } +void +ganv_item_set_wrapper(GanvItem* item, void* wrapper) +{ + item->impl->wrapper = wrapper; +} + +void* +ganv_item_get_wrapper(GanvItem* item) +{ + return item->impl->wrapper; +} + static gboolean boolean_handled_accumulator(GSignalInvocationHint* ihint, GValue* return_accu, diff --git a/src/module.c b/src/module.c index 550ee48..46cdfff 100644 --- a/src/module.c +++ b/src/module.c @@ -63,7 +63,7 @@ ganv_module_init(GanvModule* module) impl->embed_height = 0; impl->widest_input = 0.0; impl->widest_output = 0.0; - impl->must_resize = TRUE; + impl->must_reorder = FALSE; } static void @@ -256,16 +256,15 @@ place_title(GanvModule* module, GanvDirection dir) if (!canvas_title) { return; - } else if (dir == GANV_DIRECTION_RIGHT) { - ganv_item_set(GANV_ITEM(canvas_title), - "x", (ganv_box_get_width(box) - title_w) / 2.0, - "y", 1.0, - NULL); + } + + GanvItem* t = GANV_ITEM(canvas_title); + if (dir == GANV_DIRECTION_RIGHT) { + t->impl->x = (ganv_box_get_width(box) - title_w) / 2.0; + t->impl->y = 1.0; } else { - ganv_item_set(GANV_ITEM(canvas_title), - "x", (ganv_box_get_width(box) - title_w) / 2.0, - "y", ganv_module_get_empty_port_depth(module) + 1.0, - NULL); + t->impl->x = (ganv_box_get_width(box) - title_w) / 2.0; + t->impl->y = ganv_module_get_empty_port_depth(module) + 1.0; } } @@ -392,7 +391,7 @@ resize_down(GanvModule* module) // Offset to shift ports to make borders line up const double border_off = (GANV_NODE(module)->impl->border_width - pnode->impl->border_width) / 2.0; - + if (p->impl->is_input) { in_x = EDGE_PAD + (in_count++ * (port_breadth + PAD + 1.0)); ganv_node_move_to(pnode, in_x, -border_off); @@ -436,12 +435,11 @@ measure_ports(GanvModule* module) } static void -layout(GanvNode* self) +ganv_module_resize(GanvNode* self) { - GanvModule* module = GANV_MODULE(self); - GanvModuleImpl* impl = module->impl; - GanvNode* node = GANV_NODE(self); - GanvCanvas* canvas = ganv_item_get_canvas(GANV_ITEM(module)); + GanvModule* module = GANV_MODULE(self); + GanvNode* node = GANV_NODE(self); + GanvCanvas* canvas = ganv_item_get_canvas(GANV_ITEM(module)); double label_w = 0.0; double label_h = 0.0; @@ -466,16 +464,8 @@ layout(GanvNode* self) break; } - impl->must_resize = FALSE; -} - -static void -ganv_module_resize(GanvNode* self) -{ - layout(self); - - if (parent_class->parent_class.resize) { - parent_class->parent_class.resize(self); + if (GANV_NODE_CLASS(parent_class)->resize) { + GANV_NODE_CLASS(parent_class)->resize(self); } } @@ -495,9 +485,9 @@ static void ganv_module_add_port(GanvModule* module, GanvPort* port) { - GanvCanvas* canvas = ganv_item_get_canvas(GANV_ITEM(module)); - GanvModuleImpl* impl = module->impl; + GanvModuleImpl* impl = module->impl; + // Update widest input/output measurements if necessary const double width = ganv_port_get_natural_width(port); if (port->impl->is_input && width > impl->widest_input) { impl->widest_input = width; @@ -505,11 +495,12 @@ ganv_module_add_port(GanvModule* module, impl->widest_output = width; } - impl->must_resize = TRUE; - + // Add to port array g_ptr_array_add(impl->ports, port); - place_title(module, ganv_canvas_get_direction(canvas)); - ganv_item_request_update(GANV_ITEM(module)); + + // Request update with resize and reorder + GANV_NODE(module)->impl->must_resize = TRUE; + impl->must_reorder = TRUE; } static void @@ -540,8 +531,7 @@ ganv_module_remove_port(GanvModule* module, } } - module->impl->must_resize = TRUE; - ganv_item_request_update(GANV_ITEM(module)); + GANV_NODE(module)->impl->must_resize = TRUE; } else { fprintf(stderr, "Failed to find port to remove\n"); } @@ -553,6 +543,10 @@ ganv_module_add(GanvItem* item, GanvItem* child) if (GANV_IS_PORT(child)) { ganv_module_add_port(GANV_MODULE(item), GANV_PORT(child)); } + ganv_item_request_update(item); + if (GANV_ITEM_CLASS(parent_class)->add) { + GANV_ITEM_CLASS(parent_class)->add(item, child); + } } static void @@ -561,16 +555,34 @@ ganv_module_remove(GanvItem* item, GanvItem* child) if (GANV_IS_PORT(child)) { ganv_module_remove_port(GANV_MODULE(item), GANV_PORT(child)); } + ganv_item_request_update(item); + if (GANV_ITEM_CLASS(parent_class)->remove) { + GANV_ITEM_CLASS(parent_class)->remove(item, child); + } +} + +static int +ptr_sort(const GanvPort** a, const GanvPort** b, const PortOrderCtx* ctx) +{ + return ctx->port_cmp(*a, *b, ctx->data); } static void ganv_module_update(GanvItem* item, int flags) { - GanvNode* node = GANV_NODE(item); GanvModule* module = GANV_MODULE(item); + GanvCanvas* canvas = ganv_item_get_canvas(item); + + if (module->impl->must_reorder) { + // Sort ports array + PortOrderCtx ctx = ganv_canvas_get_port_order(canvas); + if (ctx.port_cmp) { + g_ptr_array_sort_with_data(module->impl->ports, + (GCompareDataFunc)ptr_sort, - if (module->impl->must_resize) { - layout(node); + &ctx); + } + module->impl->must_reorder = FALSE; } if (module->impl->embed_item) { @@ -578,9 +590,6 @@ ganv_module_update(GanvItem* item, int flags) ganv_item_move(GANV_ITEM(module->impl->embed_item), 0.0, 0.0); } - GanvItemClass* item_class = GANV_ITEM_CLASS(parent_class); - item_class->update(item, flags); - FOREACH_PORT(module->impl->ports, p) { ganv_item_invoke_update(GANV_ITEM(*p), flags); } @@ -588,6 +597,8 @@ ganv_module_update(GanvItem* item, int flags) if (module->impl->embed_item) { ganv_item_invoke_update(GANV_ITEM(module->impl->embed_item), flags); } + + GANV_ITEM_CLASS(parent_class)->update(item, flags); } static void @@ -763,10 +774,9 @@ on_embed_size_request(GtkWidget* widget, return; } - impl->embed_width = r->width; - impl->embed_height = r->height; - - impl->must_resize = TRUE; + impl->embed_width = r->width; + impl->embed_height = r->height; + GANV_NODE(module)->impl->must_resize = TRUE; GtkAllocation allocation; allocation.width = r->width; @@ -784,43 +794,46 @@ ganv_module_embed(GanvModule* module, GtkWidget* widget) { GanvModuleImpl* impl = module->impl; + if (!widget && !impl->embed_item) { + return; + } if (impl->embed_item) { + // Free existing embedded widget gtk_object_destroy(GTK_OBJECT(impl->embed_item)); impl->embed_item = NULL; } if (!widget) { - impl->embed_width = 0; - impl->embed_height = 0; - impl->must_resize = TRUE; + // Removing an existing embedded widget + impl->embed_width = 0; + impl->embed_height = 0; + GANV_NODE(module)->impl->must_resize = TRUE; + ganv_item_request_update(GANV_ITEM(module)); return; } double title_w, title_h; title_size(module, &title_w, &title_h); - const double y = 4.0 + title_h; impl->embed_item = ganv_item_new( GANV_ITEM(module), ganv_widget_get_type(), "x", 2.0, - "y", y, + "y", 4.0 + title_h, "widget", widget, NULL); - gtk_widget_show_all(widget); - GtkRequisition r; + gtk_widget_show_all(widget); gtk_widget_size_request(widget, &r); on_embed_size_request(widget, &r, module); - ganv_item_show(impl->embed_item); g_signal_connect(widget, "size-request", G_CALLBACK(on_embed_size_request), module); - layout(GANV_NODE(module)); + GANV_NODE(module)->impl->must_resize = TRUE; ganv_item_request_update(GANV_ITEM(module)); } @@ -831,8 +844,8 @@ ganv_module_set_direction(GanvModule* module, FOREACH_PORT(module->impl->ports, p) { ganv_port_set_direction(*p, direction); } - module->impl->must_resize = TRUE; - ganv_module_resize(GANV_NODE(module)); + GANV_NODE(module)->impl->must_resize = TRUE; + ganv_item_request_update(GANV_ITEM(module)); } void diff --git a/src/node.c b/src/node.c index 5a4f727..e77ded3 100644 --- a/src/node.c +++ b/src/node.c @@ -71,6 +71,7 @@ ganv_node_init(GanvNode* node) impl->draggable = FALSE; impl->show_label = TRUE; impl->grabbed = FALSE; + impl->must_resize = FALSE; #ifdef GANV_FDGL impl->force.x = 0.0; impl->force.y = 0.0; @@ -114,6 +115,22 @@ ganv_node_destroy(GtkObject* object) item->impl->canvas = NULL; } +static void +ganv_node_update(GanvItem* item, int flags) +{ + GanvNode* node = GANV_NODE(item); + if (node->impl->must_resize) { + ganv_node_resize(node); + node->impl->must_resize = FALSE; + } + + if (node->impl->label) { + ganv_item_invoke_update(GANV_ITEM(node->impl->label), flags); + } + + GANV_ITEM_CLASS(parent_class)->update(item, flags); +} + static void ganv_node_draw(GanvItem* item, cairo_t* cr, double cx, double cy, double cw, double ch) @@ -319,14 +336,8 @@ ganv_node_set_label(GanvNode* node, const char* str) NULL)); } - if (impl->show_label) { - GanvNodeClass* klass = GANV_NODE_GET_CLASS(node); - if (klass->resize) { - klass->resize(node); - } - - ganv_item_request_update(GANV_ITEM(node)); - } + impl->must_resize = TRUE; + ganv_item_request_update(GANV_ITEM(node)); } void @@ -371,6 +382,7 @@ ganv_node_default_move(GanvNode* node, ganv_item_move(GANV_ITEM(node), dx, dy); ganv_canvas_for_each_edge_on( canvas, node, (GanvEdgeFunc)ganv_edge_update_location, NULL); + ganv_item_request_update(GANV_ITEM(node)); } static void @@ -378,11 +390,10 @@ ganv_node_default_move_to(GanvNode* node, double x, double y) { - GanvCanvas* canvas = ganv_item_get_canvas(GANV_ITEM(node)); - ganv_item_set(GANV_ITEM(node), - "x", x, - "y", y, - NULL); + GanvItem* item = GANV_ITEM(node); + GanvCanvas* canvas = ganv_item_get_canvas(item); + item->impl->x = x; + item->impl->y = y; if (node->impl->can_tail) { ganv_canvas_for_each_edge_from( canvas, node, (GanvEdgeFunc)ganv_edge_update_location, NULL); @@ -390,6 +401,7 @@ ganv_node_default_move_to(GanvNode* node, ganv_canvas_for_each_edge_to( canvas, node, (GanvEdgeFunc)ganv_edge_update_location, NULL); } + ganv_item_request_update(GANV_ITEM(node)); } static void @@ -399,6 +411,7 @@ ganv_node_default_resize(GanvNode* node) if (GANV_IS_NODE(item->impl->parent)) { ganv_node_resize(GANV_NODE(item->impl->parent)); } + node->impl->must_resize = FALSE; } static void @@ -406,7 +419,8 @@ ganv_node_default_redraw_text(GanvNode* node) { if (node->impl->label) { ganv_text_layout(node->impl->label); - ganv_node_resize(node); + node->impl->must_resize = TRUE; + ganv_item_request_update(GANV_ITEM(node)); } } @@ -425,12 +439,14 @@ ganv_node_default_event(GanvItem* item, switch (event->type) { case GDK_ENTER_NOTIFY: ganv_item_raise(GANV_ITEM(node)); - ganv_item_set(GANV_ITEM(node), "highlighted", TRUE, NULL); + node->impl->highlighted = TRUE; + ganv_item_request_update(item); return TRUE; case GDK_LEAVE_NOTIFY: ganv_item_lower(GANV_ITEM(node)); - ganv_item_set(GANV_ITEM(node), "highlighted", FALSE, NULL); + node->impl->highlighted = FALSE; + ganv_item_request_update(item); return TRUE; case GDK_BUTTON_PRESS: @@ -685,6 +701,7 @@ ganv_node_class_init(GanvNodeClass* klass) item_class->realize = ganv_node_realize; item_class->event = ganv_node_default_event; + item_class->update = ganv_node_update; item_class->draw = ganv_node_draw; klass->disconnect = ganv_node_default_disconnect; @@ -856,6 +873,7 @@ void ganv_node_resize(GanvNode* node) { GANV_NODE_GET_CLASS(node)->resize(node); + node->impl->must_resize = FALSE; } void diff --git a/src/port.c b/src/port.c index 9a97e0b..7f4df2d 100644 --- a/src/port.c +++ b/src/port.c @@ -133,8 +133,13 @@ ganv_port_update(GanvItem* item, int flags) if (impl->control) { const double border_width = GANV_NODE(item)->impl->border_width; impl->control->rect->impl->coords.border_width = border_width; + ganv_item_invoke_update(GANV_ITEM(impl->control->rect), flags); } + if (impl->value_label) { + ganv_item_invoke_update(GANV_ITEM(port->impl->value_label), flags); + } + GanvItemClass* item_class = GANV_ITEM_CLASS(parent_class); item_class->update(item, flags); } @@ -316,14 +321,12 @@ ganv_port_resize(GanvNode* self) ganv_box_set_width(&port->box, labels_w); ganv_box_set_height(&port->box, MAX(label_h, vlabel_h) + (PORT_LABEL_VPAD * 2.0)); - ganv_item_set(GANV_ITEM(node->impl->label), - "x", PORT_LABEL_HPAD, - "y", PORT_LABEL_VPAD, - NULL); + + ganv_port_place_labels(port); } - if (parent_class->parent_class.resize) { - parent_class->parent_class.resize(self); + if (GANV_NODE_CLASS(parent_class)->resize) { + GANV_NODE_CLASS(parent_class)->resize(self); } } @@ -486,7 +489,9 @@ ganv_port_set_direction(GanvPort* port, box->impl->radius_bl = (is_input ? 4.0 : 0.0); break; } - ganv_node_resize(node); + + node->impl->must_resize = TRUE; + ganv_item_request_update(GANV_ITEM(node)); } void @@ -552,7 +557,6 @@ ganv_port_set_value_label(GanvPort* port, "color", 0xFFFFFFAA, "managed", TRUE, NULL)); - ganv_port_resize(GANV_NODE(port)); } } @@ -582,7 +586,7 @@ ganv_port_update_control_slider(GanvPort* port, float value, gboolean force) if (value > impl->control->max) { value = impl->control->max; } - + if (!force && value == impl->control->value) { return; // No change, do nothing } diff --git a/src/text.c b/src/text.c index bfc7d0b..04c991c 100644 --- a/src/text.c +++ b/src/text.c @@ -140,24 +140,34 @@ ganv_text_set_property(GObject* object, GanvTextImpl* impl = text->impl; switch (prop_id) { - SET_CASE(X, double, impl->coords.x); - SET_CASE(Y, double, impl->coords.y); - SET_CASE(WIDTH, double, impl->coords.width); - SET_CASE(HEIGHT, double, impl->coords.height); - SET_CASE(COLOR, uint, impl->color) - SET_CASE(FONT_SIZE, double, impl->font_size) + case PROP_X: + impl->coords.x = g_value_get_double(value); + break; + case PROP_Y: + impl->coords.y = g_value_get_double(value); + break; + case PROP_COLOR: + impl->color = g_value_get_uint(value); + break; + case PROP_FONT_SIZE: + impl->font_size = g_value_get_double(value); + impl->needs_layout = TRUE; + break; case PROP_TEXT: free(impl->text); - impl->text = g_value_dup_string(value); + impl->text = g_value_dup_string(value); impl->needs_layout = TRUE; - if (GANV_IS_NODE(GANV_ITEM(text)->impl->parent)) { - ganv_node_resize(GANV_NODE(GANV_ITEM(text)->impl->parent)); - } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; + return; } + if (impl->needs_layout) { + if (GANV_IS_NODE(GANV_ITEM(text)->impl->parent)) { + GANV_NODE(GANV_ITEM(text)->impl->parent)->impl->must_resize = TRUE; + } + } + ganv_item_request_update(GANV_ITEM(text)); } static void @@ -219,14 +229,14 @@ ganv_text_bounds(GanvItem* item, static void ganv_text_update(GanvItem* item, int flags) { - parent_class->update(item, flags); - // Update world-relative bounding box ganv_text_bounds(item, &item->impl->x1, &item->impl->y1, &item->impl->x2, &item->impl->y2); ganv_item_i2w_pair(item, &item->impl->x1, &item->impl->y1, &item->impl->x2, &item->impl->y2); ganv_canvas_request_redraw_w( item->impl->canvas, item->impl->x1, item->impl->y1, item->impl->x2, item->impl->y2); + + parent_class->update(item, flags); } static double @@ -334,7 +344,7 @@ ganv_text_class_init(GanvTextClass* klass) _("The current width of the text."), -G_MAXDOUBLE, G_MAXDOUBLE, 1.0, - G_PARAM_READWRITE)); + G_PARAM_READABLE)); g_object_class_install_property( gobject_class, PROP_HEIGHT, g_param_spec_double( @@ -343,7 +353,7 @@ ganv_text_class_init(GanvTextClass* klass) _("The current height of the text."), -G_MAXDOUBLE, G_MAXDOUBLE, 1.0, - G_PARAM_READWRITE)); + G_PARAM_READABLE)); g_object_class_install_property( gobject_class, PROP_COLOR, g_param_spec_uint( diff --git a/wscript b/wscript index 3c78a9e..f50f03d 100644 --- a/wscript +++ b/wscript @@ -8,7 +8,7 @@ import waflib.extras.autowaf as autowaf # major increment <=> incompatible changes # minor increment <=> compatible changes (additions) # micro increment <=> no interface changes -GANV_VERSION = '1.5.1' +GANV_VERSION = '1.5.2' GANV_MAJOR_VERSION = '1' # Mandatory waf variables -- cgit v1.2.1