From 3bde5c8aee16f7f33e3ce7b9058fded916fe2369 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20L=C3=BCbking?= <thomas.luebking@gmail.com>
Date: Sun, 24 Jul 2016 16:40:03 +0200
Subject: Improve stretching (RELATIVE) toolbar items

The available space is distributed reg. the preferred width
of items (spacers and the iconbar ;-) instead of evenly.

The preferred width of the iconbar is calculated from its buttons.

This allows to align the iconbar using spacers and makes better use of
the available space
---
 src/IconbarTool.cc | 29 ++++++++++++++++++++++++-----
 src/IconbarTool.hh |  1 +
 src/Toolbar.cc     | 33 ++++++++++++++++++---------------
 src/ToolbarItem.hh |  1 +
 src/Window.cc      |  1 +
 5 files changed, 45 insertions(+), 20 deletions(-)

diff --git a/src/IconbarTool.cc b/src/IconbarTool.cc
index 294d17a..4e2114d 100644
--- a/src/IconbarTool.cc
+++ b/src/IconbarTool.cc
@@ -292,7 +292,9 @@ void IconbarTool::move(int x, int y) {
 
 void IconbarTool::resize(unsigned int width, unsigned int height) {
     m_icon_container.resize(width, height);
-    m_icon_container.setMaxTotalSize(m_icon_container.orientation() == FbTk::ROT0 || m_icon_container.orientation() == FbTk::ROT180 ? width : height);
+    const unsigned int maxsize = (m_icon_container.orientation() & 1) ? height : width;
+    m_icon_container.setMaxTotalSize(maxsize);
+    m_icon_container.setMaxSizePerClient(maxsize/std::max(1, m_icon_container.size()));
     renderTheme();
 }
 
@@ -300,7 +302,9 @@ void IconbarTool::moveResize(int x, int y,
                              unsigned int width, unsigned int height) {
 
     m_icon_container.moveResize(x, y, width, height);
-    m_icon_container.setMaxTotalSize(m_icon_container.orientation() == FbTk::ROT0 || m_icon_container.orientation() == FbTk::ROT180 ? width : height);
+    const unsigned int maxsize = (m_icon_container.orientation() & 1) ? height : width;
+    m_icon_container.setMaxTotalSize(maxsize);
+    m_icon_container.setMaxSizePerClient(maxsize/std::max(1, m_icon_container.size()));
     renderTheme();
 }
 
@@ -362,6 +366,18 @@ unsigned int IconbarTool::width() const {
     return m_icon_container.width();
 }
 
+unsigned int IconbarTool::preferredWidth() const {
+    // border and paddings
+    unsigned int w = 2*borderWidth() + *m_rc_client_padding * m_icons.size();
+
+    // the buttons
+    for (IconMap::const_iterator it = m_icons.begin(), end = m_icons.end(); it != end; ++it) {
+        w += it->second->preferredWidth();
+    }
+
+    return w;
+}
+
 unsigned int IconbarTool::height() const {
     return m_icon_container.height();
 }
@@ -384,9 +400,6 @@ void IconbarTool::update(UpdateReason reason, Focusable *win) {
 
     m_icon_container.setAlignment(*m_rc_alignment);
 
-    *m_rc_client_width = FbTk::Util::clamp(*m_rc_client_width, 10, 400);
-    m_icon_container.setMaxSizePerClient(*m_rc_client_width);
-
     // lock graphic update
     m_icon_container.setUpdateLock(true);
 
@@ -404,6 +417,11 @@ void IconbarTool::update(UpdateReason reason, Focusable *win) {
             break;
     }
 
+    resizeSig().emit();
+    const unsigned int maxsize = (m_icon_container.orientation() & 1) ? height() : width();
+    m_icon_container.setMaxTotalSize(maxsize);
+    m_icon_container.setMaxSizePerClient(maxsize/std::max(1, m_icon_container.size()));
+
     // unlock container and update graphics
     m_icon_container.setUpdateLock(false);
     m_icon_container.update();
@@ -441,6 +459,7 @@ void IconbarTool::insertWindow(Focusable &win, int pos) {
     }
 
     m_icon_container.insertItem(button, pos);
+    m_tracker.join(button->titleChanged(), FbTk::MemFun(resizeSig(), &FbTk::Signal<>::emit));
 }
 
 void IconbarTool::reset() {
diff --git a/src/IconbarTool.hh b/src/IconbarTool.hh
index 118eacf..1680f5c 100644
--- a/src/IconbarTool.hh
+++ b/src/IconbarTool.hh
@@ -60,6 +60,7 @@ public:
     void parentMoved() { m_icon_container.parentMoved(); }
 
     unsigned int width() const;
+    unsigned int preferredWidth() const;
     unsigned int height() const;
     unsigned int borderWidth() const;
 
diff --git a/src/Toolbar.cc b/src/Toolbar.cc
index b607f21..1bd3fb5 100644
--- a/src/Toolbar.cc
+++ b/src/Toolbar.cc
@@ -868,6 +868,8 @@ void Toolbar::rearrangeItems() {
     ItemList::iterator item_it_end = m_item_list.end();
     int bevel_width = theme()->bevelWidth();
     int fixed_width = bevel_width; // combined size of all fixed items
+    int relative_width = 0; // combined *desired* size of all relative items
+    int stretch_items = 0;
     int relative_items = 0;
     int last_bw = 0; // we show the largest border of adjoining items
     bool first = true;
@@ -895,7 +897,7 @@ void Toolbar::rearrangeItems() {
 
         last_bw = borderW;
 
-        tmpw = (*item_it)->width();
+        tmpw = (*item_it)->preferredWidth();
         tmph = (*item_it)->height();
         FbTk::translateSize(orient, tmpw, tmph);
 
@@ -906,18 +908,22 @@ void Toolbar::rearrangeItems() {
             if (bevel_width)
                 fixed_width -= 2*(borderW + bevel_width);
         } else {
-            relative_items++;
+            ++relative_items;
+            relative_width += tmpw;
+            if (!tmpw)
+                ++stretch_items;
         }
     }
 
     // calculate what's going to be left over to the relative sized items
-    int relative_width = 0;
-    int rounding_error = 0;
-    if (relative_items == 0)
-        relative_width = 0;
-    else { // size left after fixed items / number of relative items
-        relative_width = (width - fixed_width) / relative_items;
-        rounding_error = width - fixed_width - relative_items * relative_width;
+    float stretch_factor = 1.0f;
+    if (relative_items) {
+        if (relative_width <= width - fixed_width && stretch_items) {
+            relative_width = int(width - fixed_width - relative_width)/stretch_items;
+        } else {
+            stretch_factor = float(width - fixed_width)/relative_width;
+            relative_width = 0;
+        }
     }
 
     // now move and resize the items
@@ -952,12 +958,9 @@ void Toolbar::rearrangeItems() {
             tmpy = offset;
 
         if ((*item_it)->type() == ToolbarItem::RELATIVE) {
-            int extra = 0;
-            if (rounding_error != 0) { // distribute rounding error over all relatives
-                extra = 1;
-                --rounding_error;
-            }
-            tmpw = extra + relative_width;
+            unsigned int itemw = (*item_it)->preferredWidth(), itemh = (*item_it)->height();
+            FbTk::translateSize(orient, itemw, itemh);
+            tmpw = itemw ? std::floor(stretch_factor * itemw) : relative_width;
             tmph = height - size_offset;
         } else if ((*item_it)->type() == ToolbarItem::SQUARE) {
             tmpw = tmph = height - size_offset;
diff --git a/src/ToolbarItem.hh b/src/ToolbarItem.hh
index bd5b38f..8759b83 100644
--- a/src/ToolbarItem.hh
+++ b/src/ToolbarItem.hh
@@ -47,6 +47,7 @@ public:
     virtual void show() = 0;
     virtual void hide() = 0;
     virtual unsigned int width() const = 0;
+    virtual unsigned int preferredWidth() const { return width(); }
     virtual unsigned int height() const = 0;
     virtual unsigned int borderWidth() const = 0;
     // some items might be there, but effectively empty, so shouldn't appear
diff --git a/src/Window.cc b/src/Window.cc
index f6fd139..871c7e8 100644
--- a/src/Window.cc
+++ b/src/Window.cc
@@ -2804,6 +2804,7 @@ void FluxboxWindow::setTitle(const std::string& title, Focusable &client) {
     frame().setFocusTitle(title);
     // relay title to others that display the focus title
     titleSig().emit(title, *this);
+    frame().tabcontainer().repositionItems();
 }
 
 void FluxboxWindow::frameExtentChanged() {
-- 
cgit v0.11.2