Page 1 of 2

How to create such a layout?

Posted: Thu Mar 14, 2019 1:49 am
by ONEEYEMAN
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.

Re: How to create such a layout?

Posted: Thu Mar 14, 2019 8:16 am
by doublemax
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?

Re: How to create such a layout?

Posted: Thu Mar 14, 2019 12:30 pm
by ONEEYEMAN
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.

Re: How to create such a layout?

Posted: Thu Mar 14, 2019 1:07 pm
by 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.
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).

Re: How to create such a layout?

Posted: Thu Mar 14, 2019 1:37 pm
by ONEEYEMAN
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.

Re: How to create such a layout?

Posted: Thu Mar 14, 2019 3:14 pm
by doublemax
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.

Re: How to create such a layout?

Posted: Fri Mar 15, 2019 7:58 am
by iwbnwif
Can't you derive from wxGridRowHeaderRenderer and draw row headers without a border or background?

Re: How to create such a layout?

Posted: Fri Mar 15, 2019 8:08 am
by doublemax
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.

Re: How to create such a layout?

Posted: Fri Mar 15, 2019 10:25 am
by iwbnwif
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).

Re: How to create such a layout?

Posted: Fri Mar 15, 2019 5:10 pm
by ONEEYEMAN
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.

Re: How to create such a layout?

Posted: Sun Mar 17, 2019 10:35 pm
by ONEEYEMAN
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.

Re: How to create such a layout?

Posted: Mon Mar 18, 2019 1:29 am
by ONEEYEMAN
And this is a screenshot from the OSX.

Thank you.

Re: How to create such a layout?

Posted: Mon Mar 18, 2019 6:22 am
by doublemax
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.

Re: How to create such a layout?

Posted: Mon Mar 18, 2019 1:37 pm
by PB
This supersimple implementation seems to work as expected on MSW with current(ish) GIT master:
grid.png
grid.png (2.43 KiB) Viewed 712 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);

Re: How to create such a layout?

Posted: Mon Mar 18, 2019 2:35 pm
by ONEEYEMAN
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.