CS456 Layout
Program - Layout
goal
To implement basic two-pass layout algorithms
Application
Same application as before except that various objects now implement the Layout interface. When your window resizes it should pass the new dimensions of your interactive panel to the setHBounds() and setVBounds() methods of the Root object.
Layout
You should create a Java interface called Layout that contains the methods:
  • double getMinWidth() Returns the minimum width that the widget will accept for drawing.
  • double getDesiredWidth() Returns the desired width that the widget wants for drawing.
  • double getMaxWidth() Returns the maximum width that the widget can use for drawing.
  • void setHBounds( double left, double right) This sets the horizontal range that the object can display within. This will also set up the necessary information for the height information.
  • double getMinHeight() Returns the minimum height that the widget will accept for drawing.
  • double getDesiredHeight() Returns the desired height that the widget wants for drawing.
  • double getMaxHeight() Returns the maximum height that the widget can use for drawing.
  • void setVBounds( double top, double bottom) This sets the vertical bounds for the object and causes it to layout any of its children.
Root
This is changed so that its contents has only one object. This implements Layout and simply passes through the layout calls to its only child. Its former function as a place for the model remains intact.
Layout objects
Several of your previous objects are modified as follows:
  • Button{ label:"my label",state:"idle", idle:{r:0,g:0,b:0}, hover:{r:100,g:100,b:100}, active:{r:255,g:255,b:0}, model:[...], value:10 } Implements Layout. Behaves as button did previously except that contents is no longer an attribute. The content list is generated programatically based on the dimensions specified in setHBounds() and setVBounds().This should pick a default font size and then report min, desired and max sizes that will create a button of that size based on the contents of the label attribute. When the sizes are actually received via setHBounds() and setVBounds() you should adapt the font size so that the label, border and other visuals will fit in the bounds.
  • ScrollV{ state:"idle", idle:{r:0,g:0,b:0}, hover:{r:100,g:100,b:100}, active:{r:255,g:255,b:0}, model:[...], max:1.0, min:0.0, step:0.1} This implements Layout and generates its contents programmaticaly rather than as an attribute. Everything else works the same. This should report relatively narrow horizontal values and then a suitable min and desired height. The maximum height should be quite large to take up any vertical space.
  • ScrollH{ state:"idle", idle:{r:0,g:0,b:0}, hover:{r:100,g:100,b:100}, active:{r:255,g:255,b:0}, model:[...], max:1.0, min:0.0, step:0.1} Works the same as ScrollV except that the slider moves horizontally with max on the right.
  • TextBox{ state:"idle", idle:{r:0,g:0,b:0}, hover:{r:100,g:100,b:100}, active:{r:255,g:255,b:0}, model:[...], desiredChars:10 } This implements Layout and operates as before. Its contents are generated programmatically based on the bounds settings. The width values should depend upon a string of "W" characters that as the length of desiredChars. To this you add any of the other text box borders etc. The height values should be based on standard font ranges. Once the bounds are set, then adjust everything to fit as you did with the Button.
  • VStack{ contents:[...] } This object's contents must all implement Layout. This will use the Layout methods on its contents objects to arrange them into a vertical stack.
  • HStack{ contents:[...] } This object's contents must all implement Layout. This will arrange all of the contents into a horizontal stack.
  • Columns{ contents:[...], nColumns:12, gutter:10 }This assumes that all of its contents objects implement Layout. It assumes also that some of its contents objects will have a "columnSpan:" attribute. This specifies how many columns that object wants to use. If there is no such attribute, then the default is 1.

    This object computes a minimum, desired and maximum column width for each of its children. For each of these values it takes the maximum for all of its children. It then computes its own width as (maxChildWidth*nColumns)+(gutter*(nColumns-1)). It does this separately for min, desired and max.

    The column width of a child object is the same as its own respective min, desired or max width if its columnSpan is 1. If its columnSpan is greater than 1 then its column width is

    (myWidth-(columnSpan-1)*gutter)/columnSpan)

    When Columns receives its width range it will compute a column width of (width-(nColumns-1)*gutter)/nColumns). Using this width, it will lay out all of its children in rows, giving each the width specified by its columnSpan attribute. If the column width is less than the minimum for some child, then it is given more columns until it has enough. If the total width is not enough then that child is given a row of its own and all the columns in that row. If it has more children than fit in nColumns it will wrap them onto new rows. It will now know how many rows it needs and which children are in each row.

    When requested for its height information it will use the min, desired and max height information of its children to compute the height of each row and report the sum of the heights of the rows.

    When it gets its height bounds, it will use the min,desired, and max values for the children in each row to determine how much of the vertical space to give to each row. This is then set as the height for each object in a row.

  • Group{ contents:[...], width:10.0, height:20.0 } This is a simple group that implements Layout. It will report width and height as its min and desired values. It will report very large max values. When it receives its bounds it will compute a uniform scale factor that will make the width and height values fit within the given bounds.
grading
__ 2) Group correctly resizes itself based on the available width and height.
__ 2) Button correctly resizes itself based on the available width and height.
__ 2) ScrollV correctly resizes itself based on the available width and height.
__ 2) ScrollH correctly resizes itself based on the available width and height.
__ 2) TextBox correctly resizes itself based on the available width and height.
__ 2) VStack correctly positions its contents based on available sizes.
__ 2) HStack correctly positions its contents when there is enough room.
__ 2) Columns correctly sets object into column layouts when there are enough available columns for all the objects.
__ 4) Columns correctly wraps objects when there are more than there are columns available.
Test 1
Root{ contents:[
	HStack{ contents:[
		Button{ label:"Long label", state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}, model: ["val"], value:4
		}
		]
	}
	], model:{ val:10, size:20 }
}
Test 2
Root{ contents:[
	HStack{ contents:[
		Button{ label:"Long label", state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}, model: ["val"], value:4
		},
		Button{ label:"Short", state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}, model: ["val"], value:5
		},
		ScrollH{ state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}, model: ["size"],min:0, max:50
		}
		]
	}
	], model:{ val:10, size:20 }
}
Test 3
Root{ contents:[
	VStack{ contents:[
		Button{ label:"Long label", state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}
		},
		Button{ label:"Short", state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}
		},
		ScrollH{ state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}
		},
		ScrollV{ state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}
		}
		]
	}
	]
}
Test 4
Root{ contents:[
	Columns{ contents:[
		Button{ label:"Long label", state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}
		},
		Button{ label:"Short", state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}
		},
		Button{ label:"Hey this is long", state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}
		},
		Button{ label:"wide", state:"idle",idle:{r:128,g:128,b:128},
			hover:{r:200,g:200,b:200}, active:{r:250,g:250,b:20}, columnSpan:2
		}
		], nColumns:3, gutter:20
	}
	]
}