[PATCH libreoffice-4-0] implement spread button layout

Caolán McNamara (via_Code_Review) gerrit at gerrit.libreoffice.org
Sun Feb 3 08:10:15 PST 2013


Hi,

I have submitted a patch for review:

    https://gerrit.libreoffice.org/1973

To pull it, you can do:

    git pull ssh://gerrit.libreoffice.org:29418/core refs/changes/73/1973/1

implement spread button layout

Change-Id: Ia17d3f4d14319adec6b0b20dced5daf5b8018c36
(cherry picked from commit 5dc0c03f797e53aef7411c26782b6d39b7e93d0c)

Resolves: fdo#59767 detect outlier widths and exclude from size normalization

For non-homogeneous (the default) button boxes we want in general to give all
buttons the same width as the max button width.

But if we detect that certain buttons are > 1.5 the average button width, then
leave those outliers at their natural size and set the rest of the buttons to
the max width of the remainder.

(cherry picked from commit 6e81082dbb2d16f0e61527c5ad13f91d49828125)

Conflicts:
	vcl/source/window/layout.cxx

Change-Id: Ice514e741e3a7725d69e150e5752158a1c267141
---
M vcl/inc/vcl/layout.hxx
M vcl/source/window/layout.cxx
2 files changed, 155 insertions(+), 54 deletions(-)



diff --git a/vcl/inc/vcl/layout.hxx b/vcl/inc/vcl/layout.hxx
index d9c5e63..3caae71 100644
--- a/vcl/inc/vcl/layout.hxx
+++ b/vcl/inc/vcl/layout.hxx
@@ -201,9 +201,8 @@
 {
 public:
     VclButtonBox(Window *pParent, int nSpacing)
-        : VclBox(pParent, true, nSpacing)
+        : VclBox(pParent, false, nSpacing)
         , m_eLayoutStyle(VCL_BUTTONBOX_DEFAULT_STYLE)
-        , m_bHomogeneousGroups(false)
     {
     }
     void set_layout(VclButtonBoxStyle eStyle)
@@ -218,20 +217,15 @@
 protected:
     virtual Size calculateRequisition() const;
     virtual void setAllocation(const Size &rAllocation);
+    Size addSpacing(const Size &rSize, sal_uInt16 nVisibleChildren) const;
 private:
     VclButtonBoxStyle m_eLayoutStyle;
-    bool m_bHomogeneousGroups;
     struct Requisition
     {
-        sal_uInt16 m_nMainGroupChildren;
-        sal_uInt16 m_nSubGroupChildren;
+        std::vector<long> m_aMainGroupDimensions;
+        std::vector<long> m_aSubGroupDimensions;
         Size m_aMainGroupSize;
         Size m_aSubGroupSize;
-        Requisition()
-            : m_nMainGroupChildren(0)
-            , m_nSubGroupChildren(0)
-        {
-        }
     };
     Requisition calculatePrimarySecondaryRequisitions() const;
     Size addReqGroups(const VclButtonBox::Requisition &rReq) const;
diff --git a/vcl/source/window/layout.cxx b/vcl/source/window/layout.cxx
index 91017d5..ad19914 100644
--- a/vcl/source/window/layout.cxx
+++ b/vcl/source/window/layout.cxx
@@ -324,23 +324,47 @@
     long nMainGroupDimension = getPrimaryDimension(rReq.m_aMainGroupSize);
     long nSubGroupDimension = getPrimaryDimension(rReq.m_aSubGroupSize);
 
-    assert(m_bHomogeneous);
-
-    if (m_bHomogeneousGroups)
-        setPrimaryDimension(aRet, std::max(nMainGroupDimension, nSubGroupDimension));
-    else
-    {
-        setPrimaryDimension(aRet,
-            (rReq.m_nMainGroupChildren * nMainGroupDimension
-            + rReq.m_nSubGroupChildren * nSubGroupDimension) /
-            (rReq.m_nMainGroupChildren + rReq.m_nSubGroupChildren));
-    }
+    setPrimaryDimension(aRet, nMainGroupDimension + nSubGroupDimension);
 
     setSecondaryDimension(aRet,
         std::max(getSecondaryDimension(rReq.m_aMainGroupSize),
         getSecondaryDimension(rReq.m_aSubGroupSize)));
 
     return aRet;
+}
+
+static long getMaxNonOutlier(const std::vector<long> &rG, long nAvgDimension)
+{
+    long nMaxDimensionNonOutlier = 0;
+    for (std::vector<long>::const_iterator aI = rG.begin(),
+        aEnd = rG.end(); aI != aEnd; ++aI)
+    {
+        long nPrimaryChildDimension = *aI;
+        if (nPrimaryChildDimension <= nAvgDimension * 1.5)
+        {
+            nMaxDimensionNonOutlier = std::max(nPrimaryChildDimension,
+                nMaxDimensionNonOutlier);
+        }
+    }
+    return nMaxDimensionNonOutlier;
+}
+
+static std::vector<long> setButtonSizes(const std::vector<long> &rG,
+    long nAvgDimension, long nMaxNonOutlier)
+{
+    std::vector<long> aVec;
+    //set everything < 1.5 times the average to the same width, leave the
+    //outliers un-touched
+    for (std::vector<long>::const_iterator aI = rG.begin(), aEnd = rG.end();
+        aI != aEnd; ++aI)
+    {
+        long nPrimaryChildDimension = *aI;
+        if (nPrimaryChildDimension <= nAvgDimension * 1.5)
+            aVec.push_back(nMaxNonOutlier);
+        else
+            aVec.push_back(nPrimaryChildDimension);
+    }
+    return aVec;
 }
 
 VclButtonBox::Requisition VclButtonBox::calculatePrimarySecondaryRequisitions() const
@@ -350,36 +374,110 @@
     Size aMainGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
     Size aSubGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
 
+    long nMinMainGroupPrimary = getPrimaryDimension(aMainGroupSize);
+    long nMinSubGroupPrimary = getPrimaryDimension(aSubGroupSize);
+    long nMainGroupSecondary = getSecondaryDimension(aMainGroupSize);
+    long nSubGroupSecondary = getSecondaryDimension(aSubGroupSize);
+
+    bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VCL_BUTTONBOX_SPREAD || m_eLayoutStyle == VCL_BUTTONBOX_CENTER);
+
+    std::vector<long> aMainGroupSizes;
+    std::vector<long> aSubGroupSizes;
+
     for (const Window *pChild = GetWindow(WINDOW_FIRSTCHILD); pChild; pChild = pChild->GetWindow(WINDOW_NEXT))
     {
         if (!pChild->IsVisible())
             continue;
         Size aChildSize = getLayoutRequisition(*pChild);
-        if (!pChild->get_secondary())
+        if (bIgnoreSecondaryPacking || !pChild->get_secondary())
         {
-            ++aReq.m_nMainGroupChildren;
-            accumulateMaxes(aChildSize, aMainGroupSize);
+            //set the max secondary dimension
+            nMainGroupSecondary = std::max(nMainGroupSecondary, getSecondaryDimension(aChildSize));
+            //collect the primary dimensions
+            aMainGroupSizes.push_back(std::max(nMinMainGroupPrimary, getPrimaryDimension(aChildSize)));
         }
         else
         {
-            ++aReq.m_nSubGroupChildren;
-            accumulateMaxes(aChildSize, aSubGroupSize);
+            nSubGroupSecondary = std::max(nSubGroupSecondary, getSecondaryDimension(aChildSize));
+            aSubGroupSizes.push_back(std::max(nMinSubGroupPrimary, getPrimaryDimension(aChildSize)));
         }
     }
 
-    if (aReq.m_nMainGroupChildren)
-        aReq.m_aMainGroupSize = aMainGroupSize;
-    if (aReq.m_nSubGroupChildren)
-        aReq.m_aSubGroupSize = aSubGroupSize;
+    if (m_bHomogeneous)
+    {
+        long nMaxMainDimension = aMainGroupSizes.empty() ? 0 :
+            *std::max_element(aMainGroupSizes.begin(), aMainGroupSizes.end());
+        long nMaxSubDimension = aSubGroupSizes.empty() ? 0 :
+            *std::max_element(aSubGroupSizes.begin(), aSubGroupSizes.end());
+        long nMaxDimension = std::max(nMaxMainDimension, nMaxSubDimension);
+        aReq.m_aMainGroupDimensions.resize(aMainGroupSizes.size(), nMaxDimension);
+        aReq.m_aSubGroupDimensions.resize(aSubGroupSizes.size(), nMaxDimension);
+    }
+    else
+    {
+        //Ideally set everything to the same size, but find outlier widgets
+        //that are way wider than the average and leave them
+        //at their natural size and set the remainder to share the
+        //max size of the remaining members of the buttonbox
+        long nAccDimension = std::accumulate(aMainGroupSizes.begin(),
+            aMainGroupSizes.end(), 0);
+        nAccDimension = std::accumulate(aSubGroupSizes.begin(),
+            aSubGroupSizes.end(), nAccDimension);
+
+        long nAvgDimension = nAccDimension /
+            (aMainGroupSizes.size() + aSubGroupSizes.size());
+
+        long nMaxMainNonOutlier = getMaxNonOutlier(aMainGroupSizes,
+            nAvgDimension);
+        long nMaxSubNonOutlier = getMaxNonOutlier(aSubGroupSizes,
+            nAvgDimension);
+        long nMaxNonOutlier = std::max(nMaxMainNonOutlier, nMaxSubNonOutlier);
+
+        aReq.m_aMainGroupDimensions = setButtonSizes(aMainGroupSizes,
+            nAvgDimension, nMaxNonOutlier);
+        aReq.m_aSubGroupDimensions = setButtonSizes(aSubGroupSizes,
+            nAvgDimension, nMaxNonOutlier);
+    }
+
+    if (!aReq.m_aMainGroupDimensions.empty())
+    {
+        setSecondaryDimension(aReq.m_aMainGroupSize, nMainGroupSecondary);
+        setPrimaryDimension(aReq.m_aMainGroupSize,
+            std::accumulate(aReq.m_aMainGroupDimensions.begin(),
+                aReq.m_aMainGroupDimensions.end(), 0));
+    }
+    if (!aReq.m_aSubGroupDimensions.empty())
+    {
+        setSecondaryDimension(aReq.m_aSubGroupSize, nSubGroupSecondary);
+        setPrimaryDimension(aReq.m_aSubGroupSize,
+            std::accumulate(aReq.m_aSubGroupDimensions.begin(),
+                aReq.m_aSubGroupDimensions.end(), 0));
+    }
 
     return aReq;
+}
+
+Size VclButtonBox::addSpacing(const Size &rSize, sal_uInt16 nVisibleChildren) const
+{
+    Size aRet;
+
+    if (nVisibleChildren)
+    {
+        long nPrimaryDimension = getPrimaryDimension(rSize);
+        setPrimaryDimension(aRet,
+            nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
+        setSecondaryDimension(aRet, getSecondaryDimension(rSize));
+    }
+
+    return aRet;
 }
 
 Size VclButtonBox::calculateRequisition() const
 {
     Requisition aReq(calculatePrimarySecondaryRequisitions());
-    sal_uInt16 nVisibleChildren = aReq.m_nMainGroupChildren + aReq.m_nSubGroupChildren;
-    return finalizeMaxes(addReqGroups(aReq), nVisibleChildren);
+    sal_uInt16 nVisibleChildren = aReq.m_aMainGroupDimensions.size() +
+        aReq.m_aSubGroupDimensions.size();
+    return addSpacing(addReqGroups(aReq), nVisibleChildren);
 }
 
 bool VclButtonBox::set_property(const rtl::OString &rKey, const rtl::OString &rValue)
@@ -405,8 +503,6 @@
         }
         set_layout(eStyle);
     }
-    else if (rKey.equalsL(RTL_CONSTASCII_STRINGPARAM("homogeneous")))
-        m_bHomogeneousGroups = toBool(rValue);
     else
         return VclBox::set_property(rKey, rValue);
     return true;
@@ -416,39 +512,45 @@
 {
     Requisition aReq(calculatePrimarySecondaryRequisitions());
 
-    sal_uInt16 nVisibleChildren = aReq.m_nMainGroupChildren + aReq.m_nSubGroupChildren;
-    if (!nVisibleChildren)
+    if (aReq.m_aMainGroupDimensions.empty() && aReq.m_aSubGroupDimensions.empty())
         return;
 
     long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);
 
-    long nMainGroupPrimaryDimension = getPrimaryDimension(aReq.m_aMainGroupSize);
-    long nSubGroupPrimaryDimension = getPrimaryDimension(aReq.m_aSubGroupSize);
-    if (m_bHomogeneousGroups)
-        nSubGroupPrimaryDimension = nMainGroupPrimaryDimension = std::max(nSubGroupPrimaryDimension, nMainGroupPrimaryDimension);
-
     Point aMainGroupPos, aOtherGroupPos;
+    int nSpacing = m_nSpacing;
 
     //To-Do, other layout styles
     switch (m_eLayoutStyle)
     {
         case VCL_BUTTONBOX_START:
-            if (aReq.m_nSubGroupChildren)
+            if (!aReq.m_aSubGroupDimensions.empty())
             {
                 long nOtherPrimaryDimension = getPrimaryDimension(
-                    finalizeMaxes(aReq.m_aSubGroupSize, aReq.m_nSubGroupChildren));
+                    addSpacing(aReq.m_aSubGroupSize, aReq.m_aSubGroupDimensions.size()));
                 setPrimaryCoordinate(aOtherGroupPos,
                     nAllocPrimaryDimension - nOtherPrimaryDimension);
+            }
+            break;
+        case VCL_BUTTONBOX_SPREAD:
+            if (!aReq.m_aMainGroupDimensions.empty())
+            {
+                long nMainPrimaryDimension = getPrimaryDimension(
+                    addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
+                long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
+                nExtraSpace += (aReq.m_aMainGroupDimensions.size()-1) * nSpacing;
+                nSpacing = nExtraSpace/(aReq.m_aMainGroupDimensions.size()+1);
+                setPrimaryCoordinate(aMainGroupPos, nSpacing);
             }
             break;
         default:
             SAL_WARN("vcl.layout", "todo unimplemented layout style");
         case VCL_BUTTONBOX_DEFAULT_STYLE:
         case VCL_BUTTONBOX_END:
-            if (aReq.m_nMainGroupChildren)
+            if (!aReq.m_aMainGroupDimensions.empty())
             {
                 long nMainPrimaryDimension = getPrimaryDimension(
-                    finalizeMaxes(aReq.m_aMainGroupSize, aReq.m_nMainGroupChildren));
+                    addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
                 setPrimaryCoordinate(aMainGroupPos,
                     nAllocPrimaryDimension - nMainPrimaryDimension);
             }
@@ -458,24 +560,29 @@
     Size aChildSize;
     setSecondaryDimension(aChildSize, getSecondaryDimension(rAllocation));
 
+    std::vector<long>::const_iterator aPrimaryI = aReq.m_aMainGroupDimensions.begin();
+    std::vector<long>::const_iterator aSecondaryI = aReq.m_aSubGroupDimensions.begin();
+    bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VCL_BUTTONBOX_SPREAD || m_eLayoutStyle == VCL_BUTTONBOX_CENTER);
     for (Window *pChild = GetWindow(WINDOW_FIRSTCHILD); pChild; pChild = pChild->GetWindow(WINDOW_NEXT))
     {
         if (!pChild->IsVisible())
             continue;
 
-        if (pChild->get_secondary())
+        if (bIgnoreSecondaryPacking || !pChild->get_secondary())
         {
-            setPrimaryDimension(aChildSize, nSubGroupPrimaryDimension);
-            setLayoutAllocation(*pChild, aOtherGroupPos, aChildSize);
-            long nPrimaryCoordinate = getPrimaryCoordinate(aOtherGroupPos);
-            setPrimaryCoordinate(aOtherGroupPos, nPrimaryCoordinate + nSubGroupPrimaryDimension + m_nSpacing);
-        }
-        else
-        {
+            long nMainGroupPrimaryDimension = *aPrimaryI++;
             setPrimaryDimension(aChildSize, nMainGroupPrimaryDimension);
             setLayoutAllocation(*pChild, aMainGroupPos, aChildSize);
             long nPrimaryCoordinate = getPrimaryCoordinate(aMainGroupPos);
-            setPrimaryCoordinate(aMainGroupPos, nPrimaryCoordinate + nMainGroupPrimaryDimension + m_nSpacing);
+            setPrimaryCoordinate(aMainGroupPos, nPrimaryCoordinate + nMainGroupPrimaryDimension + nSpacing);
+        }
+        else
+        {
+            long nSubGroupPrimaryDimension = *aSecondaryI++;
+            setPrimaryDimension(aChildSize, nSubGroupPrimaryDimension);
+            setLayoutAllocation(*pChild, aOtherGroupPos, aChildSize);
+            long nPrimaryCoordinate = getPrimaryCoordinate(aOtherGroupPos);
+            setPrimaryCoordinate(aOtherGroupPos, nPrimaryCoordinate + nSubGroupPrimaryDimension + nSpacing);
         }
     }
 }

-- 
To view, visit https://gerrit.libreoffice.org/1973
To unsubscribe, visit https://gerrit.libreoffice.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ice514e741e3a7725d69e150e5752158a1c267141
Gerrit-PatchSet: 1
Gerrit-Project: core
Gerrit-Branch: libreoffice-4-0
Gerrit-Owner: Caolán McNamara <caolanm at redhat.com>


More information about the LibreOffice mailing list