diff options
Diffstat (limited to 'scene/gui/graph_edit.cpp')
-rw-r--r-- | scene/gui/graph_edit.cpp | 441 |
1 files changed, 391 insertions, 50 deletions
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 06b1c42690..a7a813d335 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -61,6 +61,7 @@ Error GraphEdit::connect_node(const StringName& p_from, int p_from_port,const St c.to_port=p_to_port; connections.push_back(c); top_layer->update(); + update(); return OK; } @@ -85,6 +86,7 @@ void GraphEdit::disconnect_node(const StringName& p_from, int p_from_port,const connections.erase(E); top_layer->update(); + update(); return; } } @@ -100,6 +102,15 @@ void GraphEdit::get_connection_list(List<Connection> *r_connections) const { *r_connections=connections; } +void GraphEdit::set_scroll_ofs(const Vector2& p_ofs) { + + setting_scroll_ofs=true; + h_scroll->set_val(p_ofs.x); + v_scroll->set_val(p_ofs.y); + _update_scroll(); + setting_scroll_ofs=false; +} + Vector2 GraphEdit::get_scroll_ofs() const{ return Vector2(h_scroll->get_val(),v_scroll->get_val()); @@ -109,6 +120,11 @@ void GraphEdit::_scroll_moved(double) { _update_scroll_offset(); top_layer->update(); + update(); + + if (!setting_scroll_ofs) {//in godot, signals on change value are avoided as a convention + emit_signal("scroll_offset_changed",get_scroll_ofs()); + } } void GraphEdit::_update_scroll_offset() { @@ -177,7 +193,11 @@ void GraphEdit::_graph_node_raised(Node* p_gn) { GraphNode *gn=p_gn->cast_to<GraphNode>(); ERR_FAIL_COND(!gn); gn->raise(); + if (gn->is_comment()) { + move_child(gn,0); + } top_layer->raise(); + emit_signal("node_selected",p_gn); } @@ -187,10 +207,13 @@ void GraphEdit::_graph_node_moved(Node *p_gn) { GraphNode *gn=p_gn->cast_to<GraphNode>(); ERR_FAIL_COND(!gn); top_layer->update(); + update(); } void GraphEdit::add_child_notify(Node *p_child) { + Control::add_child_notify(p_child); + top_layer->call_deferred("raise"); //top layer always on top! GraphNode *gn = p_child->cast_to<GraphNode>(); if (gn) { @@ -200,10 +223,14 @@ void GraphEdit::add_child_notify(Node *p_child) { _graph_node_moved(gn); gn->set_stop_mouse(false); } + + } void GraphEdit::remove_child_notify(Node *p_child) { + Control::remove_child_notify(p_child); + top_layer->call_deferred("raise"); //top layer always on top! GraphNode *gn = p_child->cast_to<GraphNode>(); if (gn) { @@ -228,18 +255,115 @@ void GraphEdit::_notification(int p_what) { h_scroll->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,hmin.height); h_scroll->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,0); + + zoom_minus->set_icon(get_icon("minus")); + zoom_reset->set_icon(get_icon("reset")); + zoom_plus->set_icon(get_icon("more")); + snap_button->set_icon(get_icon("snap")); // zoom_icon->set_texture( get_icon("Zoom", "EditorIcons")); } if (p_what==NOTIFICATION_DRAW) { + draw_style_box( get_stylebox("bg"),Rect2(Point2(),get_size()) ); VS::get_singleton()->canvas_item_set_clip(get_canvas_item(),true); + if (is_using_snap()) { + //draw grid + + int snap = get_snap(); + + Vector2 offset = get_scroll_ofs()/zoom; + Size2 size = get_size()/zoom; + + Point2i from = (offset/float(snap)).floor(); + Point2i len = (size/float(snap)).floor()+Vector2(1,1); + + Color grid_minor = get_color("grid_minor"); + Color grid_major = get_color("grid_major"); + + for(int i=from.x;i<from.x+len.x;i++) { + + Color color; + + if (ABS(i)%10==0) + color=grid_major; + else + color=grid_minor; + + float base_ofs = i*snap*zoom - offset.x*zoom; + draw_line(Vector2(base_ofs,0),Vector2(base_ofs,get_size().height),color); + } + + for(int i=from.y;i<from.y+len.y;i++) { + + Color color; + + if (ABS(i)%10==0) + color=grid_major; + else + color=grid_minor; + + float base_ofs = i*snap*zoom - offset.y*zoom; + draw_line(Vector2(0,base_ofs),Vector2(get_size().width,base_ofs),color); + } + + } + + { + //draw connections + List<List<Connection>::Element* > to_erase; + for(List<Connection>::Element *E=connections.front();E;E=E->next()) { + + NodePath fromnp(E->get().from); + + Node * from = get_node(fromnp); + if (!from) { + to_erase.push_back(E); + continue; + } + + GraphNode *gfrom = from->cast_to<GraphNode>(); + + if (!gfrom) { + to_erase.push_back(E); + continue; + } + + NodePath tonp(E->get().to); + Node * to = get_node(tonp); + if (!to) { + to_erase.push_back(E); + continue; + } + + GraphNode *gto = to->cast_to<GraphNode>(); + + if (!gto) { + to_erase.push_back(E); + continue; + } + + Vector2 frompos=gfrom->get_connection_output_pos(E->get().from_port)+gfrom->get_pos(); + Color color = gfrom->get_connection_output_color(E->get().from_port); + Vector2 topos=gto->get_connection_input_pos(E->get().to_port)+gto->get_pos(); + Color tocolor = gto->get_connection_input_color(E->get().to_port); + _draw_cos_line(this,frompos,topos,color,tocolor); + + } + + while(to_erase.size()) { + connections.erase(to_erase.front()->get()); + to_erase.pop_front(); + } + } + } if (p_what==NOTIFICATION_RESIZED) { _update_scroll(); top_layer->update(); + } } @@ -295,6 +419,36 @@ void GraphEdit::_top_layer_input(const InputEvent& p_ev) { Vector2 pos = gn->get_connection_output_pos(j)+gn->get_pos(); if (pos.distance_to(mpos)<grab_r) { + + if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) { + //check disconnect + for (List<Connection>::Element*E=connections.front();E;E=E->next()) { + + if (E->get().from==gn->get_name() && E->get().from_port==j) { + + Node*to = get_node(String(E->get().to)); + if (to && to->cast_to<GraphNode>()) { + + connecting_from=E->get().to; + connecting_index=E->get().to_port; + connecting_out=false; + connecting_type=to->cast_to<GraphNode>()->get_connection_input_type(E->get().to_port); + connecting_color=to->cast_to<GraphNode>()->get_connection_input_color(E->get().to_port); + connecting_target=false; + connecting_to=pos; + + emit_signal("disconnection_request",E->get().from,E->get().from_port,E->get().to,E->get().to_port); + to = get_node(String(connecting_from)); //maybe it was erased + if (to && to->cast_to<GraphNode>()) { + connecting=true; + } + return; + } + + } + } + } + connecting=true; connecting_from=gn->get_name(); connecting_index=j; @@ -315,7 +469,7 @@ void GraphEdit::_top_layer_input(const InputEvent& p_ev) { if (pos.distance_to(mpos)<grab_r) { - if (right_disconnects) { + if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) { //check disconnect for (List<Connection>::Element*E=connections.front();E;E=E->next()) { @@ -365,7 +519,7 @@ void GraphEdit::_top_layer_input(const InputEvent& p_ev) { connecting_to=Vector2(p_ev.mouse_motion.x,p_ev.mouse_motion.y); connecting_target=false; - top_layer->update(); + top_layer->update(); Ref<Texture> port =get_icon("port","GraphNode"); Vector2 mpos(p_ev.mouse_button.x,p_ev.mouse_button.y); @@ -381,7 +535,7 @@ void GraphEdit::_top_layer_input(const InputEvent& p_ev) { Vector2 pos = gn->get_connection_output_pos(j)+gn->get_pos(); int type =gn->get_connection_output_type(j); - if (type==connecting_type && pos.distance_to(mpos)<grab_r) { + if ((type==connecting_type ||valid_connection_types.has(ConnType(type,connecting_type))) && pos.distance_to(mpos)<grab_r) { connecting_target=true; connecting_to=pos; @@ -398,7 +552,7 @@ void GraphEdit::_top_layer_input(const InputEvent& p_ev) { Vector2 pos = gn->get_connection_input_pos(j)+gn->get_pos(); int type =gn->get_connection_input_type(j); - if (type==connecting_type && pos.distance_to(mpos)<grab_r) { + if ((type==connecting_type ||valid_connection_types.has(ConnType(type,connecting_type))) && pos.distance_to(mpos)<grab_r) { connecting_target=true; connecting_to=pos; connecting_target_to=gn->get_name(); @@ -425,18 +579,90 @@ void GraphEdit::_top_layer_input(const InputEvent& p_ev) { } emit_signal("connection_request",from,from_slot,to,to_slot); + } else { + String from = connecting_from; + int from_slot = connecting_index; + Vector2 ofs = Vector2(p_ev.mouse_button.x,p_ev.mouse_button.y); + emit_signal("connection_to_empty",from,from_slot,ofs); } connecting=false; top_layer->update(); + update(); } } -void GraphEdit::_draw_cos_line(const Vector2& p_from, const Vector2& p_to,const Color& p_color) { + +template<class Vector2> +static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, Vector2 start, Vector2 control_1, Vector2 control_2, Vector2 end) { + /* Formula from Wikipedia article on Bezier curves. */ + real_t omt = (1.0 - t); + real_t omt2 = omt*omt; + real_t omt3 = omt2*omt; + real_t t2 = t*t; + real_t t3 = t2*t; + + return start * omt3 + + control_1 * omt2 * t * 3.0 + + control_2 * omt * t2 * 3.0 + + end * t3; +} + + +void GraphEdit::_bake_segment2d(CanvasItem* p_where,float p_begin, float p_end,const Vector2& p_a,const Vector2& p_out,const Vector2& p_b, const Vector2& p_in,int p_depth,int p_min_depth,int p_max_depth,float p_tol,const Color& p_color,const Color& p_to_color,int &lines) const { + + float mp = p_begin+(p_end-p_begin)*0.5; + Vector2 beg = _bezier_interp(p_begin,p_a,p_a+p_out,p_b+p_in,p_b); + Vector2 mid = _bezier_interp(mp,p_a,p_a+p_out,p_b+p_in,p_b); + Vector2 end = _bezier_interp(p_end,p_a,p_a+p_out,p_b+p_in,p_b); + + Vector2 na = (mid-beg).normalized(); + Vector2 nb = (end-mid).normalized(); + float dp = Math::rad2deg(Math::acos(na.dot(nb))); + + if (p_depth>=p_min_depth && ( dp<p_tol || p_depth>=p_max_depth)) { + + + + p_where->draw_line(beg,end,p_color.linear_interpolate(p_to_color,mp),2); + lines++; + } else { + _bake_segment2d(p_where,p_begin,mp,p_a,p_out,p_b,p_in,p_depth+1,p_min_depth,p_max_depth,p_tol,p_color,p_to_color,lines); + _bake_segment2d(p_where,mp,p_end,p_a,p_out,p_b,p_in,p_depth+1,p_min_depth,p_max_depth,p_tol,p_color,p_to_color,lines); + } +} + + +void GraphEdit::_draw_cos_line(CanvasItem* p_where,const Vector2& p_from, const Vector2& p_to,const Color& p_color,const Color& p_to_color) { + +#if 1 + + //cubic bezier code + float diff = p_to.x-p_from.x; + float cp_offset; + int cp_len = get_constant("bezier_len_pos"); + int cp_neg_len = get_constant("bezier_len_neg"); + + if (diff>0) { + cp_offset=MAX(cp_len,diff*0.5); + } else { + cp_offset=MAX(MIN(cp_len-diff,cp_neg_len),-diff*0.5); + } + + Vector2 c1 = Vector2(cp_offset,0); + Vector2 c2 = Vector2(-cp_offset,0); + + int lines=0; + _bake_segment2d(p_where,0,1,p_from,c1,p_to,c2,0,5,12,8,p_color,p_to_color,lines); + //print_line("used lines: "+itos(lines)); + + +#else static const int steps = 20; + //old cosine code Rect2 r; r.pos=p_from; r.expand_to(p_to); @@ -446,6 +672,7 @@ void GraphEdit::_draw_cos_line(const Vector2& p_from, const Vector2& p_to,const Vector2 prev; for(int i=0;i<=steps;i++) { + float d = i/float(steps); float c=-Math::cos(d*Math_PI) * 0.5+0.5; if (flip) @@ -454,11 +681,12 @@ void GraphEdit::_draw_cos_line(const Vector2& p_from, const Vector2& p_to,const if (i>0) { - top_layer->draw_line(prev,p,p_color,2); + p_where->draw_line(prev,p,p_color.linear_interpolate(p_to_color,d),2); } prev=p; } +#endif } void GraphEdit::_top_layer_draw() { @@ -488,54 +716,28 @@ void GraphEdit::_top_layer_draw() { col.g+=0.4; col.b+=0.4; } - _draw_cos_line(pos,topos,col); - } - List<List<Connection>::Element* > to_erase; - for(List<Connection>::Element *E=connections.front();E;E=E->next()) { - - NodePath fromnp(E->get().from); - - Node * from = get_node(fromnp); - if (!from) { - to_erase.push_back(E); - continue; + if (!connecting_out) { + SWAP(pos,topos); } + _draw_cos_line(top_layer,pos,topos,col,col); + } - GraphNode *gfrom = from->cast_to<GraphNode>(); - if (!gfrom) { - to_erase.push_back(E); - continue; - } + if (box_selecting) + top_layer->draw_rect(box_selecting_rect,Color(0.7,0.7,1.0,0.3)); +} - NodePath tonp(E->get().to); - Node * to = get_node(tonp); - if (!to) { - to_erase.push_back(E); - continue; - } +void GraphEdit::set_selected(Node* p_child) { - GraphNode *gto = to->cast_to<GraphNode>(); + for(int i=get_child_count()-1;i>=0;i--) { - if (!gto) { - to_erase.push_back(E); + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn) continue; - } - - Vector2 frompos=gfrom->get_connection_output_pos(E->get().from_port)+gfrom->get_pos(); - Color color = gfrom->get_connection_output_color(E->get().from_port); - Vector2 topos=gto->get_connection_input_pos(E->get().to_port)+gto->get_pos(); - _draw_cos_line(frompos,topos,color); + gn->set_selected(gn==p_child); } - - while(to_erase.size()) { - connections.erase(to_erase.front()->get()); - to_erase.pop_front(); - } - if (box_selecting) - top_layer->draw_rect(box_selecting_rect,Color(0.7,0.7,1.0,0.3)); } void GraphEdit::_input_event(const InputEvent& p_ev) { @@ -548,11 +750,21 @@ void GraphEdit::_input_event(const InputEvent& p_ev) { if (p_ev.type==InputEvent::MOUSE_MOTION && dragging) { just_selected=true; - drag_accum+=Vector2(p_ev.mouse_motion.relative_x,p_ev.mouse_motion.relative_y); + // TODO: Remove local mouse pos hack if/when InputEventMouseMotion is fixed to support floats + //drag_accum+=Vector2(p_ev.mouse_motion.relative_x,p_ev.mouse_motion.relative_y); + drag_accum = get_local_mouse_pos() - drag_origin; for(int i=get_child_count()-1;i>=0;i--) { GraphNode *gn=get_child(i)->cast_to<GraphNode>(); - if (gn && gn->is_selected()) - gn->set_offset((gn->get_drag_from()*zoom+drag_accum)/zoom); + if (gn && gn->is_selected()) { + + Vector2 pos = (gn->get_drag_from()*zoom+drag_accum)/zoom; + if (is_using_snap()) { + int snap = get_snap(); + pos = pos.snapped(Vector2(snap,snap)); + } + + gn->set_offset(pos); + } } } @@ -641,6 +853,7 @@ void GraphEdit::_input_event(const InputEvent& p_ev) { dragging = false; top_layer->update(); + update(); } if (b.button_index==BUTTON_LEFT && b.pressed) { @@ -651,6 +864,10 @@ void GraphEdit::_input_event(const InputEvent& p_ev) { gn=get_child(i)->cast_to<GraphNode>(); if (gn) { + + if (gn->is_resizing()) + continue; + Rect2 r = gn->get_rect(); r.size*=zoom; if (r.has_point(get_local_mouse_pos())) @@ -665,6 +882,7 @@ void GraphEdit::_input_event(const InputEvent& p_ev) { dragging = true; drag_accum = Vector2(); + drag_origin = get_local_mouse_pos(); just_selected = !gn->is_selected(); if(!gn->is_selected() && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { for (int i = 0; i < get_child_count(); i++) { @@ -804,6 +1022,29 @@ bool GraphEdit::is_right_disconnects_enabled() const{ return right_disconnects; } +void GraphEdit::add_valid_right_disconnect_type(int p_type) { + + valid_right_disconnect_types.insert(p_type); +} + +void GraphEdit::remove_valid_right_disconnect_type(int p_type){ + + valid_right_disconnect_types.erase(p_type); + +} + +void GraphEdit::add_valid_left_disconnect_type(int p_type){ + + valid_left_disconnect_types.insert(p_type); + +} + +void GraphEdit::remove_valid_left_disconnect_type(int p_type){ + + valid_left_disconnect_types.erase(p_type); + +} + Array GraphEdit::_get_connection_list() const { List<Connection> conns; @@ -838,6 +1079,67 @@ void GraphEdit::_zoom_plus() { set_zoom(zoom*ZOOM_SCALE); } +void GraphEdit::add_valid_connection_type(int p_type,int p_with_type) { + + ConnType ct; + ct.type_a=p_type; + ct.type_b=p_with_type; + + valid_connection_types.insert(ct); +} + +void GraphEdit::remove_valid_connection_type(int p_type,int p_with_type) { + + ConnType ct; + ct.type_a=p_type; + ct.type_b=p_with_type; + + valid_connection_types.erase(ct); + +} + +bool GraphEdit::is_valid_connection_type(int p_type,int p_with_type) const { + + ConnType ct; + ct.type_a=p_type; + ct.type_b=p_with_type; + + return valid_connection_types.has(ct); + +} + +void GraphEdit::set_use_snap(bool p_enable) { + + snap_button->set_pressed(p_enable); + update(); +} + +bool GraphEdit::is_using_snap() const{ + + return snap_button->is_pressed(); + +} + +int GraphEdit::get_snap() const{ + + return snap_amount->get_val(); +} + +void GraphEdit::set_snap(int p_snap) { + + ERR_FAIL_COND(p_snap<5); + snap_amount->set_val(p_snap); + update(); +} +void GraphEdit::_snap_toggled() { + update(); +} + +void GraphEdit::_snap_value_changed(double) { + + update(); +} + void GraphEdit::_bind_methods() { @@ -846,10 +1148,17 @@ void GraphEdit::_bind_methods() { ObjectTypeDB::bind_method(_MD("disconnect_node","from","from_port","to","to_port"),&GraphEdit::disconnect_node); ObjectTypeDB::bind_method(_MD("get_connection_list"),&GraphEdit::_get_connection_list); ObjectTypeDB::bind_method(_MD("get_scroll_ofs"),&GraphEdit::get_scroll_ofs); + ObjectTypeDB::bind_method(_MD("set_scroll_ofs","ofs"),&GraphEdit::set_scroll_ofs); ObjectTypeDB::bind_method(_MD("set_zoom","p_zoom"),&GraphEdit::set_zoom); ObjectTypeDB::bind_method(_MD("get_zoom"),&GraphEdit::get_zoom); + ObjectTypeDB::bind_method(_MD("set_snap","pixels"),&GraphEdit::set_snap); + ObjectTypeDB::bind_method(_MD("get_snap"),&GraphEdit::get_snap); + + ObjectTypeDB::bind_method(_MD("set_use_snap","enable"),&GraphEdit::set_use_snap); + ObjectTypeDB::bind_method(_MD("is_using_snap"),&GraphEdit::is_using_snap); + ObjectTypeDB::bind_method(_MD("set_right_disconnects","enable"),&GraphEdit::set_right_disconnects); ObjectTypeDB::bind_method(_MD("is_right_disconnects_enabled"),&GraphEdit::is_right_disconnects_enabled); @@ -862,16 +1171,23 @@ void GraphEdit::_bind_methods() { ObjectTypeDB::bind_method(_MD("_zoom_minus"),&GraphEdit::_zoom_minus); ObjectTypeDB::bind_method(_MD("_zoom_reset"),&GraphEdit::_zoom_reset); ObjectTypeDB::bind_method(_MD("_zoom_plus"),&GraphEdit::_zoom_plus); + ObjectTypeDB::bind_method(_MD("_snap_toggled"),&GraphEdit::_snap_toggled); + ObjectTypeDB::bind_method(_MD("_snap_value_changed"),&GraphEdit::_snap_value_changed); ObjectTypeDB::bind_method(_MD("_input_event"),&GraphEdit::_input_event); + ObjectTypeDB::bind_method(_MD("set_selected","node"),&GraphEdit::set_selected); + ADD_SIGNAL(MethodInfo("connection_request",PropertyInfo(Variant::STRING,"from"),PropertyInfo(Variant::INT,"from_slot"),PropertyInfo(Variant::STRING,"to"),PropertyInfo(Variant::INT,"to_slot"))); ADD_SIGNAL(MethodInfo("disconnection_request",PropertyInfo(Variant::STRING,"from"),PropertyInfo(Variant::INT,"from_slot"),PropertyInfo(Variant::STRING,"to"),PropertyInfo(Variant::INT,"to_slot"))); ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2,"p_position"))); ADD_SIGNAL(MethodInfo("duplicate_nodes_request")); + ADD_SIGNAL(MethodInfo("node_selected",PropertyInfo(Variant::OBJECT,"node"))); + ADD_SIGNAL(MethodInfo("connection_to_empty",PropertyInfo(Variant::STRING,"from"),PropertyInfo(Variant::INT,"from_slot"),PropertyInfo(Variant::VECTOR2,"release_pos"))); ADD_SIGNAL(MethodInfo("delete_nodes_request")); ADD_SIGNAL(MethodInfo("_begin_node_move")); ADD_SIGNAL(MethodInfo("_end_node_move")); + ADD_SIGNAL(MethodInfo("scroll_offset_changed",PropertyInfo(Variant::VECTOR2,"ofs"))); } @@ -902,6 +1218,13 @@ GraphEdit::GraphEdit() { box_selecting = false; dragging = false; + //set large minmax so it can scroll even if not resized yet + h_scroll->set_min(-10000); + h_scroll->set_max(10000); + + v_scroll->set_min(-10000); + v_scroll->set_max(10000); + h_scroll->connect("value_changed", this,"_scroll_moved"); v_scroll->connect("value_changed", this,"_scroll_moved"); @@ -915,17 +1238,35 @@ GraphEdit::GraphEdit() { zoom_minus = memnew( ToolButton ); zoom_hb->add_child(zoom_minus); zoom_minus->connect("pressed",this,"_zoom_minus"); - zoom_minus->set_icon(get_icon("minus")); + zoom_minus->set_focus_mode(FOCUS_NONE); zoom_reset = memnew( ToolButton ); zoom_hb->add_child(zoom_reset); zoom_reset->connect("pressed",this,"_zoom_reset"); - zoom_reset->set_icon(get_icon("reset")); + zoom_reset->set_focus_mode(FOCUS_NONE); zoom_plus = memnew( ToolButton ); zoom_hb->add_child(zoom_plus); zoom_plus->connect("pressed",this,"_zoom_plus"); - zoom_plus->set_icon(get_icon("more")); + zoom_plus->set_focus_mode(FOCUS_NONE); + + snap_button = memnew( ToolButton ); + snap_button->set_toggle_mode(true); + snap_button->connect("pressed",this,"_snap_toggled"); + snap_button->set_pressed(true); + snap_button->set_focus_mode(FOCUS_NONE); + zoom_hb->add_child(snap_button); + + snap_amount = memnew( SpinBox ); + snap_amount->set_min(5); + snap_amount->set_max(100); + snap_amount->set_step(1); + snap_amount->set_val(20); + snap_amount->connect("value_changed",this,"_snap_value_changed"); + zoom_hb->add_child(snap_amount); + + setting_scroll_ofs=false; + } |