Files
wxWidgets/src/ribbon/toolbar.cpp
T
Blake-Madden 8b080b8cb0 Add system color change responsiveness to ribbon
Add new wxRibbonArtProvider::UpdateColoursFromSystem() and implement it
in wxRibbonMSWArtProvider.

Closes #26644.
2026-07-01 03:19:51 +02:00

1293 lines
38 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/ribbon/toolbar.cpp
// Purpose: Ribbon-style tool bar
// Author: Peter Cawley
// Created: 2009-07-06
// Copyright: (C) Peter Cawley
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#if wxUSE_RIBBON
#include "wx/ribbon/toolbar.h"
#include "wx/ribbon/art.h"
#include "wx/ribbon/bar.h"
#include "wx/dcbuffer.h"
#ifndef WX_PRECOMP
#endif
#ifdef __WXMSW__
#include "wx/msw/private.h"
#endif
#include <memory>
class wxRibbonToolBarToolBase
{
public:
wxString help_string;
wxBitmapBundle bitmap;
wxBitmapBundle bitmap_disabled;
wxRect dropdown;
wxPoint position;
wxSize size;
wxObject* client_data = nullptr;
int id;
wxRibbonButtonKind kind;
long state;
};
WX_DEFINE_ARRAY_PTR(wxRibbonToolBarToolBase*, wxArrayRibbonToolBarToolBase);
class wxRibbonToolBarToolGroup
{
public:
// To identify the group as a wxRibbonToolBarToolBase*
wxRibbonToolBarToolBase dummy_tool;
wxArrayRibbonToolBarToolBase tools;
wxPoint position;
wxSize size;
};
wxDEFINE_EVENT(wxEVT_RIBBONTOOLBAR_CLICKED, wxRibbonToolBarEvent);
wxDEFINE_EVENT(wxEVT_RIBBONTOOLBAR_DROPDOWN_CLICKED, wxRibbonToolBarEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxRibbonToolBarEvent, wxCommandEvent);
wxIMPLEMENT_CLASS(wxRibbonToolBar, wxRibbonControl);
wxBEGIN_EVENT_TABLE(wxRibbonToolBar, wxRibbonControl)
EVT_ENTER_WINDOW(wxRibbonToolBar::OnMouseEnter)
EVT_ERASE_BACKGROUND(wxRibbonToolBar::OnEraseBackground)
EVT_LEAVE_WINDOW(wxRibbonToolBar::OnMouseLeave)
EVT_LEFT_DOWN(wxRibbonToolBar::OnMouseDown)
EVT_LEFT_DCLICK(wxRibbonToolBar::OnMouseDown)
EVT_LEFT_UP(wxRibbonToolBar::OnMouseUp)
EVT_MOTION(wxRibbonToolBar::OnMouseMove)
EVT_PAINT(wxRibbonToolBar::OnPaint)
EVT_SIZE(wxRibbonToolBar::OnSize)
EVT_DPI_CHANGED(wxRibbonToolBar::OnDPIChanged)
EVT_SYS_COLOUR_CHANGED(wxRibbonToolBar::OnSysColourChanged)
wxEND_EVENT_TABLE()
wxRibbonToolBar::wxRibbonToolBar()
{
}
wxRibbonToolBar::wxRibbonToolBar(wxWindow* parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style)
: wxRibbonControl(parent, id, pos, size, wxBORDER_NONE)
{
CommonInit(style);
}
bool wxRibbonToolBar::Create(wxWindow* parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style)
{
if(!wxRibbonControl::Create(parent, id, pos, size, wxBORDER_NONE))
{
return false;
}
CommonInit(style);
return true;
}
void wxRibbonToolBar::CommonInit(long WXUNUSED(style))
{
AppendGroup();
m_hover_tool = nullptr;
m_active_tool = nullptr;
m_nrows_min = 1;
m_nrows_max = 1;
m_sizes = new wxSize[1];
SetBackgroundStyle(wxBG_STYLE_PAINT);
}
wxRibbonToolBar::~wxRibbonToolBar()
{
size_t count = m_groups.GetCount();
size_t i, t;
for(i = 0; i < count; ++i)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(i);
size_t tool_count = group->tools.GetCount();
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
delete tool;
}
delete group;
}
m_groups.Clear();
delete[] m_sizes;
}
wxRibbonToolBarToolBase* wxRibbonToolBar::AddTool(
int tool_id,
const wxBitmapBundle& bitmap,
const wxString& help_string,
wxRibbonButtonKind kind)
{
return AddTool(tool_id, bitmap, wxBitmapBundle(), help_string, kind, nullptr);
}
wxRibbonToolBarToolBase* wxRibbonToolBar::AddDropdownTool(
int tool_id,
const wxBitmapBundle& bitmap,
const wxString& help_string)
{
return AddTool(tool_id, bitmap, wxBitmapBundle(), help_string,
wxRIBBON_BUTTON_DROPDOWN, nullptr);
}
wxRibbonToolBarToolBase* wxRibbonToolBar::AddHybridTool(
int tool_id,
const wxBitmapBundle& bitmap,
const wxString& help_string)
{
return AddTool(tool_id, bitmap, wxBitmapBundle(), help_string,
wxRIBBON_BUTTON_HYBRID, nullptr);
}
wxRibbonToolBarToolBase* wxRibbonToolBar::AddToggleTool(
int tool_id,
const wxBitmapBundle& bitmap,
const wxString& help_string)
{
return AddTool(tool_id, bitmap, wxBitmapBundle(), help_string,
wxRIBBON_BUTTON_TOGGLE, nullptr);
}
wxRibbonToolBarToolBase* wxRibbonToolBar::AddTool(
int tool_id,
const wxBitmapBundle& bitmap,
const wxBitmapBundle& bitmap_disabled,
const wxString& help_string,
wxRibbonButtonKind kind,
wxObject* client_data)
{
return InsertTool(GetToolCount(), tool_id, bitmap, bitmap_disabled,
help_string, kind, client_data);
}
wxRibbonToolBarToolBase* wxRibbonToolBar::AddSeparator()
{
if(m_groups.Last()->tools.IsEmpty())
return nullptr;
AppendGroup();
return &m_groups.Last()->dummy_tool;
}
wxRibbonToolBarToolBase* wxRibbonToolBar::InsertTool(
size_t pos,
int tool_id,
const wxBitmapBundle& bitmap,
const wxString& help_string,
wxRibbonButtonKind kind)
{
return InsertTool(pos, tool_id, bitmap, wxBitmapBundle(), help_string, kind,
nullptr);
}
wxRibbonToolBarToolBase* wxRibbonToolBar::InsertDropdownTool(
size_t pos,
int tool_id,
const wxBitmapBundle& bitmap,
const wxString& help_string)
{
return InsertTool(pos, tool_id, bitmap, wxBitmapBundle(), help_string,
wxRIBBON_BUTTON_DROPDOWN, nullptr);
}
wxRibbonToolBarToolBase* wxRibbonToolBar::InsertHybridTool(
size_t pos,
int tool_id,
const wxBitmapBundle& bitmap,
const wxString& help_string)
{
return InsertTool(pos, tool_id, bitmap, wxBitmapBundle(), help_string,
wxRIBBON_BUTTON_HYBRID, nullptr);
}
wxRibbonToolBarToolBase* wxRibbonToolBar::InsertToggleTool(
size_t pos,
int tool_id,
const wxBitmapBundle& bitmap,
const wxString& help_string)
{
return InsertTool(pos, tool_id, bitmap, wxBitmapBundle(), help_string,
wxRIBBON_BUTTON_TOGGLE, nullptr);
}
wxRibbonToolBarToolBase* wxRibbonToolBar::InsertTool(
size_t pos,
int tool_id,
const wxBitmapBundle& bitmap,
const wxBitmapBundle& bitmap_disabled,
const wxString& help_string,
wxRibbonButtonKind kind,
wxObject* client_data)
{
wxASSERT(bitmap.IsOk());
// Create the wxRibbonToolBarToolBase with parameters
std::unique_ptr<wxRibbonToolBarToolBase> tool(new wxRibbonToolBarToolBase);
tool->id = tool_id;
tool->bitmap = bitmap;
if(bitmap_disabled.IsOk())
{
wxASSERT(bitmap.GetDefaultSize() == bitmap_disabled.GetDefaultSize());
tool->bitmap_disabled = bitmap_disabled;
}
else
{
// Generate disabled bitmap from normal bitmap
wxBitmap bmp = bitmap.GetBitmap(bitmap.GetDefaultSize());
tool->bitmap_disabled = wxBitmapBundle::FromBitmap(MakeDisabledBitmap(bmp));
}
tool->help_string = help_string;
tool->kind = kind;
tool->client_data = client_data;
tool->state = 0;
// Find the position where insert tool
size_t group_count = m_groups.GetCount();
size_t g;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
if(pos <= tool_count)
{
// Give the ownership of the tool to group->tools
wxRibbonToolBarToolBase* const p = tool.release();
group->tools.Insert(p, pos);
return p;
}
pos -= tool_count + 1;
}
wxFAIL_MSG("Tool position out of toolbar bounds.");
return nullptr;
}
wxRibbonToolBarToolBase* wxRibbonToolBar::InsertSeparator(size_t pos)
{
size_t group_count = m_groups.GetCount();
size_t g;
for(g = 0; g < group_count; ++g)
{
if(pos==0) // Prepend group
return &InsertGroup(g)->dummy_tool;
if(pos==group_count) // Append group
return &InsertGroup(g+1)->dummy_tool;
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
if(pos < tool_count)
{
wxRibbonToolBarToolGroup* new_group = InsertGroup(g+1);
for(size_t t = pos; t < tool_count; t++)
new_group->tools.Add(group->tools[t]);
group->tools.RemoveAt(pos, tool_count-pos);
return &group->dummy_tool;
}
pos -= tool_count + 1;
}
// Add an empty group at the end of the bar.
if(m_groups.Last()->tools.IsEmpty())
return nullptr;
AppendGroup();
return &m_groups.Last()->dummy_tool;
}
wxRibbonToolBarToolGroup* wxRibbonToolBar::InsertGroup(size_t pos)
{
wxRibbonToolBarToolGroup* group = new wxRibbonToolBarToolGroup;
m_groups.Insert(group, pos);
return group;
}
void wxRibbonToolBar::ClearTools()
{
size_t count = m_groups.GetCount();
size_t i, t;
for(i = 0; i < count; ++i)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(i);
size_t tool_count = group->tools.GetCount();
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
delete tool;
}
delete group;
}
m_groups.Clear();
}
bool wxRibbonToolBar::DeleteTool(int tool_id)
{
size_t group_count = m_groups.GetCount();
size_t g, t;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
if(tool->id == tool_id)
{
group->tools.RemoveAt(t);
delete tool;
return true;
}
}
}
return false;
}
bool wxRibbonToolBar::DeleteToolByPos(size_t pos)
{
size_t group_count = m_groups.GetCount();
size_t g, t;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
if(pos<tool_count)
{
// Remove tool
wxRibbonToolBarToolBase* tool = group->tools.Item(pos);
group->tools.RemoveAt(pos);
delete tool;
return true;
}
else if(pos==tool_count)
{
// Remove separator
if(g < group_count - 1)
{
wxRibbonToolBarToolGroup* next_group = m_groups.Item(g+1);
for(t = 0; t < next_group->tools.GetCount(); ++t)
group->tools.Add(next_group->tools[t]);
m_groups.RemoveAt(g+1);
delete next_group;
}
return true;
}
pos -= tool_count+1;
}
return false;
}
wxRibbonToolBarToolBase* wxRibbonToolBar::FindById(int tool_id)const
{
size_t group_count = m_groups.GetCount();
size_t g, t;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
if(tool->id == tool_id)
{
return tool;
}
}
}
return nullptr;
}
wxRibbonToolBarToolBase* wxRibbonToolBar::GetToolByPos(size_t pos)const
{
size_t group_count = m_groups.GetCount();
size_t g;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
if(pos<tool_count)
{
return group->tools[pos];
}
else if(pos==tool_count)
{
return nullptr;
}
pos -= tool_count+1;
}
return nullptr;
}
wxRibbonToolBarToolBase* wxRibbonToolBar::GetToolByPos(wxCoord x, wxCoord y)const
{
size_t group_count = m_groups.GetCount();
for ( size_t g = 0; g < group_count; ++g )
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
for ( size_t t = 0; t < tool_count; ++t )
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
wxRect rect(group->position + tool->position, tool->size);
if(rect.Contains(x, y))
{
return tool;
}
}
}
return nullptr;
}
size_t wxRibbonToolBar::GetToolCount() const
{
size_t count = 0;
for(size_t g = 0; g < m_groups.GetCount(); ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
count += group->tools.GetCount();
}
// There is a splitter in front of every group except for the first
// If only one group, no separator.
if(m_groups.GetCount()>1)
count += m_groups.GetCount() - 1;
return count;
}
int wxRibbonToolBar::GetToolId(const wxRibbonToolBarToolBase* tool)const
{
wxCHECK_MSG(tool != nullptr , wxNOT_FOUND, "The tool pointer must not be null");
return tool->id;
}
wxRibbonToolBarToolBase* wxRibbonToolBar::GetActiveTool() const
{
return m_active_tool == nullptr ? nullptr : m_active_tool;
}
wxObject* wxRibbonToolBar::GetToolClientData(int tool_id)const
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_MSG(tool != nullptr , nullptr, "Invalid tool id");
return tool->client_data;
}
bool wxRibbonToolBar::GetToolEnabled(int tool_id)const
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_MSG(tool != nullptr , false, "Invalid tool id");
return (tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED) == 0;
}
wxString wxRibbonToolBar::GetToolHelpString(int tool_id)const
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_MSG(tool != nullptr , wxEmptyString, "Invalid tool id");
return tool->help_string;
}
wxRibbonButtonKind wxRibbonToolBar::GetToolKind(int tool_id)const
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_MSG(tool != nullptr , wxRIBBON_BUTTON_NORMAL, "Invalid tool id");
return tool->kind;
}
int wxRibbonToolBar::GetToolPos(int tool_id)const
{
size_t group_count = m_groups.GetCount();
size_t g, t;
int pos = 0;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
if(tool->id == tool_id)
{
return pos;
}
++pos;
}
++pos; // Increment pos for group separator.
}
return wxNOT_FOUND;
}
wxRect wxRibbonToolBar::GetToolRect(int tool_id)const
{
size_t group_count = m_groups.GetCount();
size_t g, t;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
if (tool->id == tool_id)
{
return wxRect(group->position + tool->position, tool->size);
}
}
}
return wxRect();
}
bool wxRibbonToolBar::GetToolState(int tool_id)const
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_MSG(tool != nullptr , false, "Invalid tool id");
return (tool->state & wxRIBBON_TOOLBAR_TOOL_TOGGLED) != 0;
}
wxBitmap wxRibbonToolBar::MakeDisabledBitmap(const wxBitmap& original)
{
wxImage img(original.ConvertToImage());
return wxBitmap(img.ConvertToGreyscale(), -1, original.GetScaleFactor());
}
void wxRibbonToolBar::AppendGroup()
{
wxRibbonToolBarToolGroup* group = new wxRibbonToolBarToolGroup;
m_groups.Add(group);
}
bool wxRibbonToolBar::IsSizingContinuous() const
{
return false;
}
void wxRibbonToolBar::SetToolClientData(int tool_id, wxObject* clientData)
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_RET(tool != nullptr , "Invalid tool id");
tool->client_data = clientData;
}
void wxRibbonToolBar::SetToolDisabledBitmap(int tool_id, const wxBitmapBundle &bitmap)
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_RET(tool != nullptr , "Invalid tool id");
tool->bitmap_disabled = bitmap;
}
void wxRibbonToolBar::SetToolHelpString(int tool_id, const wxString& helpString)
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_RET(tool != nullptr , "Invalid tool id");
tool->help_string = helpString;
}
void wxRibbonToolBar::SetToolNormalBitmap(int tool_id, const wxBitmapBundle &bitmap)
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_RET(tool != nullptr , "Invalid tool id");
tool->bitmap = bitmap;
}
void wxRibbonToolBar::EnableTool(int tool_id, bool enable)
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_RET(tool != nullptr , "Invalid tool id");
if(enable)
{
if(tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
{
tool->state &= ~wxRIBBON_TOOLBAR_TOOL_DISABLED;
Refresh();
}
}
else
{
if((tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)==0)
{
tool->state |= wxRIBBON_TOOLBAR_TOOL_DISABLED;
Refresh();
}
}
}
void wxRibbonToolBar::ToggleTool(int tool_id, bool checked)
{
wxRibbonToolBarToolBase* tool = FindById(tool_id);
wxCHECK_RET(tool != nullptr , "Invalid tool id");
if(checked)
{
if((tool->state & wxRIBBON_TOOLBAR_TOOL_TOGGLED) == 0)
{
tool->state |= wxRIBBON_TOOLBAR_TOOL_TOGGLED;
Refresh();
}
}
else
{
if(tool->state & wxRIBBON_TOOLBAR_TOOL_TOGGLED)
{
tool->state &= ~wxRIBBON_TOOLBAR_TOOL_TOGGLED;
Refresh();
}
}
}
static int GetSizeInOrientation(wxSize size, wxOrientation orientation)
{
switch(orientation)
{
case wxHORIZONTAL: return size.GetWidth();
case wxVERTICAL: return size.GetHeight();
case wxBOTH: return size.GetWidth() * size.GetHeight();
default: return 0;
}
}
wxSize wxRibbonToolBar::DoGetNextSmallerSize(wxOrientation direction,
wxSize relative_to) const
{
wxSize result(relative_to);
int area = 0;
int nrows;
for(nrows = m_nrows_min; nrows <= m_nrows_max; ++nrows)
{
wxSize size(m_sizes[nrows - m_nrows_min]);
wxSize original(size);
switch(direction)
{
case wxHORIZONTAL:
if(size.GetWidth() < relative_to.GetWidth()
&& size.GetHeight() <= relative_to.GetHeight())
{
size.SetHeight(relative_to.GetHeight());
break;
}
continue;
case wxVERTICAL:
if(size.GetWidth() <= relative_to.GetWidth()
&& size.GetHeight() < relative_to.GetHeight())
{
size.SetWidth(relative_to.GetWidth());
break;
}
continue;
case wxBOTH:
if(size.GetWidth() < relative_to.GetWidth()
&& size.GetHeight() < relative_to.GetHeight())
{
break;
}
continue;
}
if(GetSizeInOrientation(original, direction) > area)
{
result = size;
area = GetSizeInOrientation(original, direction);
}
}
return result;
}
wxSize wxRibbonToolBar::DoGetNextLargerSize(wxOrientation direction,
wxSize relative_to) const
{
// Pick the smallest of our sizes which are larger than the given size
wxSize result(relative_to);
int area = INT_MAX;
int nrows;
for(nrows = m_nrows_min; nrows <= m_nrows_max; ++nrows)
{
wxSize size(m_sizes[nrows - m_nrows_min]);
wxSize original(size);
switch(direction)
{
case wxHORIZONTAL:
if(size.GetWidth() > relative_to.GetWidth()
&& size.GetHeight() <= relative_to.GetHeight())
{
size.SetHeight(relative_to.GetHeight());
break;
}
continue;
case wxVERTICAL:
if(size.GetWidth() <= relative_to.GetWidth()
&& size.GetHeight() > relative_to.GetHeight())
{
size.SetWidth(relative_to.GetWidth());
break;
}
continue;
case wxBOTH:
if(size.GetWidth() > relative_to.GetWidth()
&& size.GetHeight() > relative_to.GetHeight())
{
break;
}
continue;
}
if(GetSizeInOrientation(original, direction) < area)
{
result = size;
area = GetSizeInOrientation(original, direction);
}
}
return result;
}
void wxRibbonToolBar::SetRows(int nMin, int nMax)
{
if(nMax == -1)
nMax = nMin;
wxASSERT(1 <= nMin);
wxASSERT(nMin <= nMax);
m_nrows_min = nMin;
m_nrows_max = nMax;
delete[] m_sizes;
m_sizes = new wxSize[m_nrows_max - m_nrows_min + 1];
Realize();
}
bool wxRibbonToolBar::Realize()
{
if(m_art == nullptr)
return false;
// Calculate the size of each group and the position/size of each tool
wxMemoryDC temp_dc;
size_t group_count = m_groups.GetCount();
size_t g, t;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolBase* prev = nullptr;
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
int tallest = 0;
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
tool->size = m_art->GetToolSize(temp_dc, this,
tool->bitmap.GetPreferredLogicalSizeFor(this), tool->kind, t == 0,
t == (tool_count - 1), &tool->dropdown);
if(t == 0)
tool->state |= wxRIBBON_TOOLBAR_TOOL_FIRST;
if(t == tool_count - 1)
tool->state |= wxRIBBON_TOOLBAR_TOOL_LAST;
if(tool->size.GetHeight() > tallest)
tallest = tool->size.GetHeight();
if(prev)
{
tool->position = prev->position;
tool->position.x += prev->size.x;
}
else
{
tool->position = wxPoint(0, 0);
}
prev = tool;
}
if(tool_count == 0)
group->size = wxSize(0, 0);
else
{
group->size = wxSize(prev->position.x + prev->size.x, tallest);
for(t = 0; t < tool_count; ++t)
group->tools.Item(t)->size.SetHeight(tallest);
}
}
// Calculate the minimum size for each possible number of rows
int nrows, r;
int sep = m_art->GetMetric(wxRIBBON_ART_TOOL_GROUP_SEPARATION_SIZE);
int smallest_area = INT_MAX;
wxSize* row_sizes = new wxSize[m_nrows_max];
wxOrientation major_axis = m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL ?
wxVERTICAL : wxHORIZONTAL;
SetMinSize(wxSize(0, 0));
wxSize minSize(INT_MAX, INT_MAX);
// See if we're sizing flexibly (i.e. wrapping), and set min size differently
bool sizingFlexibly = false;
wxRibbonPanel* panel = wxDynamicCast(GetParent(), wxRibbonPanel);
if (panel && (panel->GetFlags() & wxRIBBON_PANEL_FLEXIBLE))
sizingFlexibly = true;
// Without this, there will be redundant horizontal space because SetMinSize will
// use the smallest possible height (and therefore largest width).
if (sizingFlexibly)
major_axis = wxHORIZONTAL;
for(nrows = m_nrows_min; nrows <= m_nrows_max; ++nrows)
{
for(r = 0; r < nrows; ++r)
row_sizes[r] = wxSize(0, 0);
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
int shortest_row = 0;
for(r = 1; r < nrows; ++r)
{
if(row_sizes[r].GetWidth() < row_sizes[shortest_row].GetWidth())
shortest_row = r;
}
row_sizes[shortest_row].x += group->size.x + sep;
if(group->size.y > row_sizes[shortest_row].y)
row_sizes[shortest_row].y = group->size.y;
}
wxSize size(0, 0);
for(r = 0; r < nrows; ++r)
{
if(row_sizes[r].GetWidth() != 0)
row_sizes[r].DecBy(sep, 0);
if(row_sizes[r].GetWidth() > size.GetWidth())
size.SetWidth(row_sizes[r].GetWidth());
size.IncBy(0, row_sizes[r].y);
}
m_sizes[nrows - m_nrows_min] = size;
if(GetSizeInOrientation(size, major_axis) < smallest_area)
{
smallest_area = GetSizeInOrientation(size, major_axis);
SetMinSize(size);
}
if (sizingFlexibly)
{
if (size.x < minSize.x)
minSize.x = size.x;
if (size.y < minSize.y)
minSize.y = size.y;
}
}
if (sizingFlexibly)
{
// Give it the min size in either direction regardless of row,
// so that we're able to vary the size of the panel according to
// the space the toolbar takes up.
SetMinSize(minSize);
}
delete[] row_sizes;
// Position the groups
wxSizeEvent dummy_event(GetSize());
OnSize(dummy_event);
return true;
}
void wxRibbonToolBar::OnSize(wxSizeEvent& evt)
{
if(m_art == nullptr)
return;
// Choose row count with largest possible area
wxSize size = evt.GetSize();
int row_count = m_nrows_max;
wxOrientation major_axis = m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL ?
wxVERTICAL : wxHORIZONTAL;
// See if we're sizing flexibly, and set min size differently
bool sizingFlexibly = false;
wxRibbonPanel* panel = wxDynamicCast(GetParent(), wxRibbonPanel);
if (panel && (panel->GetFlags() & wxRIBBON_PANEL_FLEXIBLE))
sizingFlexibly = true;
// Without this, there will be redundant horizontal space because SetMinSize will
// use the smallest possible height (and therefore largest width).
if (sizingFlexibly)
major_axis = wxHORIZONTAL;
if(m_nrows_max != m_nrows_min)
{
int area = 0;
for(int i = 0; i <= m_nrows_max - m_nrows_min; ++i)
{
if(m_sizes[i].x <= size.x && m_sizes[i].y <= size.y &&
GetSizeInOrientation(m_sizes[i], major_axis) > area)
{
area = GetSizeInOrientation(m_sizes[i], major_axis);
row_count = m_nrows_min + i;
}
}
}
// Assign groups to rows and calculate row widths
wxSize* row_sizes = new wxSize[row_count];
int sep = m_art->GetMetric(wxRIBBON_ART_TOOL_GROUP_SEPARATION_SIZE);
int r;
size_t g;
size_t group_count = m_groups.GetCount();
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
int shortest_row = 0;
for(r = 1; r < row_count; ++r)
{
if(row_sizes[r].GetWidth() < row_sizes[shortest_row].GetWidth())
shortest_row = r;
}
group->position = wxPoint(row_sizes[shortest_row].x, shortest_row);
row_sizes[shortest_row].x += group->size.x + sep;
if(group->size.y > row_sizes[shortest_row].y)
row_sizes[shortest_row].y = group->size.y;
}
// Calculate row positions
int total_height = 0;
for(r = 0; r < row_count; ++r)
total_height += row_sizes[r].GetHeight();
int rowsep = (size.GetHeight() - total_height) / (row_count + 1);
int* rowypos = new int[row_count];
rowypos[0] = rowsep;
for(r = 1; r < row_count; ++r)
{
rowypos[r] = rowypos[r - 1] + row_sizes[r - 1].GetHeight() + rowsep;
}
// Set group y positions
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
group->position.y = rowypos[group->position.y];
}
delete[] rowypos;
delete[] row_sizes;
}
void wxRibbonToolBar::OnDPIChanged(wxDPIChangedEvent& event)
{
Realize();
event.Skip();
}
void wxRibbonToolBar::OnSysColourChanged(wxSysColourChangedEvent& event)
{
event.Skip();
m_art->UpdateColoursFromSystem();
}
// Finds the best width and height given the parents' width and height
wxSize wxRibbonToolBar::GetBestSizeForParentSize(const wxSize& parentSize) const
{
if (!m_sizes)
return GetMinSize();
// Choose row count with largest possible area
wxSize size = parentSize;
// A toolbar should maximize its width whether vertical or horizontal, so
// force the major axis to be horizontal. Without this, there will be
// redundant horizontal space.
wxOrientation major_axis = wxHORIZONTAL;
wxSize bestSize = m_sizes[0];
if(m_nrows_max != m_nrows_min)
{
int area = 0;
for(int i = 0; i <= m_nrows_max - m_nrows_min; ++i)
{
if(m_sizes[i].x <= size.x && m_sizes[i].y <= size.y &&
GetSizeInOrientation(m_sizes[i], major_axis) > area)
{
area = GetSizeInOrientation(m_sizes[i], major_axis);
bestSize = m_sizes[i];
}
}
}
return bestSize;
}
wxSize wxRibbonToolBar::DoGetBestSize() const
{
return GetMinSize();
}
void wxRibbonToolBar::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
{
// All painting done in main paint handler to minimise flicker
}
void wxRibbonToolBar::OnPaint(wxPaintEvent& WXUNUSED(evt))
{
wxAutoBufferedPaintDC dc(this);
if(m_art == nullptr)
return;
m_art->DrawToolBarBackground(dc, this, GetSize());
size_t group_count = m_groups.GetCount();
size_t g, t;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
if(tool_count != 0)
{
m_art->DrawToolGroupBackground(dc, this,
wxRect(group->position, group->size));
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
wxRect rect(group->position + tool->position, tool->size);
// Resolve bitmap bundle to actual bitmap for current DPI
wxBitmap bmp;
if(tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
bmp = tool->bitmap_disabled.GetBitmapFor(this);
else
bmp = tool->bitmap.GetBitmapFor(this);
m_art->DrawTool(dc, this, rect, bmp, tool->kind, tool->state);
}
}
}
}
void wxRibbonToolBar::OnMouseMove(wxMouseEvent& evt)
{
wxPoint pos(evt.GetPosition());
wxRibbonToolBarToolBase *new_hover = nullptr;
size_t group_count = m_groups.GetCount();
size_t g, t;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
if(group->position.x <= pos.x && pos.x < group->position.x + group->size.x
&& group->position.y <= pos.y && pos.y < group->position.y + group->size.y)
{
size_t tool_count = group->tools.GetCount();
pos -= group->position;
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
if(tool->position.x <= pos.x && pos.x < tool->position.x + tool->size.x
&& tool->position.y <= pos.y && pos.y < tool->position.y + tool->size.y)
{
pos -= tool->position;
new_hover = tool;
break;
}
}
break;
}
}
#if wxUSE_TOOLTIPS
if(new_hover)
{
if (new_hover != m_hover_tool &&
!(new_hover->state & wxRIBBON_TOOLBAR_TOOL_DROPDOWN_ACTIVE))
SetToolTip(new_hover->help_string);
}
else if(GetToolTip())
{
UnsetToolTip();
}
#endif
if(new_hover != m_hover_tool)
{
if(m_hover_tool)
{
m_hover_tool->state &= ~(wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
| wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK);
}
m_hover_tool = new_hover;
if(new_hover && new_hover->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
new_hover = nullptr; // A disabled tool can not be highlighted
if(new_hover)
{
long what = wxRIBBON_TOOLBAR_TOOL_NORMAL_HOVERED;
if(new_hover->dropdown.Contains(pos))
what = wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED;
new_hover->state |= what;
if(new_hover == m_active_tool)
{
new_hover->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
new_hover->state |= (what << 2);
}
}
Refresh(false);
}
else if(m_hover_tool && m_hover_tool->kind == wxRIBBON_BUTTON_HYBRID)
{
long newstate = m_hover_tool->state &~wxRIBBON_TOOLBAR_TOOL_HOVER_MASK;
long what = wxRIBBON_TOOLBAR_TOOL_NORMAL_HOVERED;
if(m_hover_tool->dropdown.Contains(pos))
what = wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED;
newstate |= what;
if(newstate != m_hover_tool->state)
{
m_hover_tool->state = newstate;
if(m_hover_tool == m_active_tool)
{
m_hover_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
m_hover_tool->state |= (what << 2);
}
Refresh(false);
}
}
}
void wxRibbonToolBar::OnMouseDown(wxMouseEvent& evt)
{
OnMouseMove(evt);
if(m_hover_tool)
{
m_active_tool = m_hover_tool;
m_active_tool->state |=
(m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_HOVER_MASK) << 2;
UnsetToolTip();
Refresh(false);
}
}
void wxRibbonToolBar::OnMouseLeave(wxMouseEvent& WXUNUSED(evt))
{
if(m_hover_tool)
{
m_hover_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_HOVER_MASK;
m_hover_tool = nullptr;
Refresh(false);
}
}
void wxRibbonToolBar::OnMouseUp(wxMouseEvent& WXUNUSED(evt))
{
if(m_active_tool)
{
if(m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK)
{
wxEventType evt_type = wxEVT_RIBBONTOOLBAR_CLICKED;
if(m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_DROPDOWN_ACTIVE)
evt_type = wxEVT_RIBBONTOOLBAR_DROPDOWN_CLICKED;
wxRibbonToolBarEvent notification(evt_type, m_active_tool->id);
if(m_active_tool->kind == wxRIBBON_BUTTON_TOGGLE)
{
m_active_tool->state ^=
wxRIBBON_TOOLBAR_TOOL_TOGGLED;
notification.SetInt(m_active_tool->state &
wxRIBBON_TOOLBAR_TOOL_TOGGLED);
}
notification.SetEventObject(this);
notification.SetBar(this);
ProcessEvent(notification);
if (auto* const panel = wxCheckedStaticCast<wxRibbonPanel>(GetParent()))
panel->HideIfExpanded();
}
// Notice that m_active_tool could have been reset by the event handler
// above so we need to test it again.
if (m_active_tool)
{
m_active_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
m_active_tool = nullptr;
Refresh(false);
}
}
}
void wxRibbonToolBar::OnMouseEnter(wxMouseEvent& evt)
{
if(m_active_tool && !evt.LeftIsDown())
{
m_active_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
m_active_tool = nullptr;
}
}
void wxRibbonToolBar::UpdateWindowUI(long flags)
{
wxWindowBase::UpdateWindowUI(flags);
// don't waste time updating state of tools in a hidden toolbar
if ( !IsShown() )
return;
size_t group_count = m_groups.GetCount();
size_t g, t;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
int id = tool->id;
wxUpdateUIEvent event(id);
event.SetEventObject(this);
if ( ProcessWindowEvent(event) )
{
if ( event.GetSetEnabled() )
EnableTool(id, event.GetEnabled());
if ( event.GetSetChecked() )
ToggleTool(id, event.GetChecked());
}
}
}
}
bool wxRibbonToolBarEvent::PopupMenu(wxMenu* menu)
{
wxPoint pos = wxDefaultPosition;
if(m_bar->m_active_tool)
{
// Find the group which contains the tool
size_t group_count = m_bar->m_groups.GetCount();
size_t g, t;
for(g = 0; g < group_count; ++g)
{
wxRibbonToolBarToolGroup* group = m_bar->m_groups.Item(g);
size_t tool_count = group->tools.GetCount();
for(t = 0; t < tool_count; ++t)
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
if(tool == m_bar->m_active_tool)
{
pos = group->position;
pos += tool->position;
pos.y += tool->size.GetHeight();
g = group_count;
break;
}
}
}
}
return m_bar->PopupMenu(menu, pos);
}
#endif // wxUSE_RIBBON