]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/sizer.cpp
Return NULL from wxWindow::GetCapture() when the capture is being lost.
[wxWidgets.git] / src / common / sizer.cpp
index 872145da95d120f3fbaa1c743f369e4c38b93320..31bed2c619db8fa09f0cd1191bccbc4c29c6663e 100644 (file)
@@ -5,7 +5,6 @@
 //              Dirk Holtwick, Ron Lee
 // Modified by: Ron Lee
 // Created:
-// RCS-ID:      $Id$
 // Copyright:   (c) Robin Dunn, Robert Roebling
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
@@ -207,6 +206,33 @@ void wxSizerItem::DoSetSpacer(const wxSize& size)
     SetRatio(size);
 }
 
+wxSize wxSizerItem::AddBorderToSize(const wxSize& size) const
+{
+    wxSize result = size;
+
+    // Notice that we shouldn't modify the unspecified component(s) of the
+    // size, it's perfectly valid to have either min or max size specified in
+    // one direction only and it shouldn't be applied in the other one then.
+
+    if ( result.x != wxDefaultCoord )
+    {
+        if (m_flag & wxWEST)
+            result.x += m_border;
+        if (m_flag & wxEAST)
+            result.x += m_border;
+    }
+
+    if ( result.y != wxDefaultCoord )
+    {
+        if (m_flag & wxNORTH)
+            result.y += m_border;
+        if (m_flag & wxSOUTH)
+            result.y += m_border;
+    }
+
+    return result;
+}
+
 wxSizerItem::wxSizerItem(int width,
                          int height,
                          int proportion,
@@ -397,20 +423,13 @@ wxSize wxSizerItem::CalcMin()
 
 wxSize wxSizerItem::GetMinSizeWithBorder() const
 {
-    wxSize ret = m_minSize;
-
-    if (m_flag & wxWEST)
-        ret.x += m_border;
-    if (m_flag & wxEAST)
-        ret.x += m_border;
-    if (m_flag & wxNORTH)
-        ret.y += m_border;
-    if (m_flag & wxSOUTH)
-        ret.y += m_border;
-
-    return ret;
+    return AddBorderToSize(m_minSize);
 }
 
+wxSize wxSizerItem::GetMaxSizeWithBorder() const
+{
+    return AddBorderToSize(GetMaxSize());
+}
 
 void wxSizerItem::SetDimension( const wxPoint& pos_, const wxSize& size_ )
 {
@@ -486,7 +505,7 @@ void wxSizerItem::SetDimension( const wxPoint& pos_, const wxSize& size_ )
             // have changed alignment or some other property which would
             // not change the size of the window. In such a case, no
             // wxSizeEvent would normally be generated and thus the
-            // control wouldn't get layed out correctly here.
+            // control wouldn't get laid out correctly here.
 #if 1
             m_window->SetSize(pos.x, pos.y, size.x, size.y,
                               wxSIZE_ALLOW_MINUS_ONE|wxSIZE_FORCE_EVENT );
@@ -585,22 +604,7 @@ bool wxSizerItem::IsShown() const
             // arbitrarily decide that if at least one of our elements is
             // shown, so are we (this arbitrariness is the reason for
             // deprecating this function)
-            {
-                // Some apps (such as dialog editors) depend on an empty sizer still
-                // being laid out correctly and reporting the correct size and position.
-                if (m_sizer->GetChildren().GetCount() == 0)
-                    return true;
-
-                for ( wxSizerItemList::compatibility_iterator
-                        node = m_sizer->GetChildren().GetFirst();
-                      node;
-                      node = node->GetNext() )
-                {
-                    if ( node->GetData()->IsShown() )
-                        return true;
-                }
-            }
-            return false;
+            return m_sizer->AreAnyItemsShown();
 
         case Item_Spacer:
             return m_spacer->IsShown();
@@ -845,6 +849,10 @@ bool wxSizer::Replace( size_t old, wxSizerItem *newitem )
 
     wxSizerItem *item = node->GetData();
     node->SetData(newitem);
+
+    if (item->IsWindow() && item->GetWindow())
+        item->GetWindow()->SetContainingSizer(NULL);
+
     delete item;
 
     return true;
@@ -910,6 +918,11 @@ wxSize wxSizer::ComputeFittingClientSize(wxWindow *window)
 
         sizeMax = wxDisplay(disp).GetClientArea().GetSize();
 
+        // If determining the display size failed, skip the max size checks as
+        // we really don't want to create windows of (0, 0) size.
+        if ( !sizeMax.x || !sizeMax.y )
+            return size;
+
         // space for decorations and toolbars etc.
         sizeMax = tlw->WindowToClientSize(sizeMax);
     }
@@ -962,6 +975,8 @@ void wxSizer::Layout()
     CalcMin();
 
     // Applies the layout and repositions/resizes the items
+    wxWindow::ChildrenRepositioningGuard repositionGuard(m_containingWindow);
+
     RecalcSizes();
 }
 
@@ -1266,6 +1281,19 @@ void wxSizer::ShowItems( bool show )
     }
 }
 
+bool wxSizer::AreAnyItemsShown() const
+{
+    wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
+    while (node)
+    {
+        if ( node->GetData()->IsShown() )
+            return true;
+        node = node->GetNext();
+    }
+
+    return false;
+}
+
 bool wxSizer::IsShown( wxWindow *window ) const
 {
     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
@@ -1324,6 +1352,7 @@ wxGridSizer::wxGridSizer( int cols, int vgap, int hgap )
       m_vgap( vgap ),
       m_hgap( hgap )
 {
+    wxASSERT(cols >= 0);
 }
 
 wxGridSizer::wxGridSizer( int cols, const wxSize& gap )
@@ -1332,6 +1361,7 @@ wxGridSizer::wxGridSizer( int cols, const wxSize& gap )
       m_vgap( gap.GetHeight() ),
       m_hgap( gap.GetWidth() )
 {
+    wxASSERT(cols >= 0);
 }
 
 wxGridSizer::wxGridSizer( int rows, int cols, int vgap, int hgap )
@@ -1340,6 +1370,7 @@ wxGridSizer::wxGridSizer( int rows, int cols, int vgap, int hgap )
       m_vgap( vgap ),
       m_hgap( hgap )
 {
+    wxASSERT(rows >= 0 && cols >= 0);
 }
 
 wxGridSizer::wxGridSizer( int rows, int cols, const wxSize& gap )
@@ -1348,6 +1379,7 @@ wxGridSizer::wxGridSizer( int rows, int cols, const wxSize& gap )
       m_vgap( gap.GetHeight() ),
       m_hgap( gap.GetWidth() )
 {
+    wxASSERT(rows >= 0 && cols >= 0);
 }
 
 wxSizerItem *wxGridSizer::DoInsert(size_t index, wxSizerItem *item)
@@ -2074,9 +2106,8 @@ void wxBoxSizer::RecalcSizes()
         minMajorSize += GetSizeInMajorDir(item->GetMinSizeWithBorder());
     }
 
-    // update our min size and delta which may have changed
+    // update our min size have changed
     SizeInMajorDir(m_minSize) = minMajorSize;
-    delta = totalMajorSize - minMajorSize;
 
 
     // space and sum of proportions for the remaining items, both may change
@@ -2090,7 +2121,7 @@ void wxBoxSizer::RecalcSizes()
 
     // Check for the degenerated case when we don't have enough space for even
     // the min sizes of all the items: in this case we really can't do much
-    // more than to to allocate the min size to as many of fixed size items as
+    // more than to allocate the min size to as many of fixed size items as
     // possible (on the assumption that variable size items such as text zones
     // or list boxes may use scrollbars to show their content even if their
     // size is less than min size but that fixed size items such as buttons
@@ -2171,8 +2202,19 @@ void wxBoxSizer::RecalcSizes()
             if ( majorSizes[n] != wxDefaultCoord )
                 continue;
 
-            const wxCoord
-                minMajor = GetSizeInMajorDir(item->GetMinSizeWithBorder());
+            wxCoord minMajor = GetSizeInMajorDir(item->GetMinSizeWithBorder());
+
+            // it doesn't make sense for min size to be negative but right now
+            // it's possible to create e.g. a spacer with (-1, 10) as size and
+            // people do it in their code apparently (see #11842) so ensure
+            // that we don't use this -1 as real min size as it conflicts with
+            // the meaning we use for it here and negative min sizes just don't
+            // make sense anyhow (which is why it might be a better idea to
+            // deal with them at wxSizerItem level in the future but for now
+            // this is the minimal fix for the bug)
+            if ( minMajor < 0 )
+                minMajor = 0;
+
             const int propItem = item->GetProportion();
             if ( propItem )
             {
@@ -2201,6 +2243,66 @@ void wxBoxSizer::RecalcSizes()
             nonFixedSpaceChanged = true;
         }
 
+        // Similar to the previous loop, but dealing with items whose max size
+        // is less than what we would allocate to them taking their proportion
+        // into account.
+        nonFixedSpaceChanged = false;
+        for ( i = m_children.begin(), n = 0; ; ++i, ++n )
+        {
+            if ( nonFixedSpaceChanged )
+            {
+                i = m_children.begin();
+                n = 0;
+                nonFixedSpaceChanged = false;
+            }
+
+            // check for the end of the loop only after the check above as
+            // otherwise we wouldn't do another pass if the last child resulted
+            // in non fixed space reduction
+            if ( i == m_children.end() )
+                break;
+
+            wxSizerItem * const item = *i;
+
+            if ( !item->IsShown() )
+                continue;
+
+            // don't check the item which we had already dealt with during a
+            // previous pass (this is more than an optimization, the code
+            // wouldn't work correctly if we kept adjusting for the same item
+            // over and over again)
+            if ( majorSizes[n] != wxDefaultCoord )
+                continue;
+
+            wxCoord maxMajor = GetSizeInMajorDir(item->GetMaxSizeWithBorder());
+
+            // must be nonzero, fixed-size items were dealt with in previous loop
+            const int propItem = item->GetProportion();
+
+            // is the desired size of this item small enough?
+            if ( maxMajor < 0 ||
+                    (remaining*propItem)/totalProportion <= maxMajor )
+            {
+                // yes, it is, we'll determine the real size of this
+                // item later, for now just leave it as wxDefaultCoord
+                continue;
+            }
+
+            // the proportion of this item won't count, it has
+            // effectively become fixed
+            totalProportion -= propItem;
+
+            // we can already allocate space for this item
+            majorSizes[n] = maxMajor;
+
+            // change the amount of the space remaining to the other items,
+            // as this can result in not being able to satisfy their
+            // proportions any more we will need to redo another loop
+            // iteration
+            remaining -= maxMajor;
+
+            nonFixedSpaceChanged = true;
+        }
 
         // Last by one pass: distribute the remaining space among the non-fixed
         // items whose size weren't fixed yet according to their proportions.
@@ -2252,8 +2354,15 @@ void wxBoxSizer::RecalcSizes()
             // its minimal size which is bad but better than not showing parts
             // of the window at all
             minorSize = totalMinorSize;
+
+            // do not allow the size in the minor direction to grow beyond the max
+            // size of the item in the minor direction
+            const wxCoord maxMinorSize = GetSizeInMinorDir(item->GetMaxSizeWithBorder());
+            if ( maxMinorSize >= 0 && minorSize > maxMinorSize )
+                minorSize = maxMinorSize;
         }
-        else if ( flag & (IsVertical() ? wxALIGN_RIGHT : wxALIGN_BOTTOM) )
+
+        if ( flag & (IsVertical() ? wxALIGN_RIGHT : wxALIGN_BOTTOM) )
         {
             PosInMinorDir(posChild) += totalMinorSize - minorSize;
         }
@@ -2289,7 +2398,12 @@ wxSize wxBoxSizer::CalcMin()
     m_totalProportion = 0;
     m_minSize = wxSize(0, 0);
 
-    // calculate the minimal sizes for all items and count sum of proportions
+    // The minimal size for the sizer should be big enough to allocate its
+    // element at least its minimal size but also, and this is the non trivial
+    // part, to respect the children proportion. To satisfy the latter
+    // condition we must find the greatest min-size-to-proportion ratio for all
+    // elements with non-zero proportion.
+    float maxMinSizeToProp = 0.;
     for ( wxSizerItemList::const_iterator i = m_children.begin();
           i != m_children.end();
           ++i )
@@ -2300,13 +2414,31 @@ wxSize wxBoxSizer::CalcMin()
             continue;
 
         const wxSize sizeMinThis = item->CalcMin();
-        SizeInMajorDir(m_minSize) += GetSizeInMajorDir(sizeMinThis);
+        if ( const int propThis = item->GetProportion() )
+        {
+            float minSizeToProp = GetSizeInMajorDir(sizeMinThis);
+            minSizeToProp /= propThis;
+
+            if ( minSizeToProp > maxMinSizeToProp )
+                maxMinSizeToProp = minSizeToProp;
+
+            m_totalProportion += item->GetProportion();
+        }
+        else // fixed size item
+        {
+            // Just account for its size directly
+            SizeInMajorDir(m_minSize) += GetSizeInMajorDir(sizeMinThis);
+        }
+
+        // In the transversal direction we just need to find the maximum.
         if ( GetSizeInMinorDir(sizeMinThis) > GetSizeInMinorDir(m_minSize) )
             SizeInMinorDir(m_minSize) = GetSizeInMinorDir(sizeMinThis);
-
-        m_totalProportion += item->GetProportion();
     }
 
+    // Using the max ratio ensures that the min size is big enough for all
+    // items to have their min size and satisfy the proportions among them.
+    SizeInMajorDir(m_minSize) += (int)(maxMinSizeToProp*m_totalProportion);
+
     return m_minSize;
 }
 
@@ -2360,8 +2492,12 @@ void wxStaticBoxSizer::RecalcSizes()
         // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
         // to the top-left corner of the staticbox:
         m_position.x = m_position.y = 0;
+#elif defined(__WXOSX__) && wxOSX_USE_COCOA
+        // the distance from the 'inner' content view to the embedded controls
+        // this is independent of the title, therefore top_border is not relevant
+        m_position.x = m_position.y = 10;
 #else
-        // if the wxStaticBox has childrens, then these windows must be placed
+        // if the wxStaticBox has children, then these windows must be placed
         // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
         // to the top-left corner of the staticbox (but unlike wxGTK, we need
         // to keep in count the static borders here!):
@@ -2409,6 +2545,16 @@ void wxStaticBoxSizer::ShowItems( bool show )
     wxBoxSizer::ShowItems( show );
 }
 
+bool wxStaticBoxSizer::AreAnyItemsShown() const
+{
+    // We don't need to check the status of our child items: if the box is
+    // shown, this sizer should be considered shown even if all its elements
+    // are hidden (or, more prosaically, there are no elements at all). And,
+    // conversely, if the box is hidden then all our items, which are its
+    // children, are hidden too.
+    return m_staticBox->IsShown();
+}
+
 bool wxStaticBoxSizer::Detach( wxWindow *window )
 {
     // avoid deleting m_staticBox in our dtor if it's being detached from the
@@ -2538,30 +2684,38 @@ void wxStdDialogButtonSizer::Realize()
         // Extra space around and at the right
         Add(12, 40);
 #elif defined(__WXGTK20__)
-        Add(0, 0, 0, wxLEFT, 9);
+        // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
+        // says that the correct button order is
+        //
+        //      [Help]                  [Alternative] [Cancel] [Affirmative]
+
+        // Flags ensuring that margins between the buttons are 6 pixels.
+        const wxSizerFlags
+            flagsBtn = wxSizerFlags().Centre().Border(wxLEFT | wxRIGHT, 3);
+
+        // Margin around the entire sizer button should be 12.
+        AddSpacer(9);
+
         if (m_buttonHelp)
-            Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
+            Add(m_buttonHelp, flagsBtn);
 
-        // extra whitespace between help and cancel/ok buttons
-        Add(0, 0, 1, wxEXPAND, 0);
+        // Align the rest of the buttons to the right.
+        AddStretchSpacer();
 
-        if (m_buttonNegative){
-            Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
-        }
+        if (m_buttonNegative)
+            Add(m_buttonNegative, flagsBtn);
 
-        // according to HIG, in explicit apply windows the order is:
-        // [ Help                     Apply   Cancel   OK ]
         if (m_buttonApply)
-            Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
+            Add(m_buttonApply, flagsBtn);
 
-        if (m_buttonCancel){
-            Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
-            // Cancel or help should be default
-            // m_buttonCancel->SetDefaultButton();
-        }
+        if (m_buttonCancel)
+            Add(m_buttonCancel, flagsBtn);
 
         if (m_buttonAffirmative)
-            Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT, 6);
+            Add(m_buttonAffirmative, flagsBtn);
+
+        // Ensure that the right margin is 12 as well.
+        AddSpacer(9);
 #elif defined(__WXMSW__)
         // Windows