void GraphNode::_resort() { int sep=get_constant("separation"); Ref<StyleBox> sb=get_stylebox("frame"); bool first=true; Size2 minsize; for(int i=0;i<get_child_count();i++) { Control *c=get_child(i)->cast_to<Control>(); if (!c) continue; if (c->is_set_as_toplevel()) continue; Size2i size=c->get_combined_minimum_size(); minsize.y+=size.y; minsize.x=MAX(minsize.x,size.x); if (first) first=false; else minsize.y+=sep; } int vofs=0; int w = get_size().x - sb->get_minimum_size().x; cache_y.clear(); for(int i=0;i<get_child_count();i++) { Control *c=get_child(i)->cast_to<Control>(); if (!c) continue; if (c->is_set_as_toplevel()) continue; Size2i size=c->get_combined_minimum_size(); Rect2 r(sb->get_margin(MARGIN_LEFT),sb->get_margin(MARGIN_TOP)+vofs,w,size.y); fit_child_in_rect(c,r); cache_y.push_back(vofs+size.y*0.5); if (vofs>0) vofs+=sep; vofs+=size.y; } _change_notify(); update(); connpos_dirty=true; }
void ScrollContainer::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { call_deferred("_update_scrollbar_pos"); }; if (p_what == NOTIFICATION_SORT_CHILDREN) { child_max_size = Size2(0, 0); Size2 size = get_size(); if (h_scroll->is_visible_in_tree()) size.y -= h_scroll->get_minimum_size().y; if (v_scroll->is_visible_in_tree()) size.x -= h_scroll->get_minimum_size().x; for (int i = 0; i < get_child_count(); i++) { Control *c = get_child(i)->cast_to<Control>(); if (!c) continue; if (c->is_set_as_toplevel()) continue; if (c == h_scroll || c == v_scroll) continue; Size2 minsize = c->get_combined_minimum_size(); child_max_size.x = MAX(child_max_size.x, minsize.x); child_max_size.y = MAX(child_max_size.y, minsize.y); Rect2 r = Rect2(-scroll, minsize); if (!scroll_h || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) { r.position.x = 0; if (c->get_h_size_flags() & SIZE_EXPAND) r.size.width = MAX(size.width, minsize.width); else r.size.width = minsize.width; } if (!scroll_v || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) { r.position.y = 0; r.size.height = size.height; if (c->get_v_size_flags() & SIZE_EXPAND) r.size.height = MAX(size.height, minsize.height); else r.size.height = minsize.height; } fit_child_in_rect(c, r); } update(); }; if (p_what == NOTIFICATION_DRAW) { update_scrollbars(); } if (p_what == NOTIFICATION_FIXED_PROCESS) { if (drag_touching) { if (drag_touching_deaccel) { Vector2 pos = Vector2(h_scroll->get_value(), v_scroll->get_value()); pos += drag_speed * get_fixed_process_delta_time(); bool turnoff_h = false; bool turnoff_v = false; if (pos.x < 0) { pos.x = 0; turnoff_h = true; } if (pos.x > (h_scroll->get_max() - h_scroll->get_page())) { pos.x = h_scroll->get_max() - h_scroll->get_page(); turnoff_h = true; } if (pos.y < 0) { pos.y = 0; turnoff_v = true; } if (pos.y > (v_scroll->get_max() - v_scroll->get_page())) { pos.y = v_scroll->get_max() - v_scroll->get_page(); turnoff_v = true; } if (scroll_h) h_scroll->set_value(pos.x); if (scroll_v) v_scroll->set_value(pos.y); float sgn_x = drag_speed.x < 0 ? -1 : 1; float val_x = Math::abs(drag_speed.x); val_x -= 1000 * get_fixed_process_delta_time(); if (val_x < 0) { turnoff_h = true; } float sgn_y = drag_speed.y < 0 ? -1 : 1; float val_y = Math::abs(drag_speed.y); val_y -= 1000 * get_fixed_process_delta_time(); if (val_y < 0) { turnoff_v = true; } drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y); if (turnoff_h && turnoff_v) { set_fixed_process(false); drag_touching = false; drag_touching_deaccel = false; } } else { if (time_since_motion == 0 || time_since_motion > 0.1) { Vector2 diff = drag_accum - last_drag_accum; last_drag_accum = drag_accum; drag_speed = diff / get_fixed_process_delta_time(); } time_since_motion += get_fixed_process_delta_time(); } } } };
void BoxContainer::_resort() { /** First pass, determine minimum size AND amount of stretchable elements */ Size2i new_size=get_size();; int sep=get_constant("separation");//,vertical?"VBoxContainer":"HBoxContainer"); bool first=true; int children_count=0; int stretch_min=0; int stretch_avail=0; float stretch_ratio_total=0; Map<Control*,_MinSizeCache> min_size_cache; for(int i=0;i<get_child_count();i++) { Control *c=get_child(i)->cast_to<Control>(); if (!c || !c->is_visible()) continue; if (c->is_set_as_toplevel()) continue; Size2i size=c->get_combined_minimum_size(); _MinSizeCache msc; if (vertical) { /* VERTICAL */ stretch_min+=size.height; msc.min_size=size.height; msc.will_stretch=c->get_v_size_flags() & SIZE_EXPAND; } else { /* HORIZONTAL */ stretch_min+=size.width; msc.min_size=size.width; msc.will_stretch=c->get_h_size_flags() & SIZE_EXPAND; } if (msc.will_stretch) { stretch_avail+=msc.min_size; stretch_ratio_total+=c->get_stretch_ratio(); } msc.final_size=msc.min_size; min_size_cache[c]=msc; children_count++; } if (children_count==0) return; int stretch_max = (vertical? new_size.height : new_size.width ) - (children_count-1) * sep; int stretch_diff = stretch_max - stretch_min; if (stretch_diff<0) { //avoid negative stretch space stretch_max=stretch_min; stretch_diff=0; } stretch_avail+=stretch_diff; //available stretch space. /** Second, pass sucessively to discard elements that can't be stretched, this will run while stretchable elements exist */ bool has_stretched = false; while(stretch_ratio_total>0) { // first of all, dont even be here if no stretchable objects exist has_stretched = true; bool refit_successful=true; //assume refit-test will go well for(int i=0;i<get_child_count();i++) { Control *c=get_child(i)->cast_to<Control>(); if (!c || !c->is_visible()) continue; if (c->is_set_as_toplevel()) continue; ERR_FAIL_COND(!min_size_cache.has(c)); _MinSizeCache &msc=min_size_cache[c]; if (msc.will_stretch) { //wants to stretch //let's see if it can really stretch int final_pixel_size=stretch_avail * c->get_stretch_ratio() / stretch_ratio_total; if (final_pixel_size<msc.min_size) { //if available stretching area is too small for widget, //then remove it from stretching area msc.will_stretch=false; stretch_ratio_total-=c->get_stretch_ratio(); refit_successful=false; stretch_avail-=msc.min_size; msc.final_size=msc.min_size; break; } else { msc.final_size=final_pixel_size; } } } if (refit_successful) //uf refit went well, break break; } /** Final pass, draw and stretch elements **/ int ofs=0; if (!has_stretched) { switch (align) { case ALIGN_BEGIN: break; case ALIGN_CENTER: ofs = stretch_diff / 2; break; case ALIGN_END: ofs = stretch_diff; break; } } first=true; int idx=0; for(int i=0;i<get_child_count();i++) { Control *c=get_child(i)->cast_to<Control>(); if (!c || !c->is_visible()) continue; if (c->is_set_as_toplevel()) continue; _MinSizeCache &msc=min_size_cache[c]; if (first) first=false; else ofs+=sep; int from=ofs; int to=ofs+msc.final_size; if (msc.will_stretch && idx==children_count-1) { //adjust so the last one always fits perfect //compensating for numerical imprecision to=vertical?new_size.height:new_size.width; } int size=to-from; Rect2 rect; if (vertical) { rect=Rect2(0,from,new_size.width,size); } else { rect=Rect2(from,0,size,new_size.height); } fit_child_in_rect(c,rect); ofs=to; idx++; } }