Strange borders on macOS with wxBG_STYLE_PAINT on retina screen

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
Post Reply
hneubauer
Earned a small fee
Earned a small fee
Posts: 11
Joined: Wed May 13, 2020 4:08 pm

Strange borders on macOS with wxBG_STYLE_PAINT on retina screen

Post by hneubauer » Mon Jul 13, 2020 12:39 pm

We use wxWidgets 3.1.3 under Linux, Windows and macOS. Under the latter we have a strange phenomenon when our application is displayed on a retina monitor.

We draw almost all components ourselves. If we use the background style wxBG_STYLE_PAINT, we get a kind of right and bottom border.

In the attached picture you can see the white lines between buttons and you can also guess the line below.

The color of this border is the same as the color of the parent component. It almost seems as if painting the background (realized with GetSize and DrawRectangle) does not paint the full area. Interestingly, the effect does not occur if you leave the Default Background Style or simply move the window to a non-retina monitor.

Does anyone have an idea why this is the case or how to get rid of this effect? Thanks a lot.
retina.png
retina.png (6.86 KiB) Viewed 179 times

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 4245
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Strange borders on macOS with wxBG_STYLE_PAINT on retina screen

Post by ONEEYEMAN » Mon Jul 13, 2020 1:15 pm

Hi,
Can you reproduce it with the minimal sample?
If not - can you post some code?

Thank you.

hneubauer
Earned a small fee
Earned a small fee
Posts: 11
Joined: Wed May 13, 2020 4:08 pm

Re: Strange borders on macOS with wxBG_STYLE_PAINT on retina screen

Post by hneubauer » Tue Jul 14, 2020 9:58 am

Yes, I adapted the minimal sample using a class which inherits from wxPanel.

You will see two red areas with a blue right/bottom border on a retina screen. With no retina screen you won't see the blue borders.

Code: Select all

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

// ----------------------------------------------------------------------------
// resources
// ----------------------------------------------------------------------------

// the application icon (under Windows it is in resources and even
// though we could still include the XPM here it would be unused)
#ifndef wxHAS_IMAGES_IN_RESOURCES
    #include "../sample.xpm"
#endif

// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------

// Define a new application type, each program should derive a class from wxApp
class MyApp : public wxApp
{
public:
    // override base class virtuals
    // ----------------------------

    // this one is called on application startup and is a good place for the app
    // initialization (doing it here and not in the ctor allows to have an error
    // return: if OnInit() returns false, the application terminates)
    virtual bool OnInit() wxOVERRIDE;
};

#include <wx/dcbuffer.h>
class MyPanel : public wxPanel {
public:
    MyPanel(wxWindow* parent) : wxPanel(parent) {
        SetBackgroundStyle(wxBG_STYLE_PAINT);
        Connect(wxEVT_PAINT, wxPaintEventHandler(MyPanel::onPaint));
    }

    void onPaint( wxPaintEvent& event ) {
        wxAutoBufferedPaintDC dc( this );
        int w, h;
        GetSize(&w, &h);
        wxRect rect( 0,0, w, h );
        dc.SetBrush( wxBrush( *wxRED ) );
        dc.SetPen( wxPen( *wxRED ) );
        dc.DrawRectangle( rect );
    }
};

// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
    // ctor(s)
    MyFrame(const wxString& title);

    // event handlers (these functions should _not_ be virtual)
    void OnQuit(wxCommandEvent& event);
    void OnAbout(wxCommandEvent& event);

private:
    // any class wishing to process wxWidgets events must use this macro
    wxDECLARE_EVENT_TABLE();
};

// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------

// IDs for the controls and the menu commands
enum
{
    // menu items
    Minimal_Quit = wxID_EXIT,

    // it is important for the id corresponding to the "About" command to have
    // this standard value as otherwise it won't be handled properly under Mac
    // (where it is special and put into the "Apple" menu)
    Minimal_About = wxID_ABOUT
};

// ----------------------------------------------------------------------------
// event tables and other macros for wxWidgets
// ----------------------------------------------------------------------------

// the event tables connect the wxWidgets events with the functions (event
// handlers) which process them. It can be also done at run-time, but for the
// simple menu events like this the static method is much simpler.
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_MENU(Minimal_Quit,  MyFrame::OnQuit)
    EVT_MENU(Minimal_About, MyFrame::OnAbout)
wxEND_EVENT_TABLE()

// Create a new application object: this macro will allow wxWidgets to create
// the application object during program execution (it's better than using a
// static object for many reasons) and also implements the accessor function
// wxGetApp() which will return the reference of the right type (i.e. MyApp and
// not wxApp)
wxIMPLEMENT_APP(MyApp);

// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// the application class
// ----------------------------------------------------------------------------

// 'Main program' equivalent: the program execution "starts" here
bool MyApp::OnInit()
{
    // call the base class initialization method, currently it only parses a
    // few common command-line options but it could be do more in the future
    if ( !wxApp::OnInit() )
        return false;

    // create the main application window
    MyFrame *frame = new MyFrame("Minimal wxWidgets App");

    // and show it (the frames, unlike simple controls, are not shown when
    // created initially)
    frame->Show(true);
    frame->Layout();
    frame->Refresh();

    // success: wxApp::OnRun() will be called which will enter the main message
    // loop and the application will run. If we returned false here, the
    // application would exit immediately.
    return true;
}

// ----------------------------------------------------------------------------
// main frame
// ----------------------------------------------------------------------------

// frame constructor
MyFrame::MyFrame(const wxString& title)
       : wxFrame(NULL, wxID_ANY, title)
{
    // set the frame icon
    SetIcon(wxICON(sample));


    // If menus are not available add a button to access the about box
    SetBackgroundColour(*wxBLUE);
    wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
    sizer->Add(new MyPanel(this), 1, wxEXPAND);
    sizer->Add(new MyPanel(this), 1, wxEXPAND);
    SetSizer(sizer);

#if wxUSE_STATUSBAR
    // create a status bar just for fun (by default with 1 pane only)
    CreateStatusBar(2);
    SetStatusText("Welcome to wxWidgets!");
#endif // wxUSE_STATUSBAR
}


// event handlers

void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
    // true is to force the frame to close
    Close(true);
}

void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
    wxMessageBox(wxString::Format
                 (
                    "Welcome to %s!\n"
                    "\n"
                    "This is the minimal wxWidgets sample\n"
                    "running under %s.",
                    wxVERSION_STRING,
                    wxGetOsDescription()
                 ),
                 "About wxWidgets minimal sample",
                 wxOK | wxICON_INFORMATION,
                 this);
}

hneubauer
Earned a small fee
Earned a small fee
Posts: 11
Joined: Wed May 13, 2020 4:08 pm

Re: Strange borders on macOS with wxBG_STYLE_PAINT on retina screen

Post by hneubauer » Fri Jul 17, 2020 8:27 am

No solution but some workarounds :)

1. Set the background color of the parent to match (not always possible, since there are several children and the fix only helps for one child component)

2. Do without the Background Style Flag wxBG_STYLE_PAINT for macOS. However, the wxAutoBufferedPaintDC has to be omitted, because it has an assertion for the presence of this flag.

3. Using a wxGraphicsContext for rendering. Its drawing methods also work correctly on a retina display.

Post Reply