Browse Source

Rework oriented containers

kobewi 3 years ago
parent
commit
73929bef73

+ 12 - 0
doc/classes/BoxContainer.xml

@@ -22,13 +22,25 @@
 		<member name="alignment" type="int" setter="set_alignment" getter="get_alignment" enum="BoxContainer.AlignmentMode" default="0">
 		<member name="alignment" type="int" setter="set_alignment" getter="get_alignment" enum="BoxContainer.AlignmentMode" default="0">
 			The alignment of the container's children (must be one of [constant ALIGNMENT_BEGIN], [constant ALIGNMENT_CENTER], or [constant ALIGNMENT_END]).
 			The alignment of the container's children (must be one of [constant ALIGNMENT_BEGIN], [constant ALIGNMENT_CENTER], or [constant ALIGNMENT_END]).
 		</member>
 		</member>
+		<member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" default="false">
+			If [code]true[/code], the [BoxContainer] will arrange its children vertically, rather than horizontally.
+			Can't be changed when using [HBoxContainer] and [VBoxContainer].
+		</member>
 	</members>
 	</members>
 	<constants>
 	<constants>
 		<constant name="ALIGNMENT_BEGIN" value="0" enum="AlignmentMode">
 		<constant name="ALIGNMENT_BEGIN" value="0" enum="AlignmentMode">
+			The child controls will be arranged at the beginning of the container, i.e. top if orientation is vertical, left if orientation is horizontal (right for RTL layout).
 		</constant>
 		</constant>
 		<constant name="ALIGNMENT_CENTER" value="1" enum="AlignmentMode">
 		<constant name="ALIGNMENT_CENTER" value="1" enum="AlignmentMode">
+			The child controls will be centered in the container.
 		</constant>
 		</constant>
 		<constant name="ALIGNMENT_END" value="2" enum="AlignmentMode">
 		<constant name="ALIGNMENT_END" value="2" enum="AlignmentMode">
+			The child controls will be arranged at the end of the container, i.e. bottom if orientation is vertical, right if orientation is horizontal (left for RTL layout).
 		</constant>
 		</constant>
 	</constants>
 	</constants>
+	<theme_items>
+		<theme_item name="separation" data_type="constant" type="int" default="4">
+			The space between the [BoxContainer]'s elements, in pixels.
+		</theme_item>
+	</theme_items>
 </class>
 </class>

+ 1 - 0
doc/classes/ColorPicker.xml

@@ -55,6 +55,7 @@
 		<member name="presets_visible" type="bool" setter="set_presets_visible" getter="are_presets_visible" default="true">
 		<member name="presets_visible" type="bool" setter="set_presets_visible" getter="are_presets_visible" default="true">
 			If [code]true[/code], saved color presets are visible.
 			If [code]true[/code], saved color presets are visible.
 		</member>
 		</member>
+		<member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" overrides="BoxContainer" default="true" />
 	</members>
 	</members>
 	<signals>
 	<signals>
 		<signal name="color_changed">
 		<signal name="color_changed">

+ 14 - 0
doc/classes/FlowContainer.xml

@@ -17,4 +17,18 @@
 			</description>
 			</description>
 		</method>
 		</method>
 	</methods>
 	</methods>
+	<members>
+		<member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" default="false">
+			If [code]true[/code], the [FlowContainer] will arrange its children vertically, rather than horizontally.
+			Can't be changed when using [HFlowContainer] and [VFlowContainer].
+		</member>
+	</members>
+	<theme_items>
+		<theme_item name="h_separation" data_type="constant" type="int" default="4">
+			The horizontal separation of children nodes.
+		</theme_item>
+		<theme_item name="v_separation" data_type="constant" type="int" default="4">
+			The vertical separation of children nodes.
+		</theme_item>
+	</theme_items>
 </class>
 </class>

+ 18 - 0
doc/classes/SplitContainer.xml

@@ -27,6 +27,10 @@
 		<member name="split_offset" type="int" setter="set_split_offset" getter="get_split_offset" default="0">
 		<member name="split_offset" type="int" setter="set_split_offset" getter="get_split_offset" default="0">
 			The initial offset of the splitting between the two [Control]s, with [code]0[/code] being at the end of the first [Control].
 			The initial offset of the splitting between the two [Control]s, with [code]0[/code] being at the end of the first [Control].
 		</member>
 		</member>
+		<member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" default="false">
+			If [code]true[/code], the [SplitContainer] will arrange its children vertically, rather than horizontally.
+			Can't be changed when using [HSplitContainer] and [VSplitContainer].
+		</member>
 	</members>
 	</members>
 	<signals>
 	<signals>
 		<signal name="dragged">
 		<signal name="dragged">
@@ -47,4 +51,18 @@
 			The split dragger is never visible and its space collapsed.
 			The split dragger is never visible and its space collapsed.
 		</constant>
 		</constant>
 	</constants>
 	</constants>
+	<theme_items>
+		<theme_item name="autohide" data_type="constant" type="int" default="1">
+			Boolean value. If 1 ([code]true[/code]), the grabber will hide automatically when it isn't under the cursor. If 0 ([code]false[/code]), it's always visible.
+		</theme_item>
+		<theme_item name="separation" data_type="constant" type="int" default="12">
+			The space between sides of the container.
+		</theme_item>
+		<theme_item name="h_grabber" data_type="icon" type="Texture2D">
+			The icon used for the grabber drawn in the middle area when [member vertical] is [code]false[/code].
+		</theme_item>
+		<theme_item name="v_grabber" data_type="icon" type="Texture2D">
+			The icon used for the grabber drawn in the middle area when [member vertical] is [code]true[/code].
+		</theme_item>
+	</theme_items>
 </class>
 </class>

+ 2 - 0
editor/editor_themes.cpp

@@ -1358,6 +1358,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
 	theme->set_color("selection_color", "TextEdit", selection_color);
 	theme->set_color("selection_color", "TextEdit", selection_color);
 	theme->set_constant("line_spacing", "TextEdit", 4 * EDSCALE);
 	theme->set_constant("line_spacing", "TextEdit", 4 * EDSCALE);
 
 
+	theme->set_icon("h_grabber", "SplitContainer", theme->get_icon(SNAME("GuiHsplitter"), SNAME("EditorIcons")));
+	theme->set_icon("v_grabber", "SplitContainer", theme->get_icon(SNAME("GuiVsplitter"), SNAME("EditorIcons")));
 	theme->set_icon("grabber", "VSplitContainer", theme->get_icon(SNAME("GuiVsplitter"), SNAME("EditorIcons")));
 	theme->set_icon("grabber", "VSplitContainer", theme->get_icon(SNAME("GuiVsplitter"), SNAME("EditorIcons")));
 	theme->set_icon("grabber", "HSplitContainer", theme->get_icon(SNAME("GuiHsplitter"), SNAME("EditorIcons")));
 	theme->set_icon("grabber", "HSplitContainer", theme->get_icon(SNAME("GuiHsplitter"), SNAME("EditorIcons")));
 
 

+ 1 - 0
editor/icons/BoxContainer.svg

@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.4520481 6.9086748c-.60273897.602739-.60272261 1.5799281 0 2.1826507l5.4566267 5.4566265c.602739.602739 1.5799281.602723 2.1826507 0l5.4566265-5.4566265c.602739-.602739.602723-1.5799281 0-2.1826507l-5.4566265-5.4566265c-.602739-.60273904-1.5799281-.60272304-2.1826507 0zm1.0913253 1.0913253 1.0913254-1.0913253 5.4566267 5.4566262-1.0913253 1.091326zm2.1826507-2.1826498 1.0913254-1.091326 5.4566265 5.4566267-1.091326 1.091325zm2.1826507-2.182651 1.0913254-1.091325 5.4566258 5.4566258-1.091325 1.0913254z" fill="#8eef97" stroke-width=".771684"/></svg>

+ 1 - 0
editor/icons/FlowContainer.svg

@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m1.4491431 6.9081906c-.59885747.5988575-.59885747 1.5847615 0 2.1836189l5.4590474 5.4590465c.5988576.598858 1.5847616.598858 2.183619.000001l5.4590465-5.4590475c.598858-.5988574.598858-1.5847614.000001-2.183619l-5.4590475-5.4590474c-.5988574-.59885747-1.5847614-.59885747-2.183619 0zm1.0918095 1.0918091 5.4590471-5.4590471 5.4590473 5.4590471-5.4590473 5.4590473zm1.6377142-.5459043c-.3024312.3024312-.3024312.7893781 0 1.0918092.3024313.3024311.7893783.3024311 1.0918095 0l1.0918095-1.0918093c.3024312-.3024312.3024312-.7893782 0-1.0918094-.3024312-.3024313-.7893782-.3024313-1.0918095 0zm2.7273401-2.7273401c-.3024312.3024311-.3024312.7893782 0 1.0918094.3024313.3024313.7893783.3024313 1.0918095 0l.5480882-.5480884c.3024311-.3024311.3024319-.7893783 0-1.0918094-.3024311-.3024313-.789378-.3024313-1.0918093 0zm-1.0896258 4.3650542c-.3024313.3024311-.3024313.7893779 0 1.0918095.3024312.302431.7893781.302431 1.0918094 0 .3024312-.3024316.3024313-.7893784 0-1.0918095-.3024311-.3024311-.7893781-.3024311-1.0918094 0zm1.6377142-1.6377142c-.3024313.3024313-.3024313.7893782 0 1.0918093s.7893782.3024311 1.0918093 0l1.6377144-1.637714c.302431-.3024312.302431-.7893783 0-1.0918096-.3024316-.3024312-.7893784-.3024311-1.0918095.0000002zm-.00218 3.2776127c-.3024312.302431-.3024313.789377-.0000001 1.091809.3024312.302431.7893782.302431 1.0918093 0l2.1858035-2.1858026c.302431-.3024311.302431-.7893787 0-1.0918098s-.7893792-.3024311-1.0918103 0z" fill="#8eef97" fill-rule="nonzero" stroke-width=".772026"/></svg>

+ 1 - 0
editor/icons/SplitContainer.svg

@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.452048 6.9086746c-.60273888.6027391-.60272252 1.5799284 0 2.1826511l5.4566266 5.4566263c.6027391.602739 1.5799284.602722 2.1826511 0l5.4566263-5.4566263c.602739-.6027397.602722-1.5799284 0-2.1826511l-5.4566263-5.4566266c-.6027397-.60273888-1.5799284-.60272252-2.1826511 0zm1.0913254 1.0913254 2.1826506-2.1826506 1.636988 1.6369879v2.1826504h2.1826508l1.6369882 1.6369883-2.182651 2.182651zm3.273976-3.273976 2.1826506-2.1826506 5.456627 5.4566266-2.182651 2.182651-1.6369883-1.6369882v-2.1826508h-2.1826504z" fill="#8eef97" stroke-width=".771683"/></svg>

+ 22 - 2
scene/gui/box_container.cpp

@@ -291,7 +291,7 @@ Size2 BoxContainer::get_minimum_size() const {
 void BoxContainer::_update_theme_item_cache() {
 void BoxContainer::_update_theme_item_cache() {
 	Container::_update_theme_item_cache();
 	Container::_update_theme_item_cache();
 
 
-	theme_cache.separation = get_theme_constant(SNAME("separation")); //,vertical?"VBoxContainer":"HBoxContainer");
+	theme_cache.separation = get_theme_constant(SNAME("separation"));
 }
 }
 
 
 void BoxContainer::_notification(int p_what) {
 void BoxContainer::_notification(int p_what) {
@@ -311,6 +311,12 @@ void BoxContainer::_notification(int p_what) {
 	}
 	}
 }
 }
 
 
+void BoxContainer::_validate_property(PropertyInfo &p_property) const {
+	if (is_fixed && p_property.name == "vertical") {
+		p_property.usage = PROPERTY_USAGE_NONE;
+	}
+}
+
 void BoxContainer::set_alignment(AlignmentMode p_alignment) {
 void BoxContainer::set_alignment(AlignmentMode p_alignment) {
 	if (alignment == p_alignment) {
 	if (alignment == p_alignment) {
 		return;
 		return;
@@ -323,6 +329,17 @@ BoxContainer::AlignmentMode BoxContainer::get_alignment() const {
 	return alignment;
 	return alignment;
 }
 }
 
 
+void BoxContainer::set_vertical(bool p_vertical) {
+	ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + ".");
+	vertical = p_vertical;
+	update_minimum_size();
+	_resort();
+}
+
+bool BoxContainer::is_vertical() const {
+	return vertical;
+}
+
 Control *BoxContainer::add_spacer(bool p_begin) {
 Control *BoxContainer::add_spacer(bool p_begin) {
 	Control *c = memnew(Control);
 	Control *c = memnew(Control);
 	c->set_mouse_filter(MOUSE_FILTER_PASS); //allow spacer to pass mouse events
 	c->set_mouse_filter(MOUSE_FILTER_PASS); //allow spacer to pass mouse events
@@ -371,14 +388,17 @@ BoxContainer::BoxContainer(bool p_vertical) {
 
 
 void BoxContainer::_bind_methods() {
 void BoxContainer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("add_spacer", "begin"), &BoxContainer::add_spacer);
 	ClassDB::bind_method(D_METHOD("add_spacer", "begin"), &BoxContainer::add_spacer);
-	ClassDB::bind_method(D_METHOD("get_alignment"), &BoxContainer::get_alignment);
 	ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &BoxContainer::set_alignment);
 	ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &BoxContainer::set_alignment);
+	ClassDB::bind_method(D_METHOD("get_alignment"), &BoxContainer::get_alignment);
+	ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &BoxContainer::set_vertical);
+	ClassDB::bind_method(D_METHOD("is_vertical"), &BoxContainer::is_vertical);
 
 
 	BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN);
 	BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN);
 	BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
 	BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
 	BIND_ENUM_CONSTANT(ALIGNMENT_END);
 	BIND_ENUM_CONSTANT(ALIGNMENT_END);
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
 }
 }
 
 
 MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control *p_control, bool p_expand) {
 MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control *p_control, bool p_expand) {

+ 8 - 2
scene/gui/box_container.h

@@ -54,9 +54,12 @@ private:
 	void _resort();
 	void _resort();
 
 
 protected:
 protected:
+	bool is_fixed = false;
+
 	virtual void _update_theme_item_cache() override;
 	virtual void _update_theme_item_cache() override;
 
 
 	void _notification(int p_what);
 	void _notification(int p_what);
+	void _validate_property(PropertyInfo &p_property) const;
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
@@ -65,6 +68,9 @@ public:
 	void set_alignment(AlignmentMode p_alignment);
 	void set_alignment(AlignmentMode p_alignment);
 	AlignmentMode get_alignment() const;
 	AlignmentMode get_alignment() const;
 
 
+	void set_vertical(bool p_vertical);
+	bool is_vertical() const;
+
 	virtual Size2 get_minimum_size() const override;
 	virtual Size2 get_minimum_size() const override;
 
 
 	virtual Vector<int> get_allowed_size_flags_horizontal() const override;
 	virtual Vector<int> get_allowed_size_flags_horizontal() const override;
@@ -78,7 +84,7 @@ class HBoxContainer : public BoxContainer {
 
 
 public:
 public:
 	HBoxContainer() :
 	HBoxContainer() :
-			BoxContainer(false) {}
+			BoxContainer(false) { is_fixed = true; }
 };
 };
 
 
 class MarginContainer;
 class MarginContainer;
@@ -89,7 +95,7 @@ public:
 	MarginContainer *add_margin_child(const String &p_label, Control *p_control, bool p_expand = false);
 	MarginContainer *add_margin_child(const String &p_label, Control *p_control, bool p_expand = false);
 
 
 	VBoxContainer() :
 	VBoxContainer() :
-			BoxContainer(true) {}
+			BoxContainer(true) { is_fixed = true; }
 };
 };
 
 
 VARIANT_ENUM_CAST(BoxContainer::AlignmentMode);
 VARIANT_ENUM_CAST(BoxContainer::AlignmentMode);

+ 22 - 0
scene/gui/flow_container.cpp

@@ -272,14 +272,36 @@ void FlowContainer::_notification(int p_what) {
 	}
 	}
 }
 }
 
 
+void FlowContainer::_validate_property(PropertyInfo &p_property) const {
+	if (is_fixed && p_property.name == "vertical") {
+		p_property.usage = PROPERTY_USAGE_NONE;
+	}
+}
+
 int FlowContainer::get_line_count() const {
 int FlowContainer::get_line_count() const {
 	return cached_line_count;
 	return cached_line_count;
 }
 }
 
 
+void FlowContainer::set_vertical(bool p_vertical) {
+	ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + ".");
+	vertical = p_vertical;
+	update_minimum_size();
+	_resort();
+}
+
+bool FlowContainer::is_vertical() const {
+	return vertical;
+}
+
 FlowContainer::FlowContainer(bool p_vertical) {
 FlowContainer::FlowContainer(bool p_vertical) {
 	vertical = p_vertical;
 	vertical = p_vertical;
 }
 }
 
 
 void FlowContainer::_bind_methods() {
 void FlowContainer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count);
 	ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count);
+
+	ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &FlowContainer::set_vertical);
+	ClassDB::bind_method(D_METHOD("is_vertical"), &FlowContainer::is_vertical);
+
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
 }
 }

+ 8 - 2
scene/gui/flow_container.h

@@ -50,14 +50,20 @@ private:
 	void _resort();
 	void _resort();
 
 
 protected:
 protected:
+	bool is_fixed = false;
+
 	virtual void _update_theme_item_cache() override;
 	virtual void _update_theme_item_cache() override;
 
 
 	void _notification(int p_what);
 	void _notification(int p_what);
+	void _validate_property(PropertyInfo &p_property) const;
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
 	int get_line_count() const;
 	int get_line_count() const;
 
 
+	void set_vertical(bool p_vertical);
+	bool is_vertical() const;
+
 	virtual Size2 get_minimum_size() const override;
 	virtual Size2 get_minimum_size() const override;
 
 
 	virtual Vector<int> get_allowed_size_flags_horizontal() const override;
 	virtual Vector<int> get_allowed_size_flags_horizontal() const override;
@@ -71,7 +77,7 @@ class HFlowContainer : public FlowContainer {
 
 
 public:
 public:
 	HFlowContainer() :
 	HFlowContainer() :
-			FlowContainer(false) {}
+			FlowContainer(false) { is_fixed = true; }
 };
 };
 
 
 class VFlowContainer : public FlowContainer {
 class VFlowContainer : public FlowContainer {
@@ -79,7 +85,7 @@ class VFlowContainer : public FlowContainer {
 
 
 public:
 public:
 	VFlowContainer() :
 	VFlowContainer() :
-			FlowContainer(true) {}
+			FlowContainer(true) { is_fixed = true; }
 };
 };
 
 
 #endif // FLOW_CONTAINER_H
 #endif // FLOW_CONTAINER_H

+ 39 - 4
scene/gui/split_container.cpp

@@ -55,6 +55,18 @@ Control *SplitContainer::_getch(int p_idx) const {
 	return nullptr;
 	return nullptr;
 }
 }
 
 
+Ref<Texture2D> SplitContainer::_get_grabber_icon() const {
+	if (is_fixed) {
+		return theme_cache.grabber_icon;
+	} else {
+		if (vertical) {
+			return theme_cache.grabber_icon_v;
+		} else {
+			return theme_cache.grabber_icon_h;
+		}
+	}
+}
+
 void SplitContainer::_resort() {
 void SplitContainer::_resort() {
 	int axis = vertical ? 1 : 0;
 	int axis = vertical ? 1 : 0;
 
 
@@ -76,7 +88,8 @@ void SplitContainer::_resort() {
 	bool second_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND;
 	bool second_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND;
 
 
 	// Determine the separation between items
 	// Determine the separation between items
-	int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? theme_cache.grabber_icon->get_height() : theme_cache.grabber_icon->get_width()) : 0;
+	Ref<Texture2D> g = _get_grabber_icon();
+	int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0;
 
 
 	// Compute the minimum size
 	// Compute the minimum size
 	Size2 ms_first = first->get_combined_minimum_size();
 	Size2 ms_first = first->get_combined_minimum_size();
@@ -126,10 +139,9 @@ void SplitContainer::_resort() {
 }
 }
 
 
 Size2 SplitContainer::get_minimum_size() const {
 Size2 SplitContainer::get_minimum_size() const {
-	/* Calculate MINIMUM SIZE */
-
 	Size2i minimum;
 	Size2i minimum;
-	int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? theme_cache.grabber_icon->get_height() : theme_cache.grabber_icon->get_width()) : 0;
+	Ref<Texture2D> g = _get_grabber_icon();
+	int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0;
 
 
 	for (int i = 0; i < 2; i++) {
 	for (int i = 0; i < 2; i++) {
 		if (!_getch(i)) {
 		if (!_getch(i)) {
@@ -164,6 +176,8 @@ void SplitContainer::_update_theme_item_cache() {
 	theme_cache.separation = get_theme_constant(SNAME("separation"));
 	theme_cache.separation = get_theme_constant(SNAME("separation"));
 	theme_cache.autohide = get_theme_constant(SNAME("autohide"));
 	theme_cache.autohide = get_theme_constant(SNAME("autohide"));
 	theme_cache.grabber_icon = get_theme_icon(SNAME("grabber"));
 	theme_cache.grabber_icon = get_theme_icon(SNAME("grabber"));
+	theme_cache.grabber_icon_h = get_theme_icon(SNAME("h_grabber"));
+	theme_cache.grabber_icon_v = get_theme_icon(SNAME("v_grabber"));
 }
 }
 
 
 void SplitContainer::_notification(int p_what) {
 void SplitContainer::_notification(int p_what) {
@@ -214,6 +228,12 @@ void SplitContainer::_notification(int p_what) {
 	}
 	}
 }
 }
 
 
+void SplitContainer::_validate_property(PropertyInfo &p_property) const {
+	if (is_fixed && p_property.name == "vertical") {
+		p_property.usage = PROPERTY_USAGE_NONE;
+	}
+}
+
 void SplitContainer::gui_input(const Ref<InputEvent> &p_event) {
 void SplitContainer::gui_input(const Ref<InputEvent> &p_event) {
 	ERR_FAIL_COND(p_event.is_null());
 	ERR_FAIL_COND(p_event.is_null());
 
 
@@ -344,6 +364,17 @@ bool SplitContainer::is_collapsed() const {
 	return collapsed;
 	return collapsed;
 }
 }
 
 
+void SplitContainer::set_vertical(bool p_vertical) {
+	ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + ".");
+	vertical = p_vertical;
+	update_minimum_size();
+	_resort();
+}
+
+bool SplitContainer::is_vertical() const {
+	return vertical;
+}
+
 Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const {
 Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const {
 	Vector<int> flags;
 	Vector<int> flags;
 	flags.append(SIZE_FILL);
 	flags.append(SIZE_FILL);
@@ -379,11 +410,15 @@ void SplitContainer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_dragger_visibility", "mode"), &SplitContainer::set_dragger_visibility);
 	ClassDB::bind_method(D_METHOD("set_dragger_visibility", "mode"), &SplitContainer::set_dragger_visibility);
 	ClassDB::bind_method(D_METHOD("get_dragger_visibility"), &SplitContainer::get_dragger_visibility);
 	ClassDB::bind_method(D_METHOD("get_dragger_visibility"), &SplitContainer::get_dragger_visibility);
 
 
+	ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &SplitContainer::set_vertical);
+	ClassDB::bind_method(D_METHOD("is_vertical"), &SplitContainer::is_vertical);
+
 	ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::INT, "offset")));
 	ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::INT, "offset")));
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "split_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_split_offset", "get_split_offset");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "split_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_split_offset", "get_split_offset");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collapsed"), "set_collapsed", "is_collapsed");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collapsed"), "set_collapsed", "is_collapsed");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "dragger_visibility", PROPERTY_HINT_ENUM, "Visible,Hidden,Hidden and Collapsed"), "set_dragger_visibility", "get_dragger_visibility");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "dragger_visibility", PROPERTY_HINT_ENUM, "Visible,Hidden,Hidden and Collapsed"), "set_dragger_visibility", "get_dragger_visibility");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
 
 
 	BIND_ENUM_CONSTANT(DRAGGER_VISIBLE);
 	BIND_ENUM_CONSTANT(DRAGGER_VISIBLE);
 	BIND_ENUM_CONSTANT(DRAGGER_HIDDEN);
 	BIND_ENUM_CONSTANT(DRAGGER_HIDDEN);

+ 11 - 2
scene/gui/split_container.h

@@ -59,17 +59,23 @@ private:
 		int separation = 0;
 		int separation = 0;
 		int autohide = 0;
 		int autohide = 0;
 		Ref<Texture2D> grabber_icon;
 		Ref<Texture2D> grabber_icon;
+		Ref<Texture2D> grabber_icon_h;
+		Ref<Texture2D> grabber_icon_v;
 	} theme_cache;
 	} theme_cache;
 
 
 	Control *_getch(int p_idx) const;
 	Control *_getch(int p_idx) const;
 
 
+	Ref<Texture2D> _get_grabber_icon() const;
 	void _resort();
 	void _resort();
 
 
 protected:
 protected:
+	bool is_fixed = false;
+
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;
 	virtual void _update_theme_item_cache() override;
 	virtual void _update_theme_item_cache() override;
 
 
 	void _notification(int p_what);
 	void _notification(int p_what);
+	void _validate_property(PropertyInfo &p_property) const;
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
@@ -83,6 +89,9 @@ public:
 	void set_dragger_visibility(DraggerVisibility p_visibility);
 	void set_dragger_visibility(DraggerVisibility p_visibility);
 	DraggerVisibility get_dragger_visibility() const;
 	DraggerVisibility get_dragger_visibility() const;
 
 
+	void set_vertical(bool p_vertical);
+	bool is_vertical() const;
+
 	virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
 	virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
 
 
 	virtual Size2 get_minimum_size() const override;
 	virtual Size2 get_minimum_size() const override;
@@ -100,7 +109,7 @@ class HSplitContainer : public SplitContainer {
 
 
 public:
 public:
 	HSplitContainer() :
 	HSplitContainer() :
-			SplitContainer(false) {}
+			SplitContainer(false) { is_fixed = true; }
 };
 };
 
 
 class VSplitContainer : public SplitContainer {
 class VSplitContainer : public SplitContainer {
@@ -108,7 +117,7 @@ class VSplitContainer : public SplitContainer {
 
 
 public:
 public:
 	VSplitContainer() :
 	VSplitContainer() :
-			SplitContainer(true) {}
+			SplitContainer(true) { is_fixed = true; }
 };
 };
 
 
 #endif // SPLIT_CONTAINER_H
 #endif // SPLIT_CONTAINER_H

+ 3 - 3
scene/register_scene_types.cpp

@@ -378,14 +378,14 @@ void register_scene_types() {
 	GDREGISTER_CLASS(VSeparator);
 	GDREGISTER_CLASS(VSeparator);
 	GDREGISTER_CLASS(TextureButton);
 	GDREGISTER_CLASS(TextureButton);
 	GDREGISTER_CLASS(Container);
 	GDREGISTER_CLASS(Container);
-	GDREGISTER_ABSTRACT_CLASS(BoxContainer);
+	GDREGISTER_CLASS(BoxContainer);
 	GDREGISTER_CLASS(HBoxContainer);
 	GDREGISTER_CLASS(HBoxContainer);
 	GDREGISTER_CLASS(VBoxContainer);
 	GDREGISTER_CLASS(VBoxContainer);
 	GDREGISTER_CLASS(GridContainer);
 	GDREGISTER_CLASS(GridContainer);
 	GDREGISTER_CLASS(CenterContainer);
 	GDREGISTER_CLASS(CenterContainer);
 	GDREGISTER_CLASS(ScrollContainer);
 	GDREGISTER_CLASS(ScrollContainer);
 	GDREGISTER_CLASS(PanelContainer);
 	GDREGISTER_CLASS(PanelContainer);
-	GDREGISTER_ABSTRACT_CLASS(FlowContainer);
+	GDREGISTER_CLASS(FlowContainer);
 	GDREGISTER_CLASS(HFlowContainer);
 	GDREGISTER_CLASS(HFlowContainer);
 	GDREGISTER_CLASS(VFlowContainer);
 	GDREGISTER_CLASS(VFlowContainer);
 
 
@@ -422,7 +422,7 @@ void register_scene_types() {
 
 
 	GDREGISTER_CLASS(MarginContainer);
 	GDREGISTER_CLASS(MarginContainer);
 	GDREGISTER_CLASS(SubViewportContainer);
 	GDREGISTER_CLASS(SubViewportContainer);
-	GDREGISTER_ABSTRACT_CLASS(SplitContainer);
+	GDREGISTER_CLASS(SplitContainer);
 	GDREGISTER_CLASS(HSplitContainer);
 	GDREGISTER_CLASS(HSplitContainer);
 	GDREGISTER_CLASS(VSplitContainer);
 	GDREGISTER_CLASS(VSplitContainer);
 
 

+ 7 - 0
scene/resources/default_theme/default_theme.cpp

@@ -996,9 +996,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
 
 
 	// Containers
 	// Containers
 
 
+	theme->set_icon("h_grabber", "SplitContainer", icons["hsplitter"]);
+	theme->set_icon("v_grabber", "SplitContainer", icons["vsplitter"]);
 	theme->set_icon("grabber", "VSplitContainer", icons["vsplitter"]);
 	theme->set_icon("grabber", "VSplitContainer", icons["vsplitter"]);
 	theme->set_icon("grabber", "HSplitContainer", icons["hsplitter"]);
 	theme->set_icon("grabber", "HSplitContainer", icons["hsplitter"]);
 
 
+	theme->set_constant("separation", "BoxContainer", 4 * scale);
 	theme->set_constant("separation", "HBoxContainer", 4 * scale);
 	theme->set_constant("separation", "HBoxContainer", 4 * scale);
 	theme->set_constant("separation", "VBoxContainer", 4 * scale);
 	theme->set_constant("separation", "VBoxContainer", 4 * scale);
 	theme->set_constant("margin_left", "MarginContainer", 0 * scale);
 	theme->set_constant("margin_left", "MarginContainer", 0 * scale);
@@ -1007,10 +1010,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
 	theme->set_constant("margin_bottom", "MarginContainer", 0 * scale);
 	theme->set_constant("margin_bottom", "MarginContainer", 0 * scale);
 	theme->set_constant("h_separation", "GridContainer", 4 * scale);
 	theme->set_constant("h_separation", "GridContainer", 4 * scale);
 	theme->set_constant("v_separation", "GridContainer", 4 * scale);
 	theme->set_constant("v_separation", "GridContainer", 4 * scale);
+	theme->set_constant("separation", "SplitContainer", 12 * scale);
 	theme->set_constant("separation", "HSplitContainer", 12 * scale);
 	theme->set_constant("separation", "HSplitContainer", 12 * scale);
 	theme->set_constant("separation", "VSplitContainer", 12 * scale);
 	theme->set_constant("separation", "VSplitContainer", 12 * scale);
+	theme->set_constant("autohide", "SplitContainer", 1 * scale);
 	theme->set_constant("autohide", "HSplitContainer", 1 * scale);
 	theme->set_constant("autohide", "HSplitContainer", 1 * scale);
 	theme->set_constant("autohide", "VSplitContainer", 1 * scale);
 	theme->set_constant("autohide", "VSplitContainer", 1 * scale);
+	theme->set_constant("h_separation", "FlowContainer", 4 * scale);
+	theme->set_constant("v_separation", "FlowContainer", 4 * scale);
 	theme->set_constant("h_separation", "HFlowContainer", 4 * scale);
 	theme->set_constant("h_separation", "HFlowContainer", 4 * scale);
 	theme->set_constant("v_separation", "HFlowContainer", 4 * scale);
 	theme->set_constant("v_separation", "HFlowContainer", 4 * scale);
 	theme->set_constant("h_separation", "VFlowContainer", 4 * scale);
 	theme->set_constant("h_separation", "VFlowContainer", 4 * scale);