How to create such a layout?

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 4431
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

How to create such a layout?

Post by ONEEYEMAN » Thu Mar 14, 2019 1:49 am

Hi,
Attached is a screenshot from the MS Access.
What would be the way to create a layout that I marked with RED? Please forgive my ugly painting technique...

Basically it's a grid without row labels and instead some static text on the left side.

Thank you for any pointers.
Attachments
layout.png

User avatar
doublemax
Moderator
Moderator
Posts: 15181
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to create such a layout?

Post by doublemax » Thu Mar 14, 2019 8:16 am

Basically it's a grid without row labels and instead some static text on the left side.
That's exactly what i'd do. You can use HideRowLabels() / HideColLabels to hide the labels.

Where exactly is the problem?
Use the source, Luke!

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

Re: How to create such a layout?

Post by ONEEYEMAN » Thu Mar 14, 2019 12:30 pm

doublemax,
I fear those labels will not be aligned to the grid rows, since wxGrid paints itself with wx and the labels are produced as OS-natives.
I can try to see whether it will work or not...

Thank you.

User avatar
doublemax
Moderator
Moderator
Posts: 15181
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to create such a layout?

Post by doublemax » Thu Mar 14, 2019 1:07 pm

I fear those labels will not be aligned to the grid rows, since wxGrid paints itself with wx and the labels are produced as OS-natives.
Yes, that's a problem. But as wxGrid is a generic control, wxGrid::GetRowSize() should tell you how far the labels must be apart. You'd have to use absolute positioning though.

Or you leave the controls in a sizer and adjust the row sizes of the grid according to their position. (Just an idea, but theoretically it should work).
Use the source, Luke!

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

Re: How to create such a layout?

Post by ONEEYEMAN » Thu Mar 14, 2019 1:37 pm

doublemax,
doublemax wrote:
Thu Mar 14, 2019 1:07 pm
I fear those labels will not be aligned to the grid rows, since wxGrid paints itself with wx and the labels are produced as OS-natives.
Yes, that's a problem. But as wxGrid is a generic control, wxGrid::GetRowSize() should tell you how far the labels must be apart. You'd have to use absolute positioning though.
If its absolute positioning - it means custom control. Don't really like that.
doublemax wrote:
Thu Mar 14, 2019 1:07 pm
Or you leave the controls in a sizer and adjust the row sizes of the grid according to their position. (Just an idea, but theoretically it should work).
Could you give a pseudo-code?

Thank you.

User avatar
doublemax
Moderator
Moderator
Posts: 15181
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to create such a layout?

Post by doublemax » Thu Mar 14, 2019 3:14 pm

If its absolute positioning - it means custom control. Don't really like that.
It will be a custom control in any case, there really is no clean way to align any control to a rows in a wxGrid.
Could you give a pseudo-code?
Not really.

My general idea is:
- have dedicated wxPanel just for the labels
- all labels in a vertical boxsizer
- put that panel and the grid next to each other in a horizontal boxsizer
- catch the wxSize event of the panel (eventually you might need another way to find out when the position of the labels changed)
- get the position of each label and calculate each distance from the label above (or the top of the panel)
- set the row height for each row so that it matches the height of the label
- eventually you might have to add a pixel or so to make it fit

Yes, it's ugly, but i don't think there is a better way.

The easiest way would be to just use the rowlabels as labels, but it wouldn't look so nice.
Use the source, Luke!

iwbnwif
Super wx Problem Solver
Super wx Problem Solver
Posts: 274
Joined: Tue Mar 19, 2013 8:52 pm

Re: How to create such a layout?

Post by iwbnwif » Fri Mar 15, 2019 7:58 am

Can't you derive from wxGridRowHeaderRenderer and draw row headers without a border or background?
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.

User avatar
doublemax
Moderator
Moderator
Posts: 15181
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to create such a layout?

Post by doublemax » Fri Mar 15, 2019 8:08 am

iwbnwif wrote:
Fri Mar 15, 2019 7:58 am
Can't you derive from wxGridRowHeaderRenderer and draw row headers without a border or background?
Good idea. The only problem is that the horizontal scrollbar will cover the whole width of the control, including the the row label area. Which looks kind of weird.
Use the source, Luke!

iwbnwif
Super wx Problem Solver
Super wx Problem Solver
Posts: 274
Joined: Tue Mar 19, 2013 8:52 pm

Re: How to create such a layout?

Post by iwbnwif » Fri Mar 15, 2019 10:25 am

The only problem is that the horizontal scrollbar will cover the whole width of the control, including the the row label area. Which looks kind of weird.
True, but presumably it is possible to use ShowScrollbars to hide the horizontal one and then add a wxScrollBar of the right width below the grid.

Another problem might be that wxGrid draws a border around the control, including a part that isn't covered by the row header area. I am not able to try this at the moment so don't know. Also, it may vary from platform to platform (but shouldn't being a generic control).
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.

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

Re: How to create such a layout?

Post by ONEEYEMAN » Fri Mar 15, 2019 5:10 pm

Hi,
iwbnwif wrote:
Fri Mar 15, 2019 7:58 am
Can't you derive from wxGridRowHeaderRenderer and draw row headers without a border or background?
Interesting idea. Will try to build grid sample to see how it will look like.

Thank you.

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

Re: How to create such a layout?

Post by ONEEYEMAN » Sun Mar 17, 2019 10:35 pm

Hi,
iwbnwif wrote:
Fri Mar 15, 2019 7:58 am
Can't you derive from wxGridRowHeaderRenderer and draw row headers without a border or background?
I tried to follow this, but following code:

Code: Select all

class GridRowLabelRenderer : public wxGridRowHeaderRenderer
{
public:
    GridRowLabelRenderer ()
    {
    }

    virtual void DrawLabel (const wxGrid &grid, wxDC &dc, const wxString &value, const wxRect &rect, int horizAlign, int vertAlign, int WXUNUSED(textOrientation)) const wxOVERRIDE
    {
		wxColour color = grid.GetParent()->GetBackgroundColour();
        dc.SetTextBackground( color );
        dc.SetTextForeground( *wxBLACK );
        wxFont font = dc.GetFont();
        font.SetWeight( wxFONTWEIGHT_NORMAL );
        dc.DrawLabel( value, rect, horizAlign | vertAlign );
    }

    virtual void DrawBorder (const wxGrid &WXUNUSED(grid), wxDC &dc, wxRect &rect) const wxOVERRIDE
    {
        dc.SetPen( *wxTRANSPARENT_PEN );
        dc.SetBrush( *wxTRANSPARENT_BRUSH );
        dc.DrawRectangle( rect );
    }
    wxDECLARE_NO_COPY_CLASS( GridRowLabelRenderer );
};

class CustomRowHeaderProvider : public wxGridCellAttrProvider
{
public:
    CustomRowHeaderProvider () : m_customRowLabelRenderer ()
    {
    }
protected:
    virtual const wxGridRowHeaderRenderer &GetRowHeaderRenderer (int WXUNUSED(row)) wxOVERRIDE
    {
        return m_customRowLabelRenderer;
    }
private:
    GridRowLabelRenderer m_customRowLabelRenderer;
    wxDECLARE_NO_COPY_CLASS( CustomRowHeaderProvider );
};

class GridCornerHeaderRenderer : public wxGridCornerHeaderRenderer
{
public:
    GridCornerHeaderRenderer()
    {
    }

    virtual void DrawBorder(const wxGrid &WXUNUSED(grid), wxDC &dc, wxRect &rect) const wxOVERRIDE
    {
        dc.SetPen( *wxTRANSPARENT_PEN );
        dc.SetBrush( *wxTRANSPARENT_BRUSH );
        dc.DrawRectangle( rect );
    }
    wxDECLARE_NO_COPY_CLASS( GridCornerHeaderRenderer );
};

class CustomCornerHeaderProvider : public wxGridCellAttrProvider
{
public:
    CustomCornerHeaderProvider() : m_customCornerLabelRenderer()
    {
    }
protected:
    virtual const wxGridCornerHeaderRenderer &GetCornerRenderer() wxOVERRIDE
    {
        return m_customCornerLabelRenderer;
    }
private:
    GridCornerHeaderRenderer m_customCornerLabelRenderer;
    wxDECLARE_NO_COPY_CLASS( CustomCornerHeaderProvider );
};
And this is how I am using it:

Code: Select all

    m_grid = new wxGrid( m_panel, wxID_ANY );
    m_grid->CreateGrid( 4, 0 );
    m_grid->GetTable()->SetAttrProvider( new CustomRowHeaderProvider );
    m_grid->GetTable()->SetAttrProvider( new CustomCornerHeaderProvider );
    m_grid->SetRowLabelAlignment( wxALIGN_RIGHT, wxALIGN_CENTER );
    m_grid->SetRowLabelValue( 0, _( "Column:" ) );
    m_grid->SetRowLabelValue( 1, _( "Sort:" ) );
    m_grid->SetRowLabelValue( 2, _( "Criteria:" ) );
    m_grid->SetRowLabelValue( 3, _( "Or:" ) );
    m_grid->SetRowLabelValue( 4, _( "" ) );
My layout is this:

wxDialog -> wxPanel -> wxGrid on the sizer.

The result on *nix (GTK3) is attached.

Am I doing something wrong?

First of all there is a dark area on top of the grid, and there are lines in the row labels column.

Thank you.

I'm using self compiled wx 3.1.1 with GTK+ 3.22.30.
Attachments
Screenshot from 2019-03-17 18-25-58.png
Screenshot from 2019-03-17 18-25-58.png (5.05 KiB) Viewed 772 times

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

Re: How to create such a layout?

Post by ONEEYEMAN » Mon Mar 18, 2019 1:29 am

And this is a screenshot from the OSX.

Thank you.
Attachments
Screen Shot 2019-03-17 at 8.53.16 PM.png
Screen Shot 2019-03-17 at 8.53.16 PM.png (12.25 KiB) Viewed 763 times

User avatar
doublemax
Moderator
Moderator
Posts: 15181
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to create such a layout?

Post by doublemax » Mon Mar 18, 2019 6:22 am

Hide the column header with "grid->HideColLabels();"
Then you also shouldn't need the CustomCornerHeaderProvider.

Do the lines disappear when you set EnableGridLines(false)?
That would also hide the gridlines in the grid itself, but at least we'll know where they come from.
Use the source, Luke!

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2572
Joined: Sun Jan 03, 2010 5:45 pm

Re: How to create such a layout?

Post by PB » Mon Mar 18, 2019 1:37 pm

This supersimple implementation seems to work as expected on MSW with current(ish) GIT master:
grid.png
grid.png (2.43 KiB) Viewed 717 times

Code: Select all

#include <wx/wx.h>
#include <wx/grid.h>

class MyGridHeaderLabelsRenderer : public wxGridRowHeaderRenderer
{
    void DrawBorder(const wxGrid& grid, wxDC& dc, wxRect& rect) const wxOVERRIDE
    {   
        const wxColour clr = grid.GetParent()->GetBackgroundColour();
        wxPen pen(clr);
        wxBrush brush(clr);

        dc.SetPen(pen);
        dc.SetBrush(brush);
        dc.DrawRectangle(rect);

        rect.Deflate(2);
    } 
 
    void DrawLabel (const wxGrid &grid, wxDC &dc, const wxString &value, const wxRect &rect, int horizAlign, int vertAlign, int textOrientation) const wxOVERRIDE
    {        
        wxFont font(grid.GetLabelFont());
        
        font.SetWeight(wxFONTWEIGHT_NORMAL);

        dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
        
        dc.SetTextForeground(grid.GetLabelTextColour());
        dc.SetFont(font);
        
        grid.DrawTextRectangle(dc, value, rect, horizAlign, vertAlign, textOrientation);     
    }
};

class MyGridHeaderLabelsRendererProvider : public wxGridCellAttrProvider
{
protected:
    const wxGridRowHeaderRenderer& GetRowHeaderRenderer(int) wxOVERRIDE
    {    
        return m_renderer;
    }
private:
    MyGridHeaderLabelsRenderer m_renderer;
}; 

class MyFrame: public wxFrame
{
public:   
    MyFrame() : wxFrame (NULL, wxID_ANY, "Test", wxDefaultPosition, wxSize(800, 600))
    {        
        wxPanel* mainPanel = new wxPanel(this);
        wxGrid* grid = new wxGrid(mainPanel, wxID_ANY);

        grid->CreateGrid(4, 1);
        grid->HideColLabels();
        grid->SetRowLabelAlignment(wxALIGN_RIGHT, wxALIGN_CENTRE);
        grid->GetTable()->SetAttrProvider(new MyGridHeaderLabelsRendererProvider);

         wxTextCtrl* logCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 
            wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);     
        wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl)); 
        
        
        wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
        
        mainSizer->Add(grid, wxSizerFlags().Expand().DoubleBorder().Proportion(2));
        mainSizer->Add(logCtrl, wxSizerFlags().Expand().DoubleBorder().Proportion(1));
        mainPanel->SetSizer(mainSizer);
    }
};

class MyApp : public wxApp
{
public:         
    bool OnInit()
    {
        (new MyFrame())->Show();               
        return true;
    }   
}; wxIMPLEMENT_APP(MyApp);

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

Re: How to create such a layout?

Post by ONEEYEMAN » Mon Mar 18, 2019 2:35 pm

Hi, guys,
@doublemax: For some unknown reason, when I hide the column labels and got rid of the renderer, the lines also disappear. I don't see them anymore.
Very strange.
@PB: I will try your code when I get home.

It would be nice to see if doing straight vertical box sizer instead of those extra classes will work, as I don't like to re-work scrolling the grid and redo the labels.

Or I may try the PR with freezing rows/cols...

Thank you.

Post Reply