2

I want to create control buttons (minimize, maximize and close) exaclty like Windows.

The final goal is to create something like the title bar of Microsoft Word.

enter image description here

I know how to create a wxButton and also I know how to set an icon for it. However I don't know how to use native OS icons or theme.

wxButton* closeButton = new wxButton(this, wxID_ANY, "x"); // how to tell that be like OS close button!

In WinAPI, there is a function called DrawThemeBackground which I can use with WP_CLOSEBUTTON but I don't know what's the equivalent in wxWidgets.

Update: With the help of all of you, this is a sample code for drawing native buttons in Windows (not work in other OS). Unfortunately, the result is not what I want. It looks like Win XP icons. It seems that wxNativeRenderer does not work properly. Does anybody have any idea to fix this code? (Yes, I have added "wx.rc" resource file and I don't use any manifest)

enter image description here

// wxWidgets "Hello World" Program
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/renderer.h>
#include <wx/artprov.h>
class MyApp: public wxApp
{
public:
  virtual bool OnInit();
};

class MyFrame: public wxFrame
{
public:
  MyFrame();
private:
};
wxIMPLEMENT_APP( MyApp );
bool MyApp::OnInit()
{
  MyFrame* frame = new MyFrame();
  frame->Show( true );
  return true;
}

wxBitmap getButtonBitmap( wxWindow* win, wxTitleBarButton type, const wxColour& bg, int flags = 0 )
{
  const wxSize sizeBmp = wxArtProvider::GetSizeHint( wxART_BUTTON );
  wxBitmap bmp( sizeBmp );
  wxMemoryDC dc( bmp );
  dc.SetBackground( bg );
  dc.Clear();
  wxRendererNative::Get().DrawTitleBarBitmap( win, dc, sizeBmp, type, flags );
  return bmp;
}

MyFrame::MyFrame()
  : wxFrame( NULL, wxID_ANY, "Hello World" )
{
  wxWindow* win = this;
  wxColour color = win->GetBackgroundColour();
  // minimize button
  wxBitmapButton* minimizeButton = new wxBitmapButton( win, wxID_ANY,
   getButtonBitmap( win, wxTITLEBAR_BUTTON_ICONIZE, color ),
   wxPoint( 0, 0 ), wxDefaultSize, wxBORDER_NONE );
  minimizeButton->SetBitmapPressed( getButtonBitmap( win, wxTITLEBAR_BUTTON_ICONIZE, color, wxCONTROL_PRESSED ) );
  minimizeButton->SetBitmapCurrent( getButtonBitmap( win, wxTITLEBAR_BUTTON_ICONIZE, color, wxCONTROL_CURRENT ) );
  // maximize button
  wxBitmapButton* maximizeButton = new wxBitmapButton( win, wxID_ANY,
   getButtonBitmap( win, wxTITLEBAR_BUTTON_MAXIMIZE, color ),
   wxPoint( 30, 0 ), wxDefaultSize, wxBORDER_NONE );
  maximizeButton->SetBitmapPressed( getButtonBitmap( win, wxTITLEBAR_BUTTON_MAXIMIZE, color, wxCONTROL_PRESSED ) );
  maximizeButton->SetBitmapCurrent( getButtonBitmap( win, wxTITLEBAR_BUTTON_MAXIMIZE, color, wxCONTROL_CURRENT ) );
  // close Button
  wxBitmapButton* closeButton = new wxBitmapButton( win, wxID_ANY,
   getButtonBitmap( win, wxTITLEBAR_BUTTON_CLOSE, color ),
   wxPoint( 60, 0 ), wxDefaultSize, wxBORDER_NONE );
  closeButton->SetBitmapPressed( getButtonBitmap( win, wxTITLEBAR_BUTTON_CLOSE, color, wxCONTROL_PRESSED ) );
  closeButton->SetBitmapCurrent( getButtonBitmap( win, wxTITLEBAR_BUTTON_CLOSE, color, wxCONTROL_CURRENT ) );
}
Reza
  • 3,473
  • 4
  • 35
  • 54
  • how did you crete your window? Is it to-level one? – Igor Oct 11 '21 at 12:33
  • Hi @Igor, no it's a normal `wxFrame`. I am trying to create a custom title bar. I removed "wxCAPTION" from frames's style and trying to put my own widgets plus native control buttons. Should I use a top level window instead of wxFrame? – Reza Oct 11 '21 at 13:00
  • why? What is the purpose of doing it like this? You still wantsd to have a `native` title bar buttons, so why noty have a full blown window? – Igor Oct 11 '21 at 14:46
  • @Igor As I mentioned, I want to create a custom title bar which could have widgets like menus and search box and so on. Something like Microsoft Word title bar. The native title bar does not provide something like that. – Reza Oct 11 '21 at 14:56
  • the users of your program will be really surprised to see the search control in the title bar, I, for one, will be. But YMMV. Also, the menu is not designed to be shown in the title bar - they usually shown right underneath. Take a look at the Notepad/Wordpad programs from Windows distribution. You will just confuse you customers/clients. Mporeover - how do you make all of them visible with the proper color scheme/theming? – Igor Oct 11 '21 at 19:33

2 Answers2

5

The process for drawing buttons like the ones on a title bar is a little more involved that simply using the DrawThemeBackground function. Here is a demo that partially shows how to do this using wxWidgets:

enter image description here

#include "wx/wx.h"

#include <wx/dcclient.h>
#include <wx/mstream.h>
#include <wx/dcmemory.h>
#include <wx/rawbmp.h>

#include <wx/msw/wrapwin.h>
#include <uxtheme.h>
#include <Vssym32.h>

#include <map>

// Helper data types
struct BGInfo
{
    wxRect BgRect;
    wxRect SizingMargins;
    wxRect ContentMargins;
    int    TotalStates;
};

struct ButtonInfo
{
    wxRect ButtonRect;
    int    TotalStates;
};

enum class DPI
{
    dpi96 = 0,
    dpi120,
    dpi144,
    dpi196
};

enum class Button
{
    Close = 0,
    Min,
    Max,
    Restore,
    Help
};

// Helper functions
void MarginsToRect(const MARGINS& m, wxRect& r)
{
    r.SetLeft(m.cxLeftWidth);
    r.SetRight(m.cxRightWidth);
    r.SetTop(m.cyTopHeight);
    r.SetBottom(m.cyBottomHeight);
}

void RectTowxRect(const RECT & r, wxRect& r2)
{
    r2.SetLeft(r.left);
    r2.SetTop(r.top);
    r2.SetRight(r.right-1);
    r2.SetBottom(r.bottom-1);
}

wxBitmap ExtractAtlas(const wxBitmap& atlas, int total, int loc)
{

    int bgheight = atlas.GetHeight();
    int individualHeight = bgheight/total;
    int bgWidth = atlas.GetWidth();
    int atlasOffset = individualHeight*loc;
    wxRect bgRect = wxRect(wxPoint(0,atlasOffset),
                           wxSize(bgWidth,individualHeight));
    return atlas.GetSubBitmap(bgRect);
}

void TileBitmap(const wxBitmap& bmp, wxDC& dc, const wxRect& r)
{
    dc.SetClippingRegion(r);

    for ( int y = 0 ; y < r.GetHeight() ; y += bmp.GetHeight() )
    {
        for ( int x = 0 ; x < r.GetWidth() ; x += bmp.GetWidth() )
        {
            dc.DrawBitmap(bmp, r.GetLeft() + x, r.GetTop() + y, true);
        }
    }

    dc.DestroyClippingRegion();
}

void TileTo(const wxBitmap& in, const wxRect& margins, wxBitmap& out, int w, int h)
{
    // Theoretically we're supposed to split the bitmap into 9 pieces based on
    // the sizing margins and leave the 8 outside pieces as unchanged as
    // possible and the fill the remainder with the center piece. However doing
    // that doesn't look actual control buttons.  So I'm going to just tile
    // the center bitmap to fill the whole space.
    int ml = margins.GetLeft();
    int mr = margins.GetRight();
    int mt = margins.GetTop();
    int mb = margins.GetBottom();

    int bw = in.GetWidth();
    int bh = in.GetHeight();

    wxBitmap center = in.GetSubBitmap(wxRect(wxPoint(   ml,mt),wxSize(bw-ml-mr,bh-mb-mt)));

    // Create and initially transparent bitmap.
    unsigned char* data = reinterpret_cast<unsigned char*>(malloc(3*w*h));
    unsigned char* alpha = reinterpret_cast<unsigned char*>(malloc(w*h));
    memset(alpha, 0, w*h);

    wxImage im(w, h, data, alpha);
    wxBitmap bmp(im);

    wxMemoryDC dc(bmp);
    TileBitmap(center, dc, wxRect(wxPoint(0,0),wxSize(w,h)));
    dc.SelectObject(wxNullBitmap);

    out = bmp;
}


class MyFrame: public wxFrame
{
public:
    MyFrame();

private:
    void OnPaintImagePanel(wxPaintEvent&);
    void OnListSelection(wxCommandEvent&);

    void BuildItemToDraw();
    void LoadThemeData();

    wxListBox* m_typeBox, *m_dpiBox, *m_stateBox;
    wxPanel* m_imagePanel;
    wxBitmap m_fullAtlas;
    wxBitmap m_itemToDraw;

    BGInfo m_closeInfo;
    BGInfo m_otherInfo;
    std::map<std::pair<DPI,Button>,ButtonInfo> m_themeMap;
};

MyFrame::MyFrame():wxFrame(NULL, wxID_ANY, "Windows Control Button Demo", wxDefaultPosition,
                           wxSize(400, 300))
{
    // Start all the image handlers.  Only the PNG handler is actually needed.
    ::wxInitAllImageHandlers();

    // Build the UI.
    wxPanel* bg = new wxPanel(this, wxID_ANY);
    wxStaticText* typeText = new wxStaticText(bg,wxID_ANY,"Type:");
    m_typeBox = new wxListBox(bg,wxID_ANY);
    wxStaticText* dpiText = new wxStaticText(bg,wxID_ANY,"dpi:");
    m_dpiBox = new wxListBox(bg,wxID_ANY);
    wxStaticText* stateText = new wxStaticText(bg,wxID_ANY,"State:");
    m_stateBox = new wxListBox(bg,wxID_ANY);
    m_imagePanel = new wxPanel(bg,wxID_ANY);

    wxBoxSizer* mainSzr = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* boxSzr = new wxBoxSizer(wxHORIZONTAL);
    boxSzr->Add(typeText, wxSizerFlags().Border(wxALL));
    boxSzr->Add(m_typeBox, wxSizerFlags().Border(wxTOP|wxRIGHT|wxBOTTOM));
    boxSzr->Add(dpiText, wxSizerFlags().Border(wxALL));
    boxSzr->Add(m_dpiBox, wxSizerFlags().Border(wxTOP|wxRIGHT|wxBOTTOM));
    boxSzr->Add(stateText, wxSizerFlags().Border(wxALL));
    boxSzr->Add(m_stateBox, wxSizerFlags().Border(wxTOP|wxRIGHT|wxBOTTOM));

    mainSzr->Add(boxSzr,wxSizerFlags());
    mainSzr->Add(m_imagePanel,wxSizerFlags(1).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM));

    bg->SetSizer(mainSzr);

    // Set the needed event handlers for the controls.
    m_imagePanel->Bind(wxEVT_PAINT, &MyFrame::OnPaintImagePanel, this);
    m_typeBox->Bind(wxEVT_LISTBOX, &MyFrame::OnListSelection, this);
    m_dpiBox->Bind(wxEVT_LISTBOX, &MyFrame::OnListSelection, this);
    m_stateBox->Bind(wxEVT_LISTBOX, &MyFrame::OnListSelection, this);

    // Concigure the controls.
    m_typeBox->Append("Close");
    m_typeBox->Append("Help");
    m_typeBox->Append("Max");
    m_typeBox->Append("Min");
    m_typeBox->Append("Restore");

    m_dpiBox->Append("96");
    m_dpiBox->Append("120");
    m_dpiBox->Append("144");
    m_dpiBox->Append("192");

    m_stateBox->Append("Normal");
    m_stateBox->Append("Hot");
    m_stateBox->Append("Pressed");
    m_stateBox->Append("Inactive");

    m_typeBox->Select(0);
    m_dpiBox->Select(0);
    m_stateBox->Select(0);

    // Load the theme data and finish setting up.
    LoadThemeData();
    BuildItemToDraw();
}

void MyFrame::LoadThemeData()
{
    HINSTANCE handle = LoadLibraryEx(L"C:\\Windows\\Resources\\Themes\\aero\\aero.msstyles",
                                     0, LOAD_LIBRARY_AS_DATAFILE);

    if ( handle == NULL )
    {
        return;
    }

    HTHEME theme = OpenThemeData(reinterpret_cast<HWND>(this->GetHandle()),L"DWMWindow");

    VOID* PBuf = NULL;
    DWORD BufSize = 0;

    GetThemeStream(theme, 0,0, TMT_DISKSTREAM, &PBuf, &BufSize, handle);

    wxMemoryInputStream mis(PBuf,static_cast<int>(BufSize));
    wxImage im(mis, wxBITMAP_TYPE_PNG);

    if ( !im.IsOk() )
    {
        return;
    }

    wxBitmap b2(im);
    m_fullAtlas = wxBitmap(im);;

    MARGINS m;
    RECT r;

    int BUTTONACTIVECAPTION = 3;
    int BUTTONACTIVECLOSE = 7;
    int BUTTONCLOSEGLYPH96 = 11;
    int BUTTONRESTOREGLYPH192 = 30;

    // Store some of the theme info for the parts BUTTONACTIVECAPTION
    // and BUTTONACTIVECLOSE.
    GetThemeRect(theme, BUTTONACTIVECAPTION, 0, TMT_ATLASRECT, &r);
    RectTowxRect(r,m_otherInfo.BgRect);
    GetThemeMargins(theme,NULL, BUTTONACTIVECAPTION,0, TMT_CONTENTMARGINS,NULL, &m);
    MarginsToRect(m,m_otherInfo.ContentMargins);
    GetThemeMargins(theme,NULL, BUTTONACTIVECAPTION,0, TMT_SIZINGMARGINS,NULL, &m);
    MarginsToRect(m,m_otherInfo.SizingMargins);
    GetThemeInt(theme, BUTTONACTIVECAPTION, 0, TMT_IMAGECOUNT, &(m_otherInfo.TotalStates));

    GetThemeRect(theme, BUTTONACTIVECLOSE, 0, TMT_ATLASRECT, &r);
    RectTowxRect(r,m_closeInfo.BgRect);
    GetThemeMargins(theme,NULL, BUTTONACTIVECLOSE,0, TMT_CONTENTMARGINS,NULL, &m);
    MarginsToRect(m,m_closeInfo.ContentMargins);
    GetThemeMargins(theme,NULL, BUTTONACTIVECLOSE,0, TMT_SIZINGMARGINS,NULL, &m);
    MarginsToRect(m,m_closeInfo.SizingMargins);
    GetThemeInt(theme, BUTTONACTIVECLOSE, 0, TMT_IMAGECOUNT, &(m_closeInfo.TotalStates));

    // Since the part numbers for BUTTONCLOSEGLYPH96..BUTTONRESTOREGLYPH192
    // are all sequential and the dpis all run from 96 to 192 in the same
    // order, we can use a for loop to store
    for ( int i = BUTTONCLOSEGLYPH96 ; i <= BUTTONRESTOREGLYPH192 ; ++i )
    {
        int j = i-BUTTONCLOSEGLYPH96;

        Button b = static_cast<Button>(j/4);
        DPI dpi = static_cast<DPI>(j%4);
        std::pair<DPI,Button> item;
        ButtonInfo info;

        item = std::make_pair(dpi,b);

        GetThemeRect(theme, i, 0, TMT_ATLASRECT, &r);
        RectTowxRect(r,info.ButtonRect);
        GetThemeInt(theme, i, 0, TMT_IMAGECOUNT, &(info.TotalStates));
        m_themeMap.insert(std::make_pair(item,info));
    }

    CloseThemeData(theme);
    FreeLibrary(handle);
}

void MyFrame::OnPaintImagePanel(wxPaintEvent&)
{
    wxPaintDC dc(m_imagePanel);
    dc.Clear();

    if ( m_itemToDraw.IsOk() )
    {
        dc.DrawBitmap(m_itemToDraw,0,0,true);
    }
}

void MyFrame::OnListSelection(wxCommandEvent&)
{
    BuildItemToDraw();
}

void MyFrame::BuildItemToDraw()
{
    BGInfo bginfo;
    Button b = static_cast<Button>(m_typeBox->GetSelection());
    DPI dpi = static_cast<DPI>(m_dpiBox->GetSelection());
    int state = m_stateBox->GetSelection();

    if ( b == Button::Close )
    {
        bginfo = m_closeInfo;
    }
    else
    {
        bginfo = m_otherInfo;
    }

    wxBitmap bgAtlas = m_fullAtlas.GetSubBitmap(bginfo.BgRect);
    int totalbgs = bginfo.TotalStates;
    wxBitmap bg = ExtractAtlas(bgAtlas, totalbgs, state);
    std::pair<DPI,Button> item = std::make_pair(dpi,b);

    auto it = m_themeMap.find(item);

    if ( it != m_themeMap.end() )
    {
        ButtonInfo info = it->second;

        wxBitmap itemAtlas = m_fullAtlas.GetSubBitmap(info.ButtonRect);

        wxBitmap item = ExtractAtlas(itemAtlas, info.TotalStates, state);

        wxRect contentmargins = bginfo.ContentMargins;
        wxRect Sizingmargins = bginfo.SizingMargins;
        int width = item.GetWidth() + contentmargins.GetLeft() + contentmargins.GetRight();
        int height = item.GetHeight() + contentmargins.GetTop() + contentmargins.GetBottom();

        if ( bg.GetWidth() > width )
        {
            width = bg.GetWidth();
        }

        if ( bg.GetHeight() > height )
        {
            height = bg.GetHeight();
        }

        wxBitmap bmp(width,height,32);
        TileTo(bg,Sizingmargins, bmp, width, height);

        wxMemoryDC dc(bmp);
        int leftOffset = (width-item.GetWidth())/2;
        int topOffset = (height - item.GetHeight())/2;

        dc.DrawBitmap(item,leftOffset,topOffset, true);
        dc.SelectObject(wxNullBitmap);

        m_itemToDraw = bmp;
    }

    m_imagePanel->Refresh();
    m_imagePanel->Update();
}


class MyApp : public wxApp
{
    public:
        virtual bool OnInit()
        {
            MyFrame* frame = new MyFrame();
            frame->Show();
            return true;
        }
};

wxIMPLEMENT_APP(MyApp);

This is only a partial answer because,

  1. This relys on numbers part numbers like BUTTONACTIVECAPTION that I'm simply entering into the code. These numbers are ultimately pulled from the file Aero.msstyles, and theoretically if Microsoft changes that file, the numbers in the code could be wrong. A full answer would look at that file and pull out the correct numbers from it so that it can always be sure it's using the correct ones. But doing that is beyond the scope of this answer.
  2. I'm not sure how the get the size for the buttos. On my system, the close button has a width of 45 pixels and height of 29 pixels. But I don't see those numers anywhere in any of the theme data.

The trick to drawing these buttons is that you first have to open the theme file as a dll. The name for the theme file can be pulled from the registry with the entry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ThemeManager\DllName. In the code above, I just hard coded this as "C:\Windows\Resources\Themes\aero\aero.msstyles", but it would probably be better to pull that from the registry instead of hard coding the filename.

Once the theme is opened, the special trick is to call the GetThemeStream function. This returns an in memory png file. The first part of it looks like this:

enter image description here

As you can see, this png contains a bunch of the pices of the control buttons. We'll need to use the GetThemeRect function to learn the rectangles in this png that coorespond to the parts that we want to draw.

But now we run into a problem. The theme class we need to use is "DWMWindow". This class is completely undocumented and the only way to learn its parts is to use a program like msstyleEditor to look at the theme file.

Running that program looks like this, enter image description here.

From the program, we can see that the part numbers we are interested in are:

int BUTTONACTIVECAPTION = 3;
int BUTTONACTIVECLOSE = 7;

int BUTTONCLOSEGLYPH96 = 11;
int BUTTONCLOSEGLYPH120 = 12;
int BUTTONCLOSEGLYPH144 = 13;
int BUTTONCLOSEGLYPH192 = 14;

int BUTTONHELPGLYPH96 = 15;
int BUTTONHELPGLYPH120 = 16;
int BUTTONHELPGLYPH144 = 17;
int BUTTONHELPGLYPH192 = 18;

int BUTTONMAXGLYPH96 = 19;
int BUTTONMAXGLYPH120 = 20;
int BUTTONMAXGLYPH144 = 21;
int BUTTONMAXGLYPH192 = 22;

int BUTTONMINGLYPH96 = 23;
int BUTTONMINGLYPH120 = 24;
int BUTTONMINGLYPH144 = 25;
int BUTTONMINGLYPH192 = 26;

int BUTTONRESTOREGLYPH96 = 27;
int BUTTONRESTOREGLYPH120 = 28;
int BUTTONRESTOREGLYPH144 = 29;
int BUTTONRESTOREGLYPH192 = 30;

With those part numbers we can use the GetThemeRect function know which parts of the png to use for the item we want to draw.

There's still some final problems. The rectangles GetThemeRect returns give for part BUTTONCLOSEGLYPH96 = 11 looks like this:

enter image description here

This is called an atlas, and each of the 4 pieces in that subrectangle corresponds to the states normal, hot, pushed, and disabled. However, again since the class is undocumented, the only way to know that is to look at the output from msstyleEditor or getting it from the theme is some other way. Fortunately we can use the GetThemeInt with the TMT_IMAGECOUNT property identifier to get the number of images in the atlas so at least we know how many pieces to cut it into.

There are a few more pieces of information we can pull from the theme data. The GetThemeMargins with the TMT_SIZINGMARGINS property id should tell us how to tile the background images into larger sizes. However in my experiments, the numbers from those margins don't seem to give good results. Consequently, in the code above I just tiled the center part to fill the whole background. In addition, using the TMT_CONTENTMARGINS property id should tell us where to place the glyphs on the background. But again, in my experiments, those positions didn't look good. So in the code above, I just centered the glyphs on the background.

Putting all of this together, we can finally draw the close, min, max, and restore buttons as they appear on a title bar.

Reza
  • 3,473
  • 4
  • 35
  • 54
New Pagodi
  • 3,484
  • 1
  • 14
  • 24
  • does it account for different resolutions and themes, because I noticed you are using Aero DLL? – Igor Oct 12 '21 at 15:12
  • @Reza, keep in mind that this is completely not portable solution and if you want to have it cross-platform you will need to have a lot of research. – Igor Oct 12 '21 at 15:13
  • @Igor my target OS is Windows (for now) but I would be very happy if I can find a an easier and portable solution. I mean creating a native bitmap should not be like this. What's the benfit of wxWidgets here? By the way, I am still trying to find a way to draw a native bitmap with the official render sample. But so far nothing. The result is like https://www.dropbox.com/s/5t4b393czsqrk3p/ControlButtons.png?dl=0 – Reza Oct 12 '21 at 15:31
  • @Reza, this is because wxWidgets is using `native` set of things. You are moving away from nativeness. The look of your program will be very odd. The behavior of your program will be very odd - the user will have to click in the very specific area in order to move the window by it title bar. You will have to implement double clicking on the title bar yourself. You will have to manage all positioning and sizing yourself. You are over-thinkibng the process instead of following the simple way of doing things and follow the KISS principle. – Igor Oct 12 '21 at 15:50
  • @Igor I know what you mean. But that's what I have to do now for my company. They want a very specific custom title bar. Something like Microsoft Office title bar which include icons, search text filed and so on. I personally find the title bar of Microsoft Word or Outlook nice. Anyway, could you please help me find a fix for the render sample program? – Reza Oct 12 '21 at 15:57
  • 1
    @Reza, I think what you are looking for is called wxRibbon. Take a look at the `ribbon` sample. So you don't need a custom title per-se. You just need a ribbon. Compile it, play with it, understand it. Implement it in your own software. This is much simpler solution. And sorry - I completely missed your OP point about MS Office style thing. – Igor Oct 12 '21 at 16:17
  • 1
    If you have to implement a custom title bar, I wrote [this answer](https://stackoverflow.com/questions/68088652/create-a-titleless-borderless-draggable-wxframe-in-wxwidgets/68092156#68092156) a while ago that shows one way to do this. As Igor said, below the title bar you would probably want to use a ribbon to create an office-ish UI. – New Pagodi Oct 12 '21 at 17:46
  • @Igor Thanks for mentioning ribbon. But that's not what I want. I want something like this https://www.dropbox.com/s/151o0cbnoee2w3n/Titlebar.png?dl=0 – Reza Oct 13 '21 at 07:30
  • @Reza, I only mentioned ribbon because you said the company wants msoffice-like UI. However, I see couple of things with yours. You will not be able to drag you window by title bar. You will not have support for double clicking on the title bar. You will need to properly position the title window yourself, accounting for different DPI. You will need to handle menu selection yourself. And finally - this is not how msoffice looks like, so your bosses might be dissatisfied – Igor Oct 13 '21 at 12:02
  • @Reza, basically you create work for nothing if there is a possibility of rejection and just a time waste. Just show the ribbon demo and you idea and ask what they want. – Igor Oct 13 '21 at 12:04
  • @NewPagodi Does this applies to all versions of Windows like (XP, Vista ,...)? What if user change the theme dynamically ( while the app is running) ? Is there an event for that anywhere? – Reza Oct 18 '21 at 13:07
  • 1
    For windows 7 and lower, you can use the buttons from render native as you showed above. I never used windows 8, so I don't know there. For windows 10 and up, you'll need to draw the buttons like in this answer. You can use the `wxGetWinVersion` (from wx/msw/private.h) to check the version at runtime. To check for changes to the system theme, you can handle the [WM_THEMECHANGED](https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-themechanged) message. I showed how to do that in this [answer](https://stackoverflow.com/questions/67843563/font-size-scaling-problems/67844528#67844528). – New Pagodi Oct 18 '21 at 14:50
  • @NewPagodi looks like only non-UWP apps use icons from `DWMWindow`: when I set display scale to 400%, icons of non-UWP apps stay relatively small (biggest 192 dpi version to be precise). Icons of UWP apps, however, continue scaling to very big sizes, this makes me think that UWP apps use different source of icons (maybe even vector ones), do you know anything about this? I also found similar icons in "Menu" & "DarkMode::Menu" classes, but they look like bold version of standard icons, have no idea whether they are used anywhere. – YaaZ Dec 29 '22 at 14:48
  • Unfortunately I don't. I really need to learn more about UWP apps and I don't know enough about them now. Sorry. – New Pagodi Dec 29 '22 at 16:02
1

You should be able to draw bitmaps with the native look of those buttons. Then use those bitmaps on wxButton.

There are some limitations, but Windows should have the best support for them. See wxRendererNative::DrawTitleBarBitmap().

Reza
  • 3,473
  • 4
  • 35
  • 54
catalin
  • 1,927
  • 20
  • 20
  • Thanks for your answer. I tried render sample but the buttons are renderend like win XP which I think because I am using the Generic renderer. When I try to load `renddll_mswud315_vc.dll', it fails to load it. If I succeed to load that dll, the buttons should be like win 10. Is my understanding correct so far? – Reza Oct 11 '21 at 13:08
  • @Reza, do you use `manifest` during the build? – Igor Oct 11 '21 at 14:45
  • @Igor No, as far as I know, I don't use manifest. – Reza Oct 11 '21 at 15:01
  • @Reza, which compiler do you use? – Igor Oct 11 '21 at 19:30
  • @Igor I am using Microsoft Visual Studio 2019 IDE and its compiler. – Reza Oct 11 '21 at 20:36
  • @Reza, are you using MSVC or you are building from the Command Prompt? – Igor Oct 11 '21 at 20:59
  • Check your project resource file - it should include `wx/msw/wx.rc`. If its not - add it and rebuild. – Igor Oct 11 '21 at 21:02
  • @Igor it is already there. I am building https://github.com/wxWidgets/wxWidgets/blob/master/samples/render/render_vc9_render.vcproj and the resource file "sample.rc" already included "wx.rc" – Reza Oct 12 '21 at 10:38