diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp
index 1e8be93f17acdf682a7fe9f77f5cbf34b0f1dc87..f623d99dc76114cb6f0b5635e104c52f6a9fcaee 100644
--- a/indra/llui/lltoolbar.cpp
+++ b/indra/llui/lltoolbar.cpp
@@ -57,8 +57,8 @@ LLToolBar::Params::Params()
 	button_icon("button_icon"),
 	button_icon_and_text("button_icon_and_text"),
 	wrap("wrap", true),
-	min_width("min_width", 0),
-	max_width("max_width", S32_MAX),
+	min_button_width("min_button_width", 0),
+	max_button_width("max_button_width", S32_MAX),
 	background_image("background_image")
 {}
 
@@ -70,8 +70,8 @@ LLToolBar::LLToolBar(const Params& p)
 	mNeedsLayout(false),
 	mCenterPanel(NULL),
 	mCenteringStack(NULL),
-	mMinWidth(p.min_width),
-	mMaxWidth(p.max_width),
+	mMinButtonWidth(p.min_button_width),
+	mMaxButtonWidth(p.max_button_width),
 	mBackgroundImage(p.background_image)
 {
 }
@@ -108,120 +108,148 @@ void LLToolBar::initFromParams(const LLToolBar::Params& p)
 	
 	mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
 
-	addRow();
-
 	BOOST_FOREACH (LLToolBarButton::Params button_p, p.buttons)
 	{
+		// buttons always follow left and top, for all orientations
+		button_p.follows.flags = FOLLOWS_LEFT|FOLLOWS_TOP;
+		button_p.fillFrom((mButtonType == BTNTYPE_ICONS_ONLY)
+							? p.button_icon						// icon only
+							: p.button_icon_and_text);			// icon + text
+
 		LLRect button_rect(button_p.rect);
 		{ // remove any offset from button
 			if (orientation == LLLayoutStack::HORIZONTAL)
 			{
-				button_rect.setOriginAndSize(0, 0, mMinWidth, getRect().getHeight());
+				button_rect.setOriginAndSize(0, 0, mMinButtonWidth, button_rect.getHeight());
 			}
 			else // VERTICAL
 			{
-				button_rect.setOriginAndSize(0, 0, mMinWidth, button_rect.getHeight());
+				button_rect.setOriginAndSize(0, 0, mMinButtonWidth, button_rect.getHeight());
 			}
 		}
 
-		button_p.fillFrom((mButtonType == BTNTYPE_ICONS_ONLY)
-							? p.button_icon						// icon only
-							: p.button_icon_and_text);			// icon + text
+		// use our calculated rect
+		button_p.rect = button_rect;
+		LLToolBarButton* buttonp = LLUICtrlFactory::create<LLToolBarButton>(button_p);
+
+		mButtons.push_back(buttonp);
+		mCenterPanel->addChild(buttonp);
 
-		mButtons.push_back(LLUICtrlFactory::create<LLToolBarButton>(button_p));
 		mNeedsLayout = true;
 	}
-
-	updateLayout();
 }
 
-void LLToolBar::addButton(LLToolBarButton* buttonp)
+void LLToolBar::updateLayoutAsNeeded()
 {
-	LLLayoutPanel::Params panel_p;
-	panel_p.name = buttonp->getName() + "_panel";
-	panel_p.user_resize = false;
-	panel_p.auto_resize= false;
-	panel_p.fit_content = true;
+	if (!mNeedsLayout) return;
 
-	LLLayoutPanel* panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
-	
-	panel->addChild(buttonp);
-	mStacks.back()->addChild(panel);
-}
+	LLLayoutStack::ELayoutOrientation orientation = getOrientation(mSideType);
 
-void LLToolBar::updateLayout()
-{
-	mCenteringStack->updateLayout();
+	// our terminology for orientation-agnostic layout is that
+	// length refers to a distance in the direction we stack the buttons 
+	// and girth refers to a distance in the direction buttons wrap
+	S32 total_length = 0;
+	S32 max_length = (orientation == LLLayoutStack::HORIZONTAL)
+					? getRect().getWidth()
+					: getRect().getHeight();
+	S32 max_row_girth = 0;
+	S32 cur_start = 0;
+	S32 cur_row = 0;
 
-	if (!mNeedsLayout) return;
-	mNeedsLayout = false;
+	LLRect panel_rect = mCenterPanel->getLocalRect();
 
-	{ // clean up existing rows
-		BOOST_FOREACH(LLToolBarButton* button, mButtons)
-		{
-			if (button->getParent())
+	std::vector<LLToolBarButton*> buttons_in_row;
+
+	BOOST_FOREACH(LLToolBarButton* button, mButtons)
+	{
+		S32 button_clamped_width = llclamp(button->getRect().getWidth(), mMinButtonWidth, mMaxButtonWidth);
+		S32 button_length = (orientation == LLLayoutStack::HORIZONTAL)
+							? button_clamped_width
+							: button->getRect().getHeight();
+		S32 button_girth = (orientation == LLLayoutStack::HORIZONTAL)
+							? button->getRect().getHeight()
+							: button_clamped_width;
+		
+		// handle wrapping
+		if (total_length + button_length > max_length 
+			&& cur_start != 0) // not first button in row
+		{ // go ahead and wrap
+			if (orientation == LLLayoutStack::VERTICAL)
+			{
+				// row girth is clamped to allowable button widths
+				max_row_girth = llclamp(max_row_girth, mMinButtonWidth, mMaxButtonWidth);
+			}
+			// make buttons in current row all same girth
+			BOOST_FOREACH(LLToolBarButton* button, buttons_in_row)
 			{
-				button->getParent()->removeChild(button);
+				if (orientation == LLLayoutStack::HORIZONTAL)
+				{
+					button->reshape(llclamp(button->getRect().getWidth(), mMinButtonWidth, mMaxButtonWidth), max_row_girth);
+				}
+				else // VERTICAL
+				{
+					button->reshape(max_row_girth, button->getRect().getHeight());
+				}
 			}
-		}	
+			buttons_in_row.clear();
 
-		BOOST_FOREACH(LLLayoutStack* stack, mStacks)
-		{
-			delete stack;
+			total_length = 0;
+			cur_start = 0;
+			cur_row += max_row_girth;
+			max_row_girth = 0;
 		}
-		mStacks.clear();
-	}
-	// start with one row of buttons
-	addRow();
-
-	S32 total_width = 0, total_height = 0;
-	S32 max_total_width = 0, max_total_height = 0;
-	S32 max_width = getRect().getWidth(), max_height = getRect().getHeight();
-	BOOST_FOREACH(LLToolBarButton* button, mButtons)
-	{
-		S32 button_width = button->getRect().getWidth();
-		S32 button_height = button->getRect().getHeight();
 
-		if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL
-			&& total_width + button_height > getRect().getWidth())
+		LLRect button_rect;
+		if (orientation == LLLayoutStack::HORIZONTAL)
+		{
+			button_rect.setLeftTopAndSize(cur_start, panel_rect.mTop - cur_row, button_clamped_width, button->getRect().getHeight());
+		}
+		else // VERTICAL
 		{
-			addRow();
-			total_width = 0;
+			button_rect.setLeftTopAndSize(cur_row, panel_rect.mTop - cur_start, button_clamped_width, button->getRect().getHeight());
 		}
-		addButton(button);
-
-		total_width += button_width;
-		total_height += button_height;
-		max_total_width = llmax(max_total_width, total_width);
-		max_total_height = llmax(max_total_height, total_height);
-		max_width = llmax(button->getRect().getWidth(), max_width);
-		max_height = llmax(button->getRect().getHeight(), max_height);
+		button->setShape(button_rect);
+		
+		buttons_in_row.push_back(button);
+
+		total_length += button_length;
+		cur_start = total_length;
+		max_row_girth = llmax(button_girth, max_row_girth);
 	}
 
-	if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
+	// final resizing in "girth" direction
+	S32 total_girth = cur_row + max_row_girth; // increment by size of final row
+
+	// grow and optionally shift toolbar to accomodate buttons
+	if (orientation == LLLayoutStack::HORIZONTAL)
 	{
-		BOOST_FOREACH(LLLayoutStack* stack, mStacks)
-		{
-			stack->reshape(max_total_width, stack->getParent()->getRect().getHeight());
-			stack->updateLayout();
+		if (mSideType == SIDE_TOP)
+		{ // shift down to maintain top edge
+			translate(0, getRect().getHeight() - total_girth);
 		}
+
+		reshape(getRect().getWidth(), total_girth);
 	}
-	else
+	else // VERTICAL
 	{
-		BOOST_FOREACH(LLLayoutStack* stack, mStacks)
-		{
-			stack->reshape(stack->getParent()->getRect().getWidth(), max_total_height);
-			stack->updateLayout();
+		if (mSideType == SIDE_RIGHT)
+		{ // shift left to maintain right edge
+			translate(getRect().getWidth() - total_girth, 0);
 		}
-
-		reshape(max_total_width, getRect().getHeight());
+		reshape(total_girth, getRect().getHeight());
 	}
+
+	// recenter toolbar buttons
+	mCenteringStack->updateLayout();
+
+	// don't clear flag until after we've resized ourselves, to avoid laying out every frame
+	mNeedsLayout = false;
 }
 
 
 void LLToolBar::draw()
 {
-	updateLayout();
+	updateLayoutAsNeeded();
 
 	{	// draw background
 		LLRect bg_rect;
@@ -231,22 +259,6 @@ void LLToolBar::draw()
 	LLUICtrl::draw();
 }
 
-void LLToolBar::addRow()
-{
-	LLLayoutStack::ELayoutOrientation orientation = getOrientation(mSideType);
-
-	LLLayoutStack::Params stack_p;
-	stack_p.rect = getLocalRect();
-	stack_p.name = llformat("button_stack_%d", mStacks.size());
-	stack_p.orientation = orientation;
-	stack_p.follows.flags = (orientation == LLLayoutStack::HORIZONTAL)
-		? (FOLLOWS_TOP|FOLLOWS_BOTTOM)  // horizontal
-		: (FOLLOWS_LEFT|FOLLOWS_RIGHT); // vertical
-
-	mStacks.push_back(LLUICtrlFactory::create<LLLayoutStack>(stack_p));
-	mCenterPanel->addChild(mStacks.back());
-}
-
 void LLToolBar::reshape(S32 width, S32 height, BOOL called_from_parent)
 {
 	LLUICtrl::reshape(width, height, called_from_parent);
diff --git a/indra/llui/lltoolbar.h b/indra/llui/lltoolbar.h
index 3a593e42d91a6d5d0b49dffb6eafce3b30d168cf..31729e32b49d57e78b94624c54480d551b2a2230 100644
--- a/indra/llui/lltoolbar.h
+++ b/indra/llui/lltoolbar.h
@@ -93,8 +93,8 @@ class LLToolBar
 												button_icon_and_text;
 
 		Optional<bool>							wrap;
-		Optional<S32>							min_width,
-												max_width;
+		Optional<S32>							min_button_width,
+												max_button_width;
 		// get rid of this
 		Multiple<LLToolBarButton::Params>		buttons;
 
@@ -112,11 +112,9 @@ class LLToolBar
 	LLToolBar(const Params&);
 
 	void initFromParams(const Params&);
-	void addButton(LLToolBarButton* buttonp);
-	void updateLayout();
 
 private:
-	void addRow();
+	void updateLayoutAsNeeded();
 
 	std::list<LLToolBarButton*>		mButtons;
 	LLToolBarEnums::ButtonType		mButtonType;
@@ -125,11 +123,10 @@ class LLToolBar
 	LLLayoutPanel*					mCenterPanel;
 	LLToolBarEnums::SideType		mSideType;
 
-	std::vector<LLLayoutStack*>		mStacks;
 	bool							mWrap;
 	bool							mNeedsLayout;
-	S32								mMinWidth,
-									mMaxWidth;
+	S32								mMinButtonWidth,
+									mMaxButtonWidth;
 
 	LLUIImagePtr					mBackgroundImage;
 };
diff --git a/indra/newview/lldndbutton.cpp b/indra/newview/lldndbutton.cpp
index 8a38c8a6437fa61452bad8c69fad0569eff84300..7c9dda6b1ddc9ac589dc906a96e62ccf99b404af 100644
--- a/indra/newview/lldndbutton.cpp
+++ b/indra/newview/lldndbutton.cpp
@@ -31,16 +31,9 @@
 
 static LLDefaultChildRegistry::Register<LLDragAndDropButton> r("dnd_button");
 
-LLDragAndDropButton::Params::Params()
-{
-
-}
-
 LLDragAndDropButton::LLDragAndDropButton(const Params& params)
 : LLButton(params)
-{
-
-}
+{}
 
 BOOL LLDragAndDropButton::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg)
 {
diff --git a/indra/newview/lldndbutton.h b/indra/newview/lldndbutton.h
index 0642cbb7b91dbdd5a4f86815e0f3e63b5c39bc5d..53ea2f5ea7bed4773a35f82ee658d4292e17fd94 100644
--- a/indra/newview/lldndbutton.h
+++ b/indra/newview/lldndbutton.h
@@ -43,10 +43,7 @@
 class LLDragAndDropButton : public LLButton
 {
 public:
-	struct Params : public LLInitParam::Block<Params, LLButton::Params>
-	{
-		Params();
-	};
+	struct Params : public LLInitParam::Block<Params, LLButton::Params> {};
 
 	LLDragAndDropButton(const Params& params);
 
diff --git a/indra/newview/skins/default/xui/en/floater_test_toolbar.xml b/indra/newview/skins/default/xui/en/floater_test_toolbar.xml
index 138322eca7b92dc4a73c7fe58d39a08de36b9f74..85f0f104fcf1e04907407c90cab3f915e00f2fbe 100644
--- a/indra/newview/skins/default/xui/en/floater_test_toolbar.xml
+++ b/indra/newview/skins/default/xui/en/floater_test_toolbar.xml
@@ -7,33 +7,64 @@
  name="floater_test_toolbar"
  translate="false"
  width="500">
-  <toolbar name="test_toolbar_horizontal"
+  <toolbar name="test_toolbar_top"
            follows="left|right|top"
            height="50"
            width="500"
            left="0"
            top="20"
+           min_width="100"
            side="top">
     <button auto_resize="true"
-            label="Button 1"/>
+            label="Button"/>
     <button auto_resize="true"
             label="Button with long label"/>
     <button auto_resize="true"
             label="Button with longest label of all"/>
   </toolbar>
-  <toolbar name="test_toolbar_vertical"
+  <toolbar name="test_toolbar_left"
            follows="left|bottom|top"
-           height="430"
+           height="380"
            width="100"
            left="0"
            top="70"
+           min_width="100"           
            side="left">
     <button height="30"
-            label="Button 1"/>
+            label="Button"/>
     <button height="50"
-            label="Button 2"/>
+            label="Button with long label"/>
     <button height="60"
-            label="Button 3"/>
+            label="Button with longest label of all"/>
+  </toolbar>
+  <toolbar name="test_toolbar_right"
+           follows="right|bottom|top"
+           height="380"
+           width="100"
+           right="500"
+           top="70"
+           min_width="100"
+           side="right">
+    <button auto_resize="true"
+            label="Button 1"/>
+    <button auto_resize="true"
+            label="Button with long label"/>
+    <button auto_resize="true"
+            label="Button with longest label of all"/>
+  </toolbar>
+  <toolbar name="test_toolbar_bottom"
+           follows="left|right|bottom"
+           height="50"
+           width="500"
+           left="0"
+           bottom="500"
+           min_width="100"
+           side="bottom">
+    <button auto_resize="true"
+            label="Button"/>
+    <button auto_resize="true"
+            label="Button with long label"/>
+    <button auto_resize="true"
+            label="Button with longest label of all"/>
   </toolbar>
-
 </floater>