Browse Source

Added intrinsic ratio for replaced elements. Replaced elements now follow the normal CSS sizing rules, that is, padding and borders are no longer subtracted from the width and height of the element by default.

Some img and input elements with paddings or borders may need adjustments.
Michael Ragazzon 5 years ago
parent
commit
cf935e077a

+ 3 - 2
Include/RmlUi/Core/Element.h

@@ -164,9 +164,10 @@ public:
 	virtual float GetBaseline() const;
 	virtual float GetBaseline() const;
 	/// Gets the intrinsic dimensions of this element, if it is of a type that has an inherent size. This size will
 	/// Gets the intrinsic dimensions of this element, if it is of a type that has an inherent size. This size will
 	/// only be overriden by a styled width or height.
 	/// only be overriden by a styled width or height.
-	/// @param[in] dimensions The dimensions to size, if appropriate.
+	/// @param[out] dimensions The dimensions to size, if appropriate.
+	/// @param[out] ratio The intrinsic ratio (width/height), if appropriate.
 	/// @return True if the element has intrinsic dimensions, false otherwise. The default element will return false.
 	/// @return True if the element has intrinsic dimensions, false otherwise. The default element will return false.
-	virtual bool GetIntrinsicDimensions(Vector2f& dimensions);
+	virtual bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio);
 
 
 	/// Checks if a given point in screen coordinates lies within the bordered area of this element.
 	/// Checks if a given point in screen coordinates lies within the bordered area of this element.
 	/// @param[in] point The point to test.
 	/// @param[in] point The point to test.

+ 1 - 1
Include/RmlUi/Core/Elements/ElementFormControlInput.h

@@ -91,7 +91,7 @@ protected:
 
 
 	/// Sizes the dimensions to the element's inherent size.
 	/// Sizes the dimensions to the element's inherent size.
 	/// @return True.
 	/// @return True.
-	bool GetIntrinsicDimensions(Vector2f& dimensions) override;
+	bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;
 
 
 private:
 private:
 	InputType* type;
 	InputType* type;

+ 2 - 1
Include/RmlUi/Core/Elements/ElementFormControlSelect.h

@@ -99,8 +99,9 @@ protected:
 
 
 	/// Returns true to mark this element as replaced.
 	/// Returns true to mark this element as replaced.
 	/// @param[out] intrinsic_dimensions Set to the arbitrary dimensions of 128 x 16 just to give this element a size. Resize with the 'width' and 'height' properties.
 	/// @param[out] intrinsic_dimensions Set to the arbitrary dimensions of 128 x 16 just to give this element a size. Resize with the 'width' and 'height' properties.
+	/// @param[out] intrinsic_ratio Ignored.
 	/// @return True.
 	/// @return True.
-	bool GetIntrinsicDimensions(Vector2f& intrinsic_dimensions) override;
+	bool GetIntrinsicDimensions(Vector2f& intrinsic_dimensions, float& intrinsic_ratio) override;
 
 
 	WidgetDropDown* widget;
 	WidgetDropDown* widget;
 };
 };

+ 1 - 1
Include/RmlUi/Core/Elements/ElementFormControlTextArea.h

@@ -93,7 +93,7 @@ public:
 
 
 	/// Returns the control's inherent size, based on the length of the input field and the current font size.
 	/// Returns the control's inherent size, based on the length of the input field and the current font size.
 	/// @return True.
 	/// @return True.
-	bool GetIntrinsicDimensions(Vector2f& dimensions) override;
+	bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;
 
 
 protected:
 protected:
 	/// Updates the control's widget.
 	/// Updates the control's widget.

+ 2 - 0
Samples/assets/invader.rcss

@@ -294,6 +294,7 @@ input.submit:disabled
 
 
 input.text, input.password
 input.text, input.password
 {
 {
+	box-sizing: border-box;
 	height: 31px;
 	height: 31px;
 	padding: 10px 10px 0px;
 	padding: 10px 10px 0px;
 	decorator: tiled-horizontal( text-l, text-c, auto ); /* Right becomes mirrored left */
 	decorator: tiled-horizontal( text-l, text-c, auto ); /* Right becomes mirrored left */
@@ -319,6 +320,7 @@ textarea
 
 
 datagrid input.text
 datagrid input.text
 {
 {
+	box-sizing: border-box;
 	width: 100%;
 	width: 100%;
 	height: 18px;
 	height: 18px;
 	margin: 0;
 	margin: 0;

+ 15 - 9
Samples/basic/databinding/data/databinding.rml

@@ -107,14 +107,20 @@ p.title
 
 
 /***  Forms  ***/
 /***  Forms  ***/
 
 
-form
-{
+form {
 	display: block;
 	display: block;
 	text-align: left;
 	text-align: left;
 }
 }
-form input, form select { margin-left: 0; }
-form h2 
-{
+form input, form select {
+	margin-left: 0;
+}
+form input.text, form input.password {
+	width: 35%;
+}
+input.text.two-wide {
+	width: 70%;
+}
+form h2 {
 	display: block;
 	display: block;
 	font-size: 16px;
 	font-size: 16px;
 	font-weight: bold;
 	font-weight: bold;
@@ -191,12 +197,12 @@ form h2
 	<form onsubmit="submit_form">
 	<form onsubmit="submit_form">
 		<h2>Full name</h2>
 		<h2>Full name</h2>
 		<div>
 		<div>
-			<input type="text" size="20" name="name"/>
+			<input class="two-wide" type="text" name="name"/>
 		</div>
 		</div>
 		<h2>Email and password</h2>
 		<h2>Email and password</h2>
 		<div>
 		<div>
-			<input type="text" size="10" name="email"/>
-			<input type="password" size="10" name="password"/>
+			<input type="text" name="email"/>
+			<input type="password" name="password"/>
 		</div>
 		</div>
 		<h2>Favorite animal</h2>
 		<h2>Favorite animal</h2>
 		<div>
 		<div>
@@ -227,7 +233,7 @@ form h2
 		</div>
 		</div>
 		<h2>Message</h2>
 		<h2>Message</h2>
 		<div>
 		<div>
-			<textarea cols="30" rows="5" wrap="nowrap" name="message">😍 Hello 🌐 World! 😎</textarea>
+			<textarea cols="25" rows="4" wrap="nowrap" name="message">😍 Hello 🌐 World! 😎</textarea>
 		</div>
 		</div>
 		<div style="margin-bottom: 15px;">
 		<div style="margin-bottom: 15px;">
 			<input type="submit">Submit</input>
 			<input type="submit">Submit</input>

+ 17 - 9
Samples/basic/demo/data/demo.rml

@@ -421,14 +421,20 @@ p.title
 
 
 /***  Forms  ***/
 /***  Forms  ***/
 
 
-form
-{
+form {
 	display: block;
 	display: block;
 	text-align: left;
 	text-align: left;
 }
 }
-form input, form select { margin-left: 0; }
-form h2 
-{
+form input, form select {
+	margin-left: 0;
+}
+input.text, input.password {
+	width: 35%;
+}
+input.text.two-wide {
+	width: 70%;
+}
+form h2 {
 	display: block;
 	display: block;
 	font-size: 16px;
 	font-size: 16px;
 	font-weight: bold;
 	font-weight: bold;
@@ -526,11 +532,13 @@ progressbar {
 	font-size: 13px;
 	font-size: 13px;
 	color: #222;
 	color: #222;
 	font-family: rmlui-debugger-font;
 	font-family: rmlui-debugger-font;
+	box-sizing: border-box;
 	width: 50%;
 	width: 50%;
 	height: 40%;
 	height: 40%;
 }
 }
 #sandbox_target 
 #sandbox_target 
 {
 {
+	box-sizing: border-box;
 	background-color: #fff;
 	background-color: #fff;
 	border: 1px #000;
 	border: 1px #000;
 	height: 55%;
 	height: 55%;
@@ -766,12 +774,12 @@ progressbar {
 	<form onsubmit="submit_form">
 	<form onsubmit="submit_form">
 		<h2>Full name</h2>
 		<h2>Full name</h2>
 		<div>
 		<div>
-			<input type="text" size="20" name="name"/>
+			<input class="two-wide" type="text" name="name"/>
 		</div>
 		</div>
 		<h2>Email and password</h2>
 		<h2>Email and password</h2>
 		<div>
 		<div>
-			<input type="text" size="10" name="email"/>
-			<input type="password" size="10" name="password"/>
+			<input type="text" name="email"/>
+			<input type="password" name="password"/>
 		</div>
 		</div>
 		<h2>Favorite animal</h2>
 		<h2>Favorite animal</h2>
 		<div>
 		<div>
@@ -802,7 +810,7 @@ progressbar {
 		</div>
 		</div>
 		<h2>Message</h2>
 		<h2>Message</h2>
 		<div>
 		<div>
-			<textarea cols="30" rows="5" wrap="nowrap" name="message">😍 Hello 🌐 World! 😎</textarea>
+			<textarea cols="25" rows="4" wrap="nowrap" name="message">😍 Hello 🌐 World! 😎</textarea>
 		</div>
 		</div>
 		<div style="margin-bottom: 15px;">
 		<div style="margin-bottom: 15px;">
 			<input type="submit">Submit</input>
 			<input type="submit">Submit</input>

+ 2 - 2
Source/Core/Element.cpp

@@ -547,10 +547,10 @@ float Element::GetBaseline() const
 }
 }
 
 
 // Gets the intrinsic dimensions of this element, if it is of a type that has an inherent size.
 // Gets the intrinsic dimensions of this element, if it is of a type that has an inherent size.
-bool Element::GetIntrinsicDimensions(Vector2f& RMLUI_UNUSED_PARAMETER(dimensions))
+bool Element::GetIntrinsicDimensions(Vector2f& RMLUI_UNUSED_PARAMETER(dimensions), float& RMLUI_UNUSED_PARAMETER(ratio))
 {
 {
 	RMLUI_UNUSED(dimensions);
 	RMLUI_UNUSED(dimensions);
-
+	RMLUI_UNUSED(ratio);
 	return false;
 	return false;
 }
 }
 
 

+ 2 - 2
Source/Core/Elements/ElementFormControlInput.cpp

@@ -177,11 +177,11 @@ void ElementFormControlInput::ProcessDefaultAction(Event& event)
 		type->ProcessDefaultAction(event);
 		type->ProcessDefaultAction(event);
 }
 }
 
 
-bool ElementFormControlInput::GetIntrinsicDimensions(Vector2f& dimensions)
+bool ElementFormControlInput::GetIntrinsicDimensions(Vector2f& dimensions, float& ratio)
 {
 {
 	if (!type)
 	if (!type)
 		return false;
 		return false;
-	return type->GetIntrinsicDimensions(dimensions);
+	return type->GetIntrinsicDimensions(dimensions, ratio);
 }
 }
 
 
 } // namespace Rml
 } // namespace Rml

+ 1 - 1
Source/Core/Elements/ElementFormControlSelect.cpp

@@ -159,7 +159,7 @@ void ElementFormControlSelect::OnLayout()
 }
 }
 
 
 // Returns true to mark this element as replaced.
 // Returns true to mark this element as replaced.
-bool ElementFormControlSelect::GetIntrinsicDimensions(Vector2f& intrinsic_dimensions)
+bool ElementFormControlSelect::GetIntrinsicDimensions(Vector2f& intrinsic_dimensions, float& /*ratio*/)
 {
 {
 	intrinsic_dimensions.x = 128;
 	intrinsic_dimensions.x = 128;
 	intrinsic_dimensions.y = 16;
 	intrinsic_dimensions.y = 16;

+ 1 - 1
Source/Core/Elements/ElementFormControlTextArea.cpp

@@ -119,7 +119,7 @@ bool ElementFormControlTextArea::GetWordWrap()
 }
 }
 
 
 // Returns the control's inherent size, based on the length of the input field and the current font size.
 // Returns the control's inherent size, based on the length of the input field and the current font size.
-bool ElementFormControlTextArea::GetIntrinsicDimensions(Vector2f& dimensions)
+bool ElementFormControlTextArea::GetIntrinsicDimensions(Vector2f& dimensions, float& /*ratio*/)
 {
 {
 	dimensions.x = (float) (GetNumColumns() * ElementUtilities::GetStringWidth(this, "m"));
 	dimensions.x = (float) (GetNumColumns() * ElementUtilities::GetStringWidth(this, "m"));
 	dimensions.y = (float)GetNumRows() * GetLineHeight();
 	dimensions.y = (float)GetNumRows() * GetLineHeight();

+ 3 - 1
Source/Core/Elements/ElementImage.cpp

@@ -48,7 +48,7 @@ ElementImage::~ElementImage()
 }
 }
 
 
 // Sizes the box to the element's inherent size.
 // Sizes the box to the element's inherent size.
-bool ElementImage::GetIntrinsicDimensions(Vector2f& _dimensions)
+bool ElementImage::GetIntrinsicDimensions(Vector2f& _dimensions, float& _ratio)
 {
 {
 	// Check if we need to reload the texture.
 	// Check if we need to reload the texture.
 	if (texture_dirty)
 	if (texture_dirty)
@@ -73,6 +73,8 @@ bool ElementImage::GetIntrinsicDimensions(Vector2f& _dimensions)
 	// Return the calculated dimensions. If this changes the size of the element, it will result in
 	// Return the calculated dimensions. If this changes the size of the element, it will result in
 	// a call to 'onresize' below which will regenerate the geometry.
 	// a call to 'onresize' below which will regenerate the geometry.
 	_dimensions = dimensions;
 	_dimensions = dimensions;
+	_ratio = dimensions.x / dimensions.y;
+
 	return true;
 	return true;
 }
 }
 
 

+ 1 - 3
Source/Core/Elements/ElementImage.h

@@ -73,9 +73,7 @@ public:
 	virtual ~ElementImage();
 	virtual ~ElementImage();
 
 
 	/// Returns the element's inherent size.
 	/// Returns the element's inherent size.
-	/// @param[out] The element's intrinsic dimensions.
-	/// @return True.
-	bool GetIntrinsicDimensions(Vector2f& dimensions) override;
+	bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;
 
 
 protected:
 protected:
 	/// Renders the image.
 	/// Renders the image.

+ 1 - 2
Source/Core/Elements/InputType.h

@@ -83,8 +83,7 @@ public:
 	virtual void ProcessDefaultAction(Event& event) = 0;
 	virtual void ProcessDefaultAction(Event& event) = 0;
 
 
 	/// Sizes the dimensions to the element's inherent size.
 	/// Sizes the dimensions to the element's inherent size.
-	/// @return True.
-	virtual bool GetIntrinsicDimensions(Vector2f& dimensions) = 0;
+	virtual bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) = 0;
 
 
 protected:
 protected:
 	ElementFormControlInput* element;
 	ElementFormControlInput* element;

+ 1 - 1
Source/Core/Elements/InputTypeButton.cpp

@@ -51,7 +51,7 @@ void InputTypeButton::ProcessDefaultAction(Event& /*event*/)
 }
 }
 
 
 // Sizes the dimensions to the element's inherent size.
 // Sizes the dimensions to the element's inherent size.
-bool InputTypeButton::GetIntrinsicDimensions(Vector2f& /*dimensions*/)
+bool InputTypeButton::GetIntrinsicDimensions(Vector2f& /*dimensions*/, float& /*ratio*/)
 {
 {
 	return false;
 	return false;
 }
 }

+ 1 - 1
Source/Core/Elements/InputTypeButton.h

@@ -58,7 +58,7 @@ public:
 
 
 	/// Sizes the dimensions to the element's inherent size.
 	/// Sizes the dimensions to the element's inherent size.
 	/// @return False.
 	/// @return False.
-	bool GetIntrinsicDimensions(Vector2f& dimensions) override;
+	bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;
 };
 };
 
 
 } // namespace Rml
 } // namespace Rml

+ 2 - 1
Source/Core/Elements/InputTypeCheckbox.cpp

@@ -76,10 +76,11 @@ void InputTypeCheckbox::ProcessDefaultAction(Event& event)
 }
 }
 
 
 // Sizes the dimensions to the element's inherent size.
 // Sizes the dimensions to the element's inherent size.
-bool InputTypeCheckbox::GetIntrinsicDimensions(Vector2f& dimensions)
+bool InputTypeCheckbox::GetIntrinsicDimensions(Vector2f& dimensions, float& ratio)
 {
 {
 	dimensions.x = 16;
 	dimensions.x = 16;
 	dimensions.y = 16;
 	dimensions.y = 16;
+	ratio = 1;
 
 
 	return true;
 	return true;
 }
 }

+ 1 - 1
Source/Core/Elements/InputTypeCheckbox.h

@@ -60,7 +60,7 @@ public:
 
 
 	/// Sizes the dimensions to the element's inherent size.
 	/// Sizes the dimensions to the element's inherent size.
 	/// @return True.
 	/// @return True.
-	bool GetIntrinsicDimensions(Vector2f& dimensions) override;
+	bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;
 };
 };
 
 
 } // namespace Rml
 } // namespace Rml

+ 2 - 1
Source/Core/Elements/InputTypeRadio.cpp

@@ -85,10 +85,11 @@ void InputTypeRadio::ProcessDefaultAction(Event& event)
 }
 }
 
 
 // Sizes the dimensions to the element's inherent size.
 // Sizes the dimensions to the element's inherent size.
-bool InputTypeRadio::GetIntrinsicDimensions(Vector2f& dimensions)
+bool InputTypeRadio::GetIntrinsicDimensions(Vector2f& dimensions, float& ratio)
 {
 {
 	dimensions.x = 16;
 	dimensions.x = 16;
 	dimensions.y = 16;
 	dimensions.y = 16;
+	ratio = 1;
 
 
 	return true;
 	return true;
 }
 }

+ 1 - 1
Source/Core/Elements/InputTypeRadio.h

@@ -63,7 +63,7 @@ public:
 
 
 	/// Sizes the dimensions to the element's inherent size.
 	/// Sizes the dimensions to the element's inherent size.
 	/// @return True.
 	/// @return True.
-	bool GetIntrinsicDimensions(Vector2f& dimensions) override;
+	bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;
 
 
 private:
 private:
 	/// Pops all other radio buttons in our form that share our name.
 	/// Pops all other radio buttons in our form that share our name.

+ 1 - 1
Source/Core/Elements/InputTypeRange.cpp

@@ -98,7 +98,7 @@ void InputTypeRange::ProcessDefaultAction(Event& /*event*/)
 }
 }
 
 
 // Sizes the dimensions to the element's inherent size.
 // Sizes the dimensions to the element's inherent size.
-bool InputTypeRange::GetIntrinsicDimensions(Vector2f& dimensions)
+bool InputTypeRange::GetIntrinsicDimensions(Vector2f& dimensions, float& /*ratio*/)
 {
 {
 	widget->GetDimensions(dimensions);
 	widget->GetDimensions(dimensions);
 	return true;
 	return true;

+ 1 - 1
Source/Core/Elements/InputTypeRange.h

@@ -68,7 +68,7 @@ public:
 
 
 	/// Sizes the dimensions to the element's inherent size.
 	/// Sizes the dimensions to the element's inherent size.
 	/// @return True.
 	/// @return True.
-	bool GetIntrinsicDimensions(Vector2f& dimensions) override;
+	bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;
 
 
 private:
 private:
 	WidgetSlider* widget;
 	WidgetSlider* widget;

+ 1 - 3
Source/Core/Elements/InputTypeSubmit.cpp

@@ -70,10 +70,8 @@ void InputTypeSubmit::ProcessDefaultAction(Event& event)
 }
 }
 
 
 // Sizes the dimensions to the element's inherent size.
 // Sizes the dimensions to the element's inherent size.
-bool InputTypeSubmit::GetIntrinsicDimensions(Vector2f& RMLUI_UNUSED_PARAMETER(dimensions))
+bool InputTypeSubmit::GetIntrinsicDimensions(Vector2f& /*dimensions*/, float& /*ratio*/)
 {
 {
-	RMLUI_UNUSED(dimensions);
-	
 	return false;
 	return false;
 }
 }
 
 

+ 1 - 1
Source/Core/Elements/InputTypeSubmit.h

@@ -55,7 +55,7 @@ public:
 
 
 	/// Sizes the dimensions to the element's inherent size.
 	/// Sizes the dimensions to the element's inherent size.
 	/// @return False.
 	/// @return False.
-	bool GetIntrinsicDimensions(Vector2f& dimensions) override;
+	bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;
 };
 };
 
 
 } // namespace Rml
 } // namespace Rml

+ 1 - 1
Source/Core/Elements/InputTypeText.cpp

@@ -111,7 +111,7 @@ void InputTypeText::ProcessDefaultAction(Event& RMLUI_UNUSED_PARAMETER(event))
 }
 }
 
 
 // Sizes the dimensions to the element's inherent size.
 // Sizes the dimensions to the element's inherent size.
-bool InputTypeText::GetIntrinsicDimensions(Vector2f& dimensions)
+bool InputTypeText::GetIntrinsicDimensions(Vector2f& dimensions, float& /*ratio*/)
 {
 {
 	dimensions.x = (float) (size * ElementUtilities::GetStringWidth(element, "m"));
 	dimensions.x = (float) (size * ElementUtilities::GetStringWidth(element, "m"));
 	dimensions.y = element->GetLineHeight() + 2.0f;
 	dimensions.y = element->GetLineHeight() + 2.0f;

+ 1 - 1
Source/Core/Elements/InputTypeText.h

@@ -76,7 +76,7 @@ public:
 
 
 	/// Sizes the dimensions to the element's inherent size.
 	/// Sizes the dimensions to the element's inherent size.
 	/// @return True.
 	/// @return True.
-	bool GetIntrinsicDimensions(Vector2f& dimensions) override;
+	bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;
 
 
 private:
 private:
 	int size;
 	int size;

+ 24 - 25
Source/Core/LayoutDetails.cpp

@@ -73,48 +73,47 @@ void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* eleme
 
 
 	// Calculate the size of the content area.
 	// Calculate the size of the content area.
 	Vector2f content_area(-1, -1);
 	Vector2f content_area(-1, -1);
+	float intrinsic_ratio = -1;
 	bool replaced_element = false;
 	bool replaced_element = false;
 
 
 	// If the element has intrinsic dimensions, then we use those as the basis for the content area and only adjust
 	// If the element has intrinsic dimensions, then we use those as the basis for the content area and only adjust
 	// them if a non-auto style has been applied to them.
 	// them if a non-auto style has been applied to them.
-	if (element->GetIntrinsicDimensions(content_area))
+	if (element->GetIntrinsicDimensions(content_area, intrinsic_ratio))
 	{
 	{
 		replaced_element = true;
 		replaced_element = true;
 
 
-		Vector2f original_content_area = content_area;
-
 		// The element has resized itself, so we only resize it if a RCSS width or height was set explicitly. A value of
 		// The element has resized itself, so we only resize it if a RCSS width or height was set explicitly. A value of
 		// 'auto' (or 'auto-fit', ie, both keywords) means keep (or adjust) the intrinsic dimensions.
 		// 'auto' (or 'auto-fit', ie, both keywords) means keep (or adjust) the intrinsic dimensions.
 		bool auto_width = false, auto_height = false;
 		bool auto_width = false, auto_height = false;
 
 
-		if (computed.width.type != Style::Width::Auto)
+		if (computed.width.type == Style::Width::Auto)
+			auto_width = true;
+		else if (computed.box_sizing == Style::BoxSizing::ContentBox)
 			content_area.x = ResolveValue(computed.width, containing_block.x);
 			content_area.x = ResolveValue(computed.width, containing_block.x);
 		else
 		else
-			auto_width = true;
+			content_area.x = BorderWidthToContentWidth(ResolveValue(computed.width, containing_block.x), box);
 
 
-		if (computed.height.type != Style::Height::Auto)
+		if (computed.height.type == Style::Height::Auto)
+			auto_height = true;
+		else if (computed.box_sizing == Style::BoxSizing::ContentBox)
 			content_area.y = ResolveValue(computed.height, containing_block.y);
 			content_area.y = ResolveValue(computed.height, containing_block.y);
 		else
 		else
-			auto_height = true;
+			content_area.y = BorderHeightToContentHeight(ResolveValue(computed.height, containing_block.y), box);
 
 
-		// If one of the dimensions is 'auto' then we need to scale it such that the original ratio is preserved.
-		if (auto_width && !auto_height)
-			content_area.x = (content_area.y / original_content_area.y) * original_content_area.x;
-		else if (auto_height && !auto_width)
-			content_area.y = (content_area.x / original_content_area.x) * original_content_area.y;
-
-		// Reduce the width and height to make up for borders and padding.
-		content_area.x -= (box.GetEdge(Box::BORDER, Box::LEFT) +
-						   box.GetEdge(Box::PADDING, Box::LEFT) +
-						   box.GetEdge(Box::BORDER, Box::RIGHT) +
-						   box.GetEdge(Box::PADDING, Box::RIGHT));
-		content_area.y -= (box.GetEdge(Box::BORDER, Box::TOP) +
-						   box.GetEdge(Box::PADDING, Box::TOP) +
-						   box.GetEdge(Box::BORDER, Box::BOTTOM) +
-						   box.GetEdge(Box::PADDING, Box::BOTTOM));
-
-		content_area.x = Math::Max(content_area.x, 0.0f);
-		content_area.y = Math::Max(content_area.y, 0.0f);
+		// Use a fallback size if we still couldn't determine the size.
+		if (content_area.x < 0)
+			content_area.x = 300;
+		if (content_area.y < 0)
+			content_area.y = 150;
+
+		// If we have an intrinsic ratio and one of the dimensions is 'auto', then scale it such that the ratio is preserved.
+		if (intrinsic_ratio > 0)
+		{
+			if (auto_width && !auto_height)
+				content_area.x = content_area.y * intrinsic_ratio;
+			else if (auto_height && !auto_width)
+				content_area.y = content_area.x / intrinsic_ratio;
+		}
 	}
 	}
 
 
 	// If the element is inline, then its calculations are much more straightforward (no worrying about auto margins
 	// If the element is inline, then its calculations are much more straightforward (no worrying about auto margins

+ 67 - 0
Tests/Data/VisualTests/replaced_elements.rml

@@ -0,0 +1,67 @@
+<rml>
+<head>
+    <title>Replaced elements</title>
+    <link type="text/rcss" href="../style.rcss"/>
+	<link rel="help" href="https://www.w3.org/TR/2011/REC-CSS2-20110607/visudet.html#inline-replaced-width" />
+	<meta name="Description" content="Sizing of replaced elements." />
+	<style>
+		@spritesheet theme 
+		{
+			src: /assets/invader.tga;
+			invader: 179px 152px 50px 40px;
+		}
+		img {
+			vertical-align: bottom;
+			margin: 10px 5px;
+		}
+		.border-box {
+			box-sizing: border-box;
+		}
+		input.text {
+			font-size: 1.1em;
+			background-color: #fff;
+			color: #555;
+			border: 5px #999;
+			padding: 15px;
+			cursor: text;
+			tab-index: auto;
+			box-sizing: border-box;
+		}
+	</style>
+</head>
+
+<body>
+<p>The following images should be the same size (width and height).</p>
+<div>
+	<img style="padding: 10px;" sprite="invader"/>
+	<img style="padding: 10px;" class="border-box" sprite="invader"/>
+	<img style="padding: 10px; height: 40px;" sprite="invader"/>
+	<img style="padding: 10px; width: 50px;" sprite="invader"/>
+	<img style="padding: 10px; height: 60px;" class="border-box" sprite="invader"/>
+	<img style="padding: 10px; width: 70px;" class="border-box" sprite="invader"/>
+</div>
+<hr/>
+<p>There should be three evenly spaced, same-sized images on a single line.</p>
+<div style="text-align: center">
+	<img style="float: left" sprite="invader"/>
+	<img style="float: right" sprite="invader"/>
+	<img sprite="invader"/>
+</div>
+<hr/>
+<p>The outer border of the images should take up the same height.</p>
+<div>
+	<img style="border: 10px black; height: 30px;" sprite="invader"/>
+	<img style="border: 2px black; height: 50px;" class="border-box" sprite="invader"/>
+	<img style="border: 2px black; padding: 5px; height: 50px;" class="border-box" sprite="invader"/>
+</div>
+<hr/>
+<p>The two images should be same-sized, and perfectly aligned below each other.</p>
+<div style="text-align: center;">
+	<img style="display: block; margin: 10px auto;" sprite="invader"/>
+	<img style="display: inline;" sprite="invader"/>
+</div>
+<hr/>
+<p>The two text boxes should have the same height and fit on a single line.</p>
+<input style="width: 30%;" type="text" value="hello"/><input style="width: 70%;" type="text" value="world"/>
+</body>
+</rml>

+ 0 - 2
Tests/Data/description.rml

@@ -47,8 +47,6 @@
 		background-color: #444;
 		background-color: #444;
 		color: #ffe;
 		color: #ffe;
 		border: 1px #555;
 		border: 1px #555;
-		line-height: 1.2;
-		height: 1.8em;
 		cursor: text;
 		cursor: text;
 		tab-index: auto;
 		tab-index: auto;
 	}
 	}

+ 7 - 1
changelog.md

@@ -67,10 +67,15 @@ Work has started on a complete test suite for RmlUi. The tests have been separat
 ### Layout improvements
 ### Layout improvements
 
 
 - Floating elements, absolutely positioned elements, and inline-block elements (as before) will now all shrink to the width of their contents when their width is set to `auto`.
 - Floating elements, absolutely positioned elements, and inline-block elements (as before) will now all shrink to the width of their contents when their width is set to `auto`.
+- Replaced elements (eg. `img` and some `input` elements) now follow the normal CSS sizing rules. That is, padding and borders are no longer subtracted from the width and height of the element by default.
+- Replaced elements can now decide whether to provide an intrinsic aspect ratio, such that users can eg. set the width property on `input.text` without affecting their height.
 - Fixed some situations where overflowing content would not be hidden or scrolled when setting a non-default `overflow` property. [#116](https://github.com/mikke89/RmlUi/issues/116)
 - Fixed some situations where overflowing content would not be hidden or scrolled when setting a non-default `overflow` property. [#116](https://github.com/mikke89/RmlUi/issues/116)
 - Several other layouting improvements (to-be-detailed).
 - Several other layouting improvements (to-be-detailed).
 
 
-These changes may result in a differently rendered layout when upgrading to RmlUi 4.0. In particular the first item. If the changes are undesired, set a definite width on the related elements, eg. using the `width` property or the `left`/`right` properties.
+These changes may result in a differently rendered layout when upgrading to RmlUi 4.0. In particular the first two items. 
+
+- If the shrink-to-fit width is undesired, set a definite width on the related elements, eg. using the `width` property or the `left`/`right` properties.
+- Replaced elements may need adjustment of width and height, in this case it may be useful to use the new `box-sizing: border-box` property in some situations.
 
 
 ### New RCSS properties
 ### New RCSS properties
 
 
@@ -98,6 +103,7 @@ These changes may result in a differently rendered layout when upgrading to RmlU
 - The changes to the layout engine may result in changes to the rendered layout in some situations, see above for more details.
 - The changes to the layout engine may result in changes to the rendered layout in some situations, see above for more details.
 - The `BaseXMLParser` class has some minor interface changes.
 - The `BaseXMLParser` class has some minor interface changes.
 - Tab set elements `tab` and `panel` should now have their `display` property set in the RCSS document, use `display: inline-block` for the same behavior as before.
 - Tab set elements `tab` and `panel` should now have their `display` property set in the RCSS document, use `display: inline-block` for the same behavior as before.
+- For custom, replaced elements: `Element::GetIntrinsicDimensions()` now additionally takes an intrinsic ratio parameter.