wxRibbonButtonBarButton: Implemented manual button size class control
Added new functions SetButtonMinSizeClass() and SetButtonMaxSizeClass() that set a button's minimum and maximum allowed size class during ribbon layout collapsing. Size classes are values from wxRibbonButtonBarButtonState enum. wxRibbonButtonBar::MakeLayouts() and wxRibbonButtonBar::TryCollapseLayout() are modified to support this new behaviour. The modified wxRibbonButtonBar provate layout routines are aware of wxRIBBON_BUTTONBAR_BUTTON_SMALL which is not implemented yet in MSW wxRibbonMSWArtProvider::DrawButtonBarButtonForeground.
This commit is contained in:
@@ -150,6 +150,10 @@ public:
|
||||
virtual void SetButtonTextMinWidth(int button_id,
|
||||
int min_width_medium, int min_width_large);
|
||||
virtual void SetButtonTextMinWidth(int button_id, const wxString& label);
|
||||
virtual void SetButtonMinSizeClass(int button_id,
|
||||
wxRibbonButtonBarButtonState min_size_class);
|
||||
virtual void SetButtonMaxSizeClass(int button_id,
|
||||
wxRibbonButtonBarButtonState max_size_class);
|
||||
|
||||
virtual wxRibbonButtonBarButtonBase *GetActiveItem() const;
|
||||
virtual wxRibbonButtonBarButtonBase *GetHoveredItem() const;
|
||||
@@ -183,7 +187,9 @@ protected:
|
||||
|
||||
void CommonInit(long style);
|
||||
void MakeLayouts();
|
||||
bool TryCollapseLayout(wxRibbonButtonBarLayout* original, size_t first_btn, size_t* last_button);
|
||||
void TryCollapseLayout(wxRibbonButtonBarLayout* original,
|
||||
size_t first_btn, size_t* last_button,
|
||||
wxRibbonButtonBarButtonState target_size);
|
||||
void MakeBitmaps(wxRibbonButtonBarButtonBase* base,
|
||||
const wxBitmap& bitmap_large,
|
||||
const wxBitmap& bitmap_large_disabled,
|
||||
|
@@ -568,6 +568,38 @@ public:
|
||||
*/
|
||||
virtual void SetButtonTextMinWidth(int button_id, const wxString& label);
|
||||
|
||||
/**
|
||||
Sets the minimum size class of a ribbon button.
|
||||
|
||||
You have to call Realize() after calling this function to
|
||||
apply the given minimum size.
|
||||
|
||||
@param button_id
|
||||
ID of the button to manipulate.
|
||||
@param min_size_class
|
||||
The minimum size-class of the button. Buttons on a button bar
|
||||
can have three distinct sizes: wxRIBBON_BUTTONBAR_BUTTON_SMALL,
|
||||
wxRIBBON_BUTTONBAR_BUTTON_MEDIUM, and wxRIBBON_BUTTONBAR_BUTTON_LARGE.
|
||||
*/
|
||||
virtual void SetButtonMinSizeClass(int button_id,
|
||||
wxRibbonButtonBarButtonState min_size_class);
|
||||
|
||||
/**
|
||||
Sets the maximum size class of a ribbon button.
|
||||
|
||||
You have to call Realize() after calling this function to
|
||||
apply the given maximum size.
|
||||
|
||||
@param button_id
|
||||
ID of the button to manipulate.
|
||||
@param max_size_class
|
||||
The maximum size-class of the button. Buttons on a button bar
|
||||
can have three distinct sizes: wxRIBBON_BUTTONBAR_BUTTON_SMALL,
|
||||
wxRIBBON_BUTTONBAR_BUTTON_MEDIUM, and wxRIBBON_BUTTONBAR_BUTTON_LARGE.
|
||||
*/
|
||||
virtual void SetButtonMaxSizeClass(int button_id,
|
||||
wxRibbonButtonBarButtonState max_size_class);
|
||||
|
||||
/**
|
||||
Returns the active item of the button bar or NULL if there is none.
|
||||
The active button is the one being clicked.
|
||||
|
@@ -75,10 +75,16 @@ public:
|
||||
|
||||
wxRibbonButtonBarButtonState GetLargestSize()
|
||||
{
|
||||
if(sizes[wxRIBBON_BUTTONBAR_BUTTON_LARGE].is_supported)
|
||||
if(sizes[wxRIBBON_BUTTONBAR_BUTTON_LARGE].is_supported
|
||||
&& max_size_class >= wxRIBBON_BUTTONBAR_BUTTON_LARGE)
|
||||
{
|
||||
return wxRIBBON_BUTTONBAR_BUTTON_LARGE;
|
||||
if(sizes[wxRIBBON_BUTTONBAR_BUTTON_MEDIUM].is_supported)
|
||||
}
|
||||
if(sizes[wxRIBBON_BUTTONBAR_BUTTON_MEDIUM].is_supported
|
||||
&& max_size_class >= wxRIBBON_BUTTONBAR_BUTTON_MEDIUM)
|
||||
{
|
||||
return wxRIBBON_BUTTONBAR_BUTTON_MEDIUM;
|
||||
}
|
||||
wxASSERT(sizes[wxRIBBON_BUTTONBAR_BUTTON_SMALL].is_supported);
|
||||
return wxRIBBON_BUTTONBAR_BUTTON_SMALL;
|
||||
}
|
||||
@@ -91,14 +97,16 @@ public:
|
||||
switch(*size)
|
||||
{
|
||||
case wxRIBBON_BUTTONBAR_BUTTON_LARGE:
|
||||
if(sizes[wxRIBBON_BUTTONBAR_BUTTON_MEDIUM].is_supported)
|
||||
if(sizes[wxRIBBON_BUTTONBAR_BUTTON_MEDIUM].is_supported
|
||||
&& min_size_class <= wxRIBBON_BUTTONBAR_BUTTON_MEDIUM)
|
||||
{
|
||||
*size = wxRIBBON_BUTTONBAR_BUTTON_MEDIUM;
|
||||
break;
|
||||
}
|
||||
wxFALLTHROUGH;
|
||||
case wxRIBBON_BUTTONBAR_BUTTON_MEDIUM:
|
||||
if(sizes[wxRIBBON_BUTTONBAR_BUTTON_SMALL].is_supported)
|
||||
if(sizes[wxRIBBON_BUTTONBAR_BUTTON_SMALL].is_supported
|
||||
&& min_size_class <= wxRIBBON_BUTTONBAR_BUTTON_SMALL)
|
||||
{
|
||||
*size = wxRIBBON_BUTTONBAR_BUTTON_SMALL;
|
||||
break;
|
||||
@@ -120,6 +128,8 @@ public:
|
||||
wxBitmap bitmap_small_disabled;
|
||||
wxCoord text_min_width[3];
|
||||
wxRibbonButtonBarButtonSizeInfo sizes[3];
|
||||
wxRibbonButtonBarButtonState min_size_class;
|
||||
wxRibbonButtonBarButtonState max_size_class;
|
||||
wxClientDataContainer client_data;
|
||||
int id;
|
||||
wxRibbonButtonKind kind;
|
||||
@@ -332,6 +342,8 @@ wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertButton(
|
||||
base->text_min_width[0] = 0;
|
||||
base->text_min_width[1] = 0;
|
||||
base->text_min_width[2] = 0;
|
||||
base->min_size_class = wxRIBBON_BUTTONBAR_BUTTON_SMALL;
|
||||
base->max_size_class = wxRIBBON_BUTTONBAR_BUTTON_LARGE;
|
||||
|
||||
wxClientDC temp_dc(this);
|
||||
FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_SMALL, temp_dc);
|
||||
@@ -635,6 +647,36 @@ void wxRibbonButtonBar::SetButtonTextMinWidth(
|
||||
m_layouts_valid = false;
|
||||
}
|
||||
|
||||
void wxRibbonButtonBar::SetButtonMinSizeClass(int button_id,
|
||||
wxRibbonButtonBarButtonState min_size_class)
|
||||
{
|
||||
wxRibbonButtonBarButtonBase* base = GetItemById(button_id);
|
||||
if(base == NULL)
|
||||
return;
|
||||
if(base->max_size_class < min_size_class)
|
||||
{
|
||||
wxFAIL_MSG("Button minimum size is larger than maximum size");
|
||||
return;
|
||||
}
|
||||
base->min_size_class = min_size_class;
|
||||
m_layouts_valid = false;
|
||||
}
|
||||
|
||||
void wxRibbonButtonBar::SetButtonMaxSizeClass(int button_id,
|
||||
wxRibbonButtonBarButtonState max_size_class)
|
||||
{
|
||||
wxRibbonButtonBarButtonBase* base = GetItemById(button_id);
|
||||
if(base == NULL)
|
||||
return;
|
||||
if(base->min_size_class > max_size_class)
|
||||
{
|
||||
wxFAIL_MSG("Button maximum size is smaller than minimum size");
|
||||
return;
|
||||
}
|
||||
base->max_size_class = max_size_class;
|
||||
m_layouts_valid = false;
|
||||
}
|
||||
|
||||
void wxRibbonButtonBar::SetArtProvider(wxRibbonArtProvider* art)
|
||||
{
|
||||
if(art == m_art)
|
||||
@@ -905,8 +947,27 @@ void wxRibbonButtonBar::MakeLayouts()
|
||||
}
|
||||
size_t btn_count = m_buttons.Count();
|
||||
size_t btn_i;
|
||||
|
||||
// Determine available height:
|
||||
// 1 large button or, if not found, 3 medium or small buttons
|
||||
int available_height = 0;
|
||||
bool large_button_found = false;
|
||||
for(btn_i = 0; btn_i < btn_count; ++btn_i)
|
||||
{
|
||||
// Best layout : all buttons large, stacking horizontally
|
||||
wxRibbonButtonBarButtonBase* button = m_buttons.Item(btn_i);
|
||||
wxRibbonButtonBarButtonState size_class = button->GetLargestSize();
|
||||
available_height = wxMax(available_height,
|
||||
button->sizes[size_class].size.GetHeight());
|
||||
if(size_class == wxRIBBON_BUTTONBAR_BUTTON_LARGE)
|
||||
large_button_found = true;
|
||||
}
|
||||
if(!large_button_found)
|
||||
available_height *= 3;
|
||||
|
||||
int stacked_width = 0;
|
||||
{
|
||||
// Best layout : all buttons large, stacking horizontally,
|
||||
// small buttons small, stacked vertically
|
||||
wxRibbonButtonBarLayout* layout = new wxRibbonButtonBarLayout;
|
||||
wxPoint cursor(0, 0);
|
||||
layout->overall_size.SetHeight(0);
|
||||
@@ -917,54 +978,111 @@ void wxRibbonButtonBar::MakeLayouts()
|
||||
instance.position = cursor;
|
||||
instance.size = button->GetLargestSize();
|
||||
wxSize& size = button->sizes[instance.size].size;
|
||||
|
||||
if(instance.size < wxRIBBON_BUTTONBAR_BUTTON_LARGE)
|
||||
{
|
||||
stacked_width = wxMax(stacked_width, size.GetWidth());
|
||||
if(cursor.y + size.GetHeight() >= available_height)
|
||||
{
|
||||
cursor.y = 0;
|
||||
cursor.x += stacked_width;
|
||||
stacked_width = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor.y += size.GetHeight();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(cursor.y != 0)
|
||||
{
|
||||
cursor.y = 0;
|
||||
cursor.x += stacked_width;
|
||||
stacked_width = 0;
|
||||
instance.position = cursor;
|
||||
}
|
||||
cursor.x += size.GetWidth();
|
||||
layout->overall_size.SetHeight(wxMax(layout->overall_size.GetHeight(),
|
||||
size.GetHeight()));
|
||||
}
|
||||
layout->buttons.Add(instance);
|
||||
}
|
||||
layout->overall_size.SetWidth(cursor.x);
|
||||
layout->overall_size.SetHeight(available_height);
|
||||
layout->overall_size.SetWidth(cursor.x + stacked_width);
|
||||
m_layouts.Add(layout);
|
||||
}
|
||||
if(btn_count >= 2)
|
||||
{
|
||||
// Collapse the rightmost buttons and stack them vertically
|
||||
size_t iLast = btn_count - 1;
|
||||
while(TryCollapseLayout(m_layouts.Last(), iLast, &iLast) && iLast > 0)
|
||||
// if they are not already small. If rightmost buttons can't
|
||||
// be collapsed because "min_size_class" is set, try it again
|
||||
// starting from second rightmost button and so on.
|
||||
size_t iLast = btn_count;
|
||||
while(iLast-- > 0)
|
||||
{
|
||||
--iLast;
|
||||
TryCollapseLayout(m_layouts.Last(), iLast, &iLast,
|
||||
wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
|
||||
}
|
||||
|
||||
// TODO: small buttons are not implemented yet in
|
||||
// art_msw.cpp:2581 and will be invisible
|
||||
/*iLast = btn_count;
|
||||
while(iLast-- > 0)
|
||||
{
|
||||
TryCollapseLayout(m_layouts.Last(), iLast, &iLast,
|
||||
wxRIBBON_BUTTONBAR_BUTTON_SMALL);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
bool wxRibbonButtonBar::TryCollapseLayout(wxRibbonButtonBarLayout* original,
|
||||
size_t first_btn, size_t* last_button)
|
||||
void wxRibbonButtonBar::TryCollapseLayout(wxRibbonButtonBarLayout* original,
|
||||
size_t first_btn, size_t* last_button,
|
||||
wxRibbonButtonBarButtonState target_size)
|
||||
{
|
||||
size_t btn_count = m_buttons.Count();
|
||||
size_t btn_i;
|
||||
int used_height = 0;
|
||||
int used_width = 0;
|
||||
int original_column_width = 0;
|
||||
int available_width = 0;
|
||||
int available_height = 0;
|
||||
int available_height = original->overall_size.GetHeight();
|
||||
|
||||
// Search for button range from right which should be
|
||||
// collapsed into a column of small buttons.
|
||||
for(btn_i = first_btn + 1; btn_i > 0; /* decrement is inside loop */)
|
||||
{
|
||||
--btn_i;
|
||||
wxRibbonButtonBarButtonBase* button = m_buttons.Item(btn_i);
|
||||
wxRibbonButtonBarButtonState large_size_class = button->GetLargestSize();
|
||||
wxSize large_size = button->sizes[large_size_class].size;
|
||||
int t_available_height = wxMax(available_height,
|
||||
large_size.GetHeight());
|
||||
int t_available_width = available_width + large_size.GetWidth();
|
||||
wxRibbonButtonBarButtonState small_size_class = large_size_class;
|
||||
if(!button->GetSmallerSize(&small_size_class))
|
||||
int t_available_width = available_width;
|
||||
|
||||
original_column_width = wxMax(original_column_width,
|
||||
large_size.GetWidth());
|
||||
|
||||
// Top button in column: add column width to available width
|
||||
if(original->buttons.Item(btn_i).position.y == 0)
|
||||
{
|
||||
return false;
|
||||
t_available_width += original_column_width;
|
||||
original_column_width = 0;
|
||||
}
|
||||
|
||||
wxRibbonButtonBarButtonState small_size_class = large_size_class;
|
||||
if(large_size_class > target_size)
|
||||
{
|
||||
if(!button->GetSmallerSize(&small_size_class,
|
||||
small_size_class - target_size))
|
||||
{
|
||||
// Large button that cannot shrink: stop search
|
||||
++btn_i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
wxSize small_size = button->sizes[small_size_class].size;
|
||||
int t_used_height = used_height + small_size.GetHeight();
|
||||
int t_used_width = wxMax(used_width, small_size.GetWidth());
|
||||
|
||||
if(t_used_height > t_available_height)
|
||||
// Height is full: stop search
|
||||
if(t_used_height > available_height)
|
||||
{
|
||||
++btn_i;
|
||||
break;
|
||||
@@ -974,13 +1092,13 @@ bool wxRibbonButtonBar::TryCollapseLayout(wxRibbonButtonBarLayout* original,
|
||||
used_height = t_used_height;
|
||||
used_width = t_used_width;
|
||||
available_width = t_available_width;
|
||||
available_height = t_available_height;
|
||||
}
|
||||
}
|
||||
|
||||
// Layout got wider than before or no suitable button found: abort
|
||||
if(btn_i >= first_btn || used_width >= available_width)
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if(last_button != NULL)
|
||||
{
|
||||
@@ -990,27 +1108,23 @@ bool wxRibbonButtonBar::TryCollapseLayout(wxRibbonButtonBarLayout* original,
|
||||
wxRibbonButtonBarLayout* layout = new wxRibbonButtonBarLayout;
|
||||
WX_APPEND_ARRAY(layout->buttons, original->buttons);
|
||||
wxPoint cursor(layout->buttons.Item(btn_i).position);
|
||||
bool preserve_height = false;
|
||||
if(btn_i == 0)
|
||||
{
|
||||
// If height isn't preserved (i.e. it is reduced), then the minimum
|
||||
// size for the button bar will decrease, preventing the original
|
||||
// layout from being used (in some cases).
|
||||
// It may be a good idea to always preserve the height, but for now
|
||||
// it is only done when the first button is involved in a collapse.
|
||||
preserve_height = true;
|
||||
}
|
||||
|
||||
cursor.y = 0;
|
||||
for(; btn_i <= first_btn; ++btn_i)
|
||||
{
|
||||
wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
|
||||
instance.base->GetSmallerSize(&instance.size);
|
||||
if(instance.size > target_size)
|
||||
{
|
||||
instance.base->GetSmallerSize(&instance.size,
|
||||
instance.size - target_size);
|
||||
}
|
||||
instance.position = cursor;
|
||||
cursor.y += instance.base->sizes[instance.size].size.GetHeight();
|
||||
}
|
||||
|
||||
int x_adjust = available_width - used_width;
|
||||
|
||||
// Adjust x coords of buttons right of shrinked column
|
||||
for(; btn_i < btn_count; ++btn_i)
|
||||
{
|
||||
wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
|
||||
@@ -1025,16 +1139,19 @@ bool wxRibbonButtonBar::TryCollapseLayout(wxRibbonButtonBarLayout* original,
|
||||
{
|
||||
delete layout;
|
||||
wxFAIL_MSG("Layout collapse resulted in increased size");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(preserve_height)
|
||||
{
|
||||
// If height isn't preserved (i.e. it is reduced), then the minimum
|
||||
// size for the button bar will decrease, preventing the original
|
||||
// layout from being used (in some cases).
|
||||
// If neither "min_size_class" nor "max_size_class" is set, this is
|
||||
// only required when the first button is involved in a collapse but
|
||||
// if small, medium and large buttons as well as min/max size classes
|
||||
// are involved this is always a good idea.
|
||||
layout->overall_size.SetHeight(original->overall_size.GetHeight());
|
||||
}
|
||||
|
||||
m_layouts.Add(layout);
|
||||
return true;
|
||||
}
|
||||
|
||||
void wxRibbonButtonBar::MakeBitmaps(wxRibbonButtonBarButtonBase* base,
|
||||
|
Reference in New Issue
Block a user