The original discussion is hereThe button is a wxStaticBitmap. I've added events to popup a menu when the bitmap is clicked and another event to change the bitmap when it is hovered.
The most tricky part is to generate the hovered and non hovered bitmap. To do this, I create a fake wxRibbonBar with the same wxRibbonArtProvider as the original ribbon, I add a tab called "File", and I ask the art provider to draw the tab in a wxMemoryDC. Finally, when the bitmap is rendered, I just put it on the wxStaticBitmap, which is put over the ribbon. Here is the complete code used to do this :You should be able to adapt it to any code base by renaming MainFrame and its members variables. If there is any problem, you can test if the bitmap generation is made correctly by saving the bitmaps to file ( wxBitmap::SaveFile ).Code: Select all
//In this code, the ribbon is a member of MainFrame called ribbon. //Rendered bitmap are also members of MainFrame, and are called ribbonFileNormalBitmap and ribbonFileHoveredBitmap //Finally, the wxStaticBitmap is called ribbonFileBt. void MainFrame::RealizeRibbonCustomButtons() { wxRibbonArtProvider * artProvider = ribbon->GetArtProvider(); if ( artProvider == NULL ) return; wxColor buttonColor; if ( !wxConfigBase::Get()->Read( _T( "/Skin/FileButtonColor" ), &buttonColor ) ) buttonColor = wxColour(200, 200, 255); //Create a temporary fake ribbon used to render the button with a custom color wxRibbonBar * fakeRibbon = new wxRibbonBar(this); fakeRibbon->SetArtProvider(artProvider->Clone()); fakeRibbon->GetArtProvider()->SetColourScheme(buttonColor, buttonColor, buttonColor); //The device context used to render the button in memory wxMemoryDC dc; //Compute width of the bitmap button int width; artProvider->GetBarTabWidth(dc, fakeRibbon, _("File"), wxNullBitmap, &width, NULL, NULL, NULL); //Create a fake ribbon page... wxRibbonPage *page = new wxRibbonPage(fakeRibbon, wxID_ANY, _("File")); //...and the associated wxRibbonPageTabInfo wxRibbonPageTabInfo tabInfo; tabInfo.rect = wxRect(0,0, width, 16 /*Will be changed later*/); tabInfo.ideal_width = width; tabInfo.small_begin_need_separator_width = width; tabInfo.small_must_have_separator_width = width; tabInfo.minimum_width = width; tabInfo.page = page; tabInfo.active = true; tabInfo.hovered = false; wxRibbonPageTabInfoArray pages; pages.Add(tabInfo); pages.Add(tabInfo); //Add page twice to ensure that tab have a correct height //Compute height of the bitmap button and create bitmap int height = artProvider->GetTabCtrlHeight(dc, ribbon, pages); wxBitmap bitmapLabel(width+2, height); dc.SelectObject(bitmapLabel); tabInfo.rect = wxRect(0,0, width, height+2); //We've got the correct height now. //Render the file button. Use the background of the real ribbon. artProvider->DrawTabCtrlBackground(dc, fakeRibbon, bitmapLabel.GetSize()); fakeRibbon->GetArtProvider()->DrawTab(dc, fakeRibbon, tabInfo); ribbonFileNormalBitmap = wxBitmap(bitmapLabel); //Render the hovered file button wxBitmap bitmapHoveredLabel(ribbonFileNormalBitmap.ConvertToImage()); dc.SelectObject(bitmapHoveredLabel); tabInfo.active = false; tabInfo.hovered = true; artProvider->DrawTabCtrlBackground(dc, fakeRibbon, bitmapHoveredLabel.GetSize()); wxColour backgroundColour = wxColor(bitmapHoveredLabel.ConvertToImage().GetRed(0,0), bitmapHoveredLabel.ConvertToImage().GetGreen(0,0), bitmapHoveredLabel.ConvertToImage().GetBlue(0,0)); //For later use... fakeRibbon->GetArtProvider()->DrawTab(dc, fakeRibbon, tabInfo); ribbonFileHoveredBitmap = bitmapHoveredLabel; //Cut a bit the bottom of the bitmaps if ( ribbonFileNormalBitmap.GetSize().GetHeight() > 3 ) ribbonFileNormalBitmap.SetHeight(ribbonFileNormalBitmap.GetSize().GetHeight()-2); if ( ribbonFileHoveredBitmap.GetSize().GetHeight() > 3 ) ribbonFileHoveredBitmap.SetHeight(ribbonFileHoveredBitmap.GetSize().GetHeight()-2); fakeRibbon->Destroy(); //Finally create our bitmaps and make sure the ribbon is ready. ribbonFileBt->SetPosition(wxPoint(3,1)); ribbonFileBt->SetBitmap(ribbonFileNormalBitmap); ribbon->SetTabCtrlMargins(bitmapLabel.GetSize().GetWidth()+3+3, 0); }
wxRibbonBar File Menu Topic is solved
wxRibbonBar File Menu
In case anyone is wondering how to implement a "File" menu at the left side of a wxRibbonBar, the author of GameDevelop has a good solution.
wxWidgets 3.1.2, MinGW64 8.1.0, g++ 8.1.0, Ubuntu 19.04, Windows 10, CodeLite + wxCrafter
Some people, when confronted with a GUI problem, think "I know, I'll use Eclipse RCP". Now they have two problems.
Some people, when confronted with a GUI problem, think "I know, I'll use Eclipse RCP". Now they have two problems.
Re: wxRibbonBar File Menu
Nice codes! I did slight modification on it.
I didn't use wxStaticBitmap and I used wxBitmapButton, because I always got crash at this line: ribbonFileBt->SetBitmap(ribbonFileNormalBitmap);
Code: Select all
void MainFrame::RealizeRibbonCustomButtons(wxRibbonBar * pParentRibbon, const char * Label) {
//@@preconditions
assert(pParentRibbon != NULL);
assert(Label != NULL && strlen(Label) > 0);
//@@end preconditions
wxRibbonArtProvider * artProvider = pParentRibbon->GetArtProvider();
if (artProvider == NULL) return;
wxColor bgNormalColor = wxColour(12, 97, 154); //blue
//Create a temporary fake wxRibbon used to render the button with a custom color
wxRibbonBar * fakeRibbon = new wxRibbonBar(this);
fakeRibbon->SetArtProvider(artProvider->Clone());
fakeRibbon->GetArtProvider()->SetColourScheme(bgNormalColor, bgNormalColor, bgNormalColor);
fakeRibbon->GetArtProvider()->SetColour(wxRIBBON_ART_TAB_LABEL_COLOUR, wxColour(255, 255, 255)/*white*/);
//The device context used to render the button in memory
wxMemoryDC dc;
//Compute width of the bitmap button
int width = 0, exWidth = 15;
artProvider->GetBarTabWidth(dc, fakeRibbon, wxT(wxString::FromUTF8(_(Label))), wxNullBitmap, &width, NULL, NULL, NULL);
width += exWidth; //more wider
//Create a fake wxRibbon page...
wxRibbonPage *page = new wxRibbonPage(fakeRibbon, wxID_ANY, wxT(wxString::FromUTF8(_(Label))));
//...and the associated wxRibbonPageTabInfo
wxRibbonPageTabInfo tabInfo;
tabInfo.rect = wxRect(0, 0, width, 16 /*Will be changed later*/);
tabInfo.ideal_width = width;
tabInfo.small_begin_need_separator_width = width;
tabInfo.small_must_have_separator_width = width;
tabInfo.minimum_width = width;
tabInfo.page = page;
tabInfo.active = true;
tabInfo.hovered = false;
wxRibbonPageTabInfoArray pages;
pages.Add(tabInfo);
pages.Add(tabInfo); //Add page twice to ensure that tab have a correct height
//Compute height of the bitmap button and create bitmap
int height = artProvider->GetTabCtrlHeight(dc, pParentRibbon, pages);
wxBitmap bitmapLabel(width + 2, height);
dc.SelectObject(bitmapLabel);
tabInfo.rect = wxRect(0, 0, width, height + 2); //We've got the correct height now.
//Render the normal button. Use the background of the real wxRibbon.
artProvider->DrawTabCtrlBackground(dc, fakeRibbon, bitmapLabel.GetSize());
fakeRibbon->GetArtProvider()->DrawTab(dc, fakeRibbon, tabInfo); assert(bitmapLabel.IsOk());
wxSize bmSize = bitmapLabel.GetSize();
wxBitmap RibbonxNormalBitmap_File = wxBitmap(bitmapLabel);
assert(RibbonxNormalBitmap_File.IsOk());
//Render the hovered button
wxBitmap bitmapHoveredLabel(RibbonxNormalBitmap_File.ConvertToImage());
dc.SelectObject(bitmapHoveredLabel);
wxColor bgHoverColor = wxColor(41, 140, 225); //light blue
fakeRibbon->GetArtProvider()->SetColour(wxRIBBON_ART_TAB_HOVER_BACKGROUND_COLOUR, bgHoverColor);
fakeRibbon->GetArtProvider()->SetColour(wxRIBBON_ART_TAB_HOVER_BACKGROUND_GRADIENT_COLOUR, bgHoverColor);
fakeRibbon->GetArtProvider()->SetColour(wxRIBBON_ART_TAB_HOVER_BACKGROUND_TOP_COLOUR, bgHoverColor);
fakeRibbon->GetArtProvider()->SetColour(wxRIBBON_ART_TAB_HOVER_BACKGROUND_TOP_GRADIENT_COLOUR, bgHoverColor);
tabInfo.active = false;
tabInfo.hovered = true;
artProvider->DrawTabCtrlBackground(dc, fakeRibbon, bitmapHoveredLabel.GetSize());
wxColour backgroundColour = wxColor(bitmapHoveredLabel.ConvertToImage().GetRed(0, 0), bitmapHoveredLabel.ConvertToImage().GetGreen(0, 0), bitmapHoveredLabel.ConvertToImage().GetBlue(0, 0)); //For later use...
fakeRibbon->GetArtProvider()->DrawTab(dc, fakeRibbon, tabInfo);
wxBitmap RibbonxHoveredBitmap_File = bitmapHoveredLabel;
//Cut a bit the bottom of the bitmaps
bmSize = RibbonxNormalBitmap_File.GetSize();
if (bmSize.GetHeight() > 3) {
RibbonxNormalBitmap_File.SetHeight(bmSize.GetHeight() - 2);
}
assert(RibbonxNormalBitmap_File.IsOk());
bmSize = RibbonxHoveredBitmap_File.GetSize();
if (bmSize.GetHeight() > 3) {
RibbonxHoveredBitmap_File.SetHeight(bmSize.GetHeight() - 2);
}
assert(RibbonxHoveredBitmap_File.IsOk());
fakeRibbon->Destroy();
int xOff = 2;
wxPoint wdgOriginalPt(0 + xOff, 0);
wxSize wdgOriginalSz(bitmapLabel.GetSize().GetWidth(), bitmapLabel.GetSize().GetHeight());
wxBitmapButton * csbAppMenuButton = new wxBitmapButton(pParentRibbon, wxID_ANY, RibbonxNormalBitmap_File, wdgOriginalPt, wdgOriginalSz, wxBU_ALIGN_MASK | wxBU_AUTODRAW | wxNO_BORDER);
csbAppMenuButton ->SetBitmapHover(RibbonxHoveredBitmap_File);
csbAppMenuButton ->SetBitmapPressed(RibbonxHoveredBitmap_File);
wxRect csbRect;
csbRect.SetX(wdgOriginalPt.x);
csbRect.SetY(wdgOriginalPt.y);
csbRect.SetWidth(wdgOriginalSz.GetWidth());
csbRect.SetHeight(wdgOriginalSz.GetHeight());
//reset TabCtrl margins of passed-in ribbon.
int tabGap = 2;
pParentRibbon->SetTabCtrlMargins(csbRect.GetWidth()+xOff+tabGap, 0);
}