Object clutter?...slow movement of user drawn control

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.
Post Reply
death_ghast
Knows some wx things
Knows some wx things
Posts: 26
Joined: Thu Jan 26, 2006 1:27 pm

Object clutter?...slow movement of user drawn control

Post by death_ghast » Sun Apr 02, 2006 3:44 pm

Hi,

Based on the dicussion i had with Sof.T i decided to write my own form designer. And it's almost done BUT...i have something strange happening. If i grab one corner of the user drawn control (wxPanel at base) and keep moving it, after about 30 seconds, i get 100% processor usage, and the movement and drawing gets jerky and stuttered somehow...it's not moving in realtime along with the mouse. And this happenes only due to my OnPaint method. So i will paste it here and maybe you can tell me what's wrong (i tested the code and moved the object around making a very simplistic paint by only drawing a rectangle...no brushes no nothing, and that's how i isolated the problem to the paint event)

It's done with winapi so maybe i have some bad usage of some GDI painting principles.

Code: Select all

void wsFormDesigner::OnPaint(wxPaintEvent& event){
    wxPaintDC DC(this);
    // convenience vars
    HDC   hDC      = GetWindowDC((HWND)GetHandle());

    // setup double buffer
    MemoryHDC        hDCMem(hDC);
    CompatibleBitmap hCBmp(hDC, GetSize().GetWidth(), GetSize().GetHeight());
    SelectInHDC      selectBmp(hDCMem, hCBmp);

    ///clear the background (parent color)...todo, blit the parent...
    HPEN hPenClear,hOldPen,hPenC;
    HBRUSH hBrushClear,hOldBrush,hBrushC;
    unsigned int clearRed=GetParent()->GetBackgroundColour().Red();
    unsigned int clearGreen=GetParent()->GetBackgroundColour().Green();
    unsigned int clearBlue=GetParent()->GetBackgroundColour().Blue();
    unsigned int cRed=wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE).Red();
    unsigned int cGreen=wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE).Green();
    unsigned int cBlue=wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE).Blue();

    hBrushClear = CreateSolidBrush(RGB(clearRed, clearGreen, clearBlue));
    hPenClear = CreatePen(PS_SOLID, 1, RGB(clearRed, clearGreen, clearBlue));
    hBrushC = CreateSolidBrush(RGB(cRed, cGreen, cBlue));
    hPenC = CreatePen(PS_SOLID, 1, RGB(cRed, cGreen, cBlue));

    hOldPen=(HPEN)SelectObject(hDCMem, hPenClear);
    hOldBrush=(HBRUSH)SelectObject(hDCMem, hBrushClear);

    Rectangle(hDCMem,0,0,GetSize().GetWidth(),GetSize().GetHeight());

    SelectObject(hDCMem, hPenC);
    SelectObject(hDCMem, hBrushC);

    Rectangle(hDCMem,
              m_BulletWidth+m_BorderSize,
              m_BulletHeight+m_TitlebarHeight,
              GetSize().GetWidth()-m_BulletWidth-m_BorderSize,
              GetSize().GetHeight()-m_BulletHeight-m_BorderSize);


    if (m_HasFocus){
        ///DRAW THE RESIZING HANDLES
        HPEN hPenFixed,hPenDrag;
        HBRUSH hBrushFixed,hBrushDrag;
        unsigned char inRed=m_HandlesInnerColour.Red();
        unsigned char inGreen=m_HandlesInnerColour.Green();
        unsigned char inBlue=m_HandlesInnerColour.Blue();
        unsigned char outRed=m_HandlesBorderColour.Red();
        unsigned char outGreen=m_HandlesBorderColour.Green();
        unsigned char outBlue=m_HandlesBorderColour.Blue();


        hBrushDrag = CreateSolidBrush(RGB(inRed, inGreen, inBlue));
        hBrushFixed = CreateSolidBrush(RGB(outRed, outGreen, outBlue));

        hPenFixed = CreatePen(PS_SOLID, 1, RGB(inRed, inGreen, inBlue));
        hPenDrag = CreatePen(PS_SOLID, 1, RGB(outRed, outGreen, outBlue));

        //draggable handles
        SelectObject(hDCMem, hPenDrag);
        SelectObject(hDCMem, hBrushDrag);

        Rectangle(hDCMem,
                  GetSize().GetWidth()-m_BulletWidth,
                  GetSize().GetHeight()/2-m_BulletHeight/2,
                  GetSize().GetWidth(),
                  GetSize().GetHeight()/2-m_BulletHeight/2+m_BulletHeight);
        Rectangle(hDCMem,
                  GetSize().GetWidth()-m_BulletWidth,
                  GetSize().GetHeight()-m_BulletHeight,
                  GetSize().GetWidth(),
                  GetSize().GetHeight());
        Rectangle(hDCMem,
                  GetSize().GetWidth()/2-m_BulletWidth/2,
                  GetSize().GetHeight()-m_BulletHeight,
                  GetSize().GetWidth()/2-m_BulletWidth/2+m_BulletWidth,
                  GetSize().GetHeight());


        //fixed handles
        SelectObject(hDCMem, hPenFixed);
        SelectObject(hDCMem, hBrushFixed);

        Rectangle(hDCMem,
                  GetSize().GetWidth()/2-m_BulletWidth/2,
                  0,
                  GetSize().GetWidth()/2-m_BulletWidth/2+m_BulletWidth,
                  m_BulletHeight);
        Rectangle(hDCMem,
                  0,
                  GetSize().GetHeight()/2-m_BulletHeight/2,
                  m_BulletWidth,
                  GetSize().GetHeight()/2-m_BulletHeight/2+m_BulletHeight);
        Rectangle(hDCMem,
                  0,
                  0,
                  m_BulletWidth,
                  m_BulletHeight);
        Rectangle(hDCMem,
                  GetSize().GetWidth()-m_BulletWidth,
                  0,
                  GetSize().GetWidth(),
                  m_BulletHeight);
        Rectangle(hDCMem,
                  0,
                  GetSize().GetHeight()-m_BulletHeight,
                  m_BulletWidth,
                  GetSize().GetHeight());

        DeleteObject(hPenFixed);
        DeleteObject(hBrushFixed);
        DeleteObject(hPenDrag);
        DeleteObject(hBrushDrag);

    }else{
        //HPEN hPenFixed,hPenDrag, hPenOld;
        //HBRUSH hBrushFixed,hBrushDrag, hBrushOld;
        //Rectangle(hDCMem, 0,0,GetSize().GetWidth(),GetSize().GetHeight());
    }
    if (UsingThemes()){
        if (g_xpStyle.IsAppThemed()){
            DrawForm(hDCMem);
        }
    }
    SelectObject(hDCMem, hOldPen);
    SelectObject(hDCMem, hOldBrush);
    DeleteObject(hPenClear);
    DeleteObject(hBrushClear);

    ::BitBlt(hDC, 0, 0, GetSize().GetWidth(), GetSize().GetHeight(), hDCMem, 0, 0, SRCCOPY);

}

void wsFormDesigner::DrawForm(MemoryHDC &hDCMem){
    RECT titlebar,frameleft,frameright,framebottom;
    //draw the borders
    titlebar.left=m_BulletWidth;
    titlebar.right=GetSize().GetWidth()-m_BulletWidth;
    titlebar.top=m_BulletHeight;
    titlebar.bottom=m_TitlebarHeight+m_BulletHeight;
    g_xpStyle.DrawThemeBackground(m_hTheme, hDCMem,1, 1, &titlebar, 0);
    frameleft.left=m_BulletWidth;
    frameleft.right=frameleft.left+m_BorderSize;
    frameleft.top=titlebar.bottom;
    frameleft.bottom=GetSize().GetHeight()-m_BulletHeight;
    g_xpStyle.DrawThemeBackground(m_hTheme, hDCMem,7, 1, &frameleft, 0);
    frameright.left=GetSize().GetWidth()-m_BulletWidth-m_BorderSize;
    frameright.right=frameright.left+m_BorderSize;
    frameright.top=titlebar.bottom;
    frameright.bottom=GetSize().GetHeight()-m_BulletHeight;
    g_xpStyle.DrawThemeBackground(m_hTheme, hDCMem,8, 1, &frameright, 0);
    framebottom.left=m_BulletWidth;
    framebottom.right=GetSize().GetWidth()-m_BulletWidth;
    framebottom.top=GetSize().GetHeight()-m_BulletHeight-m_BorderSize;
    framebottom.bottom=framebottom.top+m_BorderSize;
    g_xpStyle.DrawThemeBackground(m_hTheme, hDCMem,9, 1, &framebottom, 0);
    //draw the control box
    //close button
    POINT point;
    SIZE size;
    RECT close,max,min;
    g_xpStyle.GetThemePosition(m_hTheme,18,1,3401,&point);
    g_xpStyle.GetThemePartSize(m_hTheme,hDCMem,18,1,NULL,TS_TRUE,&size);
    close.left=titlebar.right+point.x;
    close.right=close.left+size.cx;
    close.top=titlebar.top+point.y;
    close.bottom=close.top+size.cy;
    g_xpStyle.DrawThemeBackground(m_hTheme, hDCMem,18, 1, &close, 0);
    //max button
    g_xpStyle.GetThemePosition(m_hTheme,17,1,3401,&point);
    g_xpStyle.GetThemePartSize(m_hTheme,hDCMem,17,1,NULL,TS_TRUE,&size);
    max.left=titlebar.right+point.x;
    max.right=max.left+size.cx;
    max.top=titlebar.top+point.y;
    max.bottom=max.top+size.cy;
    g_xpStyle.DrawThemeBackground(m_hTheme, hDCMem,17, 1, &max, 0);
    //min button
    g_xpStyle.GetThemePosition(m_hTheme,15,1,3401,&point);
    g_xpStyle.GetThemePartSize(m_hTheme,hDCMem,15,1,NULL,TS_TRUE,&size);
    min.left=titlebar.right+point.x;
    min.right=min.left+size.cx;
    min.top=titlebar.top+point.y;
    min.bottom=min.top+size.cy;
    g_xpStyle.DrawThemeBackground(m_hTheme, hDCMem,15, 1, &min, 0);
}

micros
Super wx Problem Solver
Super wx Problem Solver
Posts: 317
Joined: Sat Mar 18, 2006 10:41 am
Location: Ustek, Bohemia

Post by micros » Sun Apr 02, 2006 4:12 pm

The documentation says that you must create a wxPaintDC object in paint handler, even if you're not going to use it. And then I suggest you use it, because mixing wx and gdi stuff looks ugly :)

http://wxwidgets.org/manuals/2.6.3/wx_w ... paintevent

The reason you need a wxPaintDC instance is that its constructor calls BeginPaint, which clears the update area. Windows keep DIRTY flag for each window, and if there are no messages in the queue and the flag is set, WM_PAINT is generated. If you don't clear the flag (i.e. don't call BeginPaint, i.e. don't create a wxPaintDC), the paint message is generated again and again...

death_ghast
Knows some wx things
Knows some wx things
Posts: 26
Joined: Thu Jan 26, 2006 1:27 pm

Post by death_ghast » Sun Apr 02, 2006 6:06 pm

Well creating the wxPaintDC is the first thing i do (look again on the code). I saw that too about treating paint events under windows, and i used that warning.

And in order to draw using theme parts under windows XP i need to use winapi. there's no other way. And since i was going to use winapi for that i used it for all the painting.

If i do part of the painting in the paint DC, and the other by getting a window handle and using winapi, the part drawn with winapi goes black or something. So this was my only solution. Painting with winapi only.

Also i saw a sample code about implementing a new BitmapButton ...by Jamie. And i wonder how can i plug into

MSWOnDraw(WXDRAWITEMSTRUCT *item);
and
WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam);

It seems that in his example, the ButtonBase already has these 2 methods plugged in and he just overwrites them. I tried too defining them and drawing something in them, but they are never called. So how do i connect them so that they are called.
I badly need MSWWindowProc, so i can treat the WM_THEMECHANGED message.

micros
Super wx Problem Solver
Super wx Problem Solver
Posts: 317
Joined: Sat Mar 18, 2006 10:41 am
Location: Ustek, Bohemia

Post by micros » Sun Apr 02, 2006 9:18 pm

Sorry, I wonder how could I miss that line, it's even in bold type! Now it seems you're not calling ReleaseDC (hope I haven't missed something again :)). I suppose you're absolutely sure MemoryHDC, CompatibleBitmap, SelectInHDC release the objects, too.

I couldn't find the sample you're talking about. I was just browsing src/msw and think that adding BS_OWNERDRAWN style could help MSWOnDraw be called. The style is set from wxButton::MakeOwnerDrawn(), a private method. But it's called from SetForeground/BackgroundColor, so if your owner-drawn button doesn't care about these colors, calling it might work... dunno.

MSWWindowProc is defined in wxWindow already, so why that's not called is a mystery to me.

death_ghast
Knows some wx things
Knows some wx things
Posts: 26
Joined: Thu Jan 26, 2006 1:27 pm

Post by death_ghast » Sun Apr 02, 2006 11:56 pm

Here WAS Jamie's sample:
http://forums.wxwidgets.org/viewtopic.p ... ed&start=0
It seems it's removed now...

Will try with BS_OWNERDRAWN as soon as i find the header to include for that :).

micros
Super wx Problem Solver
Super wx Problem Solver
Posts: 317
Joined: Sat Mar 18, 2006 10:41 am
Location: Ustek, Bohemia

Post by micros » Mon Apr 03, 2006 8:28 am

Oh sorry, it's BS_OWNERDRAW == 11 defined in winuser.h, included from windows.h. I don't know how you could add it directly, but I suggested that SetForegroundColor does. (just searched for it in src/)

And the OnPaint, does it work with ReleaseDC?

death_ghast
Knows some wx things
Knows some wx things
Posts: 26
Joined: Thu Jan 26, 2006 1:27 pm

Post by death_ghast » Wed Apr 05, 2006 9:03 pm

ok...i got it working...
The initial problem was due to the fact that i did not call ReleaseDC after getting it like this:

Code: Select all

HDC   hDC      = GetWindowDC((HWND)GetHandle());
MSWOnDraw still can't be called, but i managed without it.
MSWWindowProc is working though.

Just setting BS_OWNERDRAWN does not lead to automatic call of MSWOnDraw.

Thanks for the help.

Sof_T
Can't get richer than this
Can't get richer than this
Posts: 864
Joined: Thu Jul 28, 2005 9:48 pm
Location: New Forest, United Kingdom
Contact:

Post by Sof_T » Thu Apr 06, 2006 7:34 pm

Hi DeathGhast

I have just made a breakthrough on the control I was working on. The result is still windows only though I would love to implement this on other platforms. Please try this control and see if it does what you want.


Known Issues:

Only works for Windows.
Title bar is drawn in unfocused colour.
If this control is the only child of a wxFrame it will be maximised to fill the frame, this is default wxWidgets behaviour.

Plus Points:

Can be a child of any window.
Is clipped by parent window.

Code:

Code: Select all

//childfrm.h

/////////////////////////////////////////////////////////////////////////////
// Name:        wx/msw/childfrm.h
// Purpose:     wxChildFrame class
// Author:      Sof.T
// Modified by:
// Created:     05/03/06
// RCS-ID:      
// Copyright:   (c) Sof.T
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#ifndef _WX_CHILD_FRAME_H_
#define _WX_CHILD_FRAME_H_

#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
    #pragma interface "frame.h"
#endif

#include <wx/frame.h>

// ---------------------------------------------------------------------------
// wxMDIChildFrame
// ---------------------------------------------------------------------------

class WXDLLEXPORT wxChildFrame : public wxFrame
{
public:
    wxChildFrame() { Init(); }
    wxChildFrame(wxWindow *parent,
                    wxWindowID id,
                    const wxString& title,
                    const wxPoint& pos = wxDefaultPosition,
                    const wxSize& size = wxDefaultSize,
                    long style = wxDEFAULT_FRAME_STYLE|WS_CHILD,
                    const wxString& name = wxFrameNameStr)
    {
        Init();

        Create(parent, id, title, pos, size, style, name);
    }

    ~wxChildFrame();

    bool Create(wxWindow *parent,
                wxWindowID id,
                const wxString& title,
                const wxPoint& pos = wxDefaultPosition,
                const wxSize& size = wxDefaultSize,
                long style = wxDEFAULT_FRAME_STYLE|WS_CHILD,
                const wxString& name = wxFrameNameStr);

    virtual bool IsTopLevel() const { return false; }


    void OnIdle(wxIdleEvent& event);

    virtual bool Show(bool show = true
protected:
    // common part of all ctors
    void Init();

private:
    bool m_needsInitialShow; // Show must be called in idle time after Creation
    bool m_needsResize; // flag which tells us to artificially resize the frame
    //virtual void DetachMenuBar() ;

    DECLARE_EVENT_TABLE()
    DECLARE_DYNAMIC_CLASS(wxChildFrame)
};
#endif

//childfrm.cpp

#include "childfrm.h"
#ifndef WX_PRECOMP
    #include "wx/setup.h"
    #include "wx/frame.h"
    #include "wx/menu.h"
    #include "wx/app.h"
    #include "wx/utils.h"
    #include "wx/dialog.h"
    #if wxUSE_STATUSBAR
        #include "wx/statusbr.h"
    #endif
    #include "wx/settings.h"
    #include "wx/intl.h"
    #include "wx/log.h"
#endif

#include "wx/stockitem.h"
#include "wx/mdi.h"
#include "wx/msw/private.h"

#if wxUSE_STATUSBAR && wxUSE_NATIVE_STATUSBAR
    #include "wx/msw/statbr95.h"
#endif

#if wxUSE_TOOLBAR
    #include "wx/toolbar.h"
#endif // wxUSE_TOOLBAR

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

// ---------------------------------------------------------------------------
// wxWin macros
// ---------------------------------------------------------------------------

IMPLEMENT_DYNAMIC_CLASS(wxChildFrame, wxFrame)

BEGIN_EVENT_TABLE(wxChildFrame, wxFrame)
    EVT_IDLE(wxChildFrame::OnIdle)
END_EVENT_TABLE()
// ===========================================================================
// wxChildFrame
// ===========================================================================

void wxChildFrame::Init()
{
    wxFrame::Init();
    m_needsResize = true;
    m_needsInitialShow = true;
}

bool wxChildFrame::Create(wxWindow *parent,
                             wxWindowID id,
                             const wxString& title,
                             const wxPoint& pos,
                             const wxSize& size,
                             long style,
                             const wxString& name)
{
    if(!wxFrame::Create(parent,id,title,pos,size,style,name))
        return false;
    DWORD FrameStyle = GetWindowLong(GetWinHwnd(this),GWL_STYLE);
    FrameStyle = FrameStyle | WS_CHILD;
    SetWindowLong(GetWinHwnd(this),GWL_STYLE,FrameStyle);
    ::SetParent(GetWinHwnd(this),GetWinHwnd(parent));
        //Set default size
        int TempHeight,TempWidth;
        parent->GetClientSize(&TempWidth,&TempHeight);
        SetSize(TempWidth/2,TempHeight/2);
}

wxChildFrame::~wxChildFrame()
{
    // will be destroyed by DestroyChildren() but reset them before calling it
    // to avoid using dangling pointers if a callback comes in the meanwhile
#if wxUSE_TOOLBAR
    m_frameToolBar = NULL;
#endif
#if wxUSE_STATUSBAR
    m_frameStatusBar = NULL;
#endif // wxUSE_STATUSBAR

    DestroyChildren();

    //RemoveWindowMenu(NULL, m_hMenu);

    MSWDestroyWindow();
}

bool wxChildFrame::Show(bool show)
{
    m_needsInitialShow = false;
    return wxFrame::Show(show);
}

void wxChildFrame::OnIdle(wxIdleEvent& event)
{
    // wxMSW prior to 2.5.3 created MDI child frames as visible, which resulted
    // in flicker e.g. when the frame contained controls with non-trivial
    // layout. Since 2.5.3, the frame is created hidden as all other top level
    // windows. In order to maintain backward compatibility, the frame is shown
    // in OnIdle, unless Show(false) was called by the programmer before.
    if ( m_needsInitialShow )
    {
        Show(true);
    }

    // MDI child frames get their WM_SIZE when they're constructed but at this
    // moment they don't have any children yet so all child windows will be
    // positioned incorrectly when they are added later - to fix this, we
    // generate an artificial size event here
    if ( m_needsResize )
    {
        m_needsResize = false; // avoid any possibility of recursion

        SendSizeEvent();
    }

    event.Skip();
}
This has been roughly converted from wxMDIChild and still contains some references to MDI.

If anyone can help to tidy this and convert it for other platforms, please help.

Sof.T
The home of Sof.T http://www.sof-t.site88.net/
Author of Programming with wxDevC++
http://sourceforge.net/projects/wxdevcpp-book/

death_ghast
Knows some wx things
Knows some wx things
Posts: 26
Joined: Thu Jan 26, 2006 1:27 pm

Post by death_ghast » Thu Apr 06, 2006 11:40 pm

Hi,

I tested your code, and i have a child window with inactive titlebar as you described. There is also a weird scrollbar on the bottom of the child window. I added a panel to the child, but the scrollbar did not disappear.

I have completed my designer form. I manually draw it. For non themed situations (9x/2k/xp non themed) it's very simple and the api is there for anything you need. But the uxtheme api is increddibly buggy and many functions just do not do anything, others return error all the time and some don't mind the parameters you pass them :).
The uxtheme dll is loaded dinamically, so it works on any windows platform.

Anyway...it is fully resizable (it has resizing handles), having the possibility of caption text, and icon change, and also of control box customization. It can have an image background (auto resized or not), and clips children on redraw. Can be added to any panel.
If you want i can post it in the code dump...(it will be posted there eventually...it still needs commenting and some more get/set methods)

Sof_T
Can't get richer than this
Can't get richer than this
Posts: 864
Joined: Thu Jul 28, 2005 9:48 pm
Location: New Forest, United Kingdom
Contact:

Post by Sof_T » Fri Apr 07, 2006 6:17 am

If you want i can post it in the code dump...(it will be posted there eventually...it still needs commenting and some more get/set methods)
It would be good to see. I guess this could be extended to other platforms quite easily.

Sof.T
The home of Sof.T http://www.sof-t.site88.net/
Author of Programming with wxDevC++
http://sourceforge.net/projects/wxdevcpp-book/

Post Reply