Whenever you want to make a custom control, the 2 main things you need to do are
1) draw the control using wxDC or
wxGraphicsContext in the controls paint handler.
2) respond to mouse, keyboard, and system events to make the control behave as you want.
Optionally, you might also want to have your control generate some events of its own to allow it to notify your application.
Here's a incomplete sample of a custom slider I have that shows an example of how to do these things. It uses a double between 0 and 1 to store the slider's value, but the code could be modified to use 0 and 255 or an arbitrary max and min instead. Unfortunately, it's completely undocumented, but I don't have time to add comments right now:
header:
class CustomSliderEvent;
wxDECLARE_EVENT(csSLIDER,CustomSliderEvent);
class CustomSliderEvent:public wxCommandEvent
{
public:
CustomSliderEvent(wxEventType commandEventType=wxEVT_NULL, int id=0);
void SetDouble(double);
double GetDouble() const;
private:
double m_double;
};
class CustomSlider:public wxWindow
{
public:
CustomSlider(wxWindow*, wxWindowID, const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0,
const wxString& name = "CustomSlider");
void SetValue(double);
double GetValue() const;
private:
void OnPaint(wxPaintEvent&);
void OnLeftUp(wxMouseEvent&);
void OnLeftDown(wxMouseEvent&);
void OnMouseMotion(wxMouseEvent&);
void OnMouseCaptureLost(wxMouseCaptureLostEvent&);
void OnSize(wxSizeEvent&);
void FinishDrag();
void ProcessMotion(int);
int ValueToPosition(double);
double PostionToValue(int);
void SetClampedPosition(int);
int m_sliderPosition;
bool m_dragging;
int m_thumbWidth;
int m_minPsn;
int m_maxPsn;
double m_value;
};
body:
#include <wx/dcbuffer.h>
#include "customslider.h"
wxDEFINE_EVENT(csSLIDER, CustomSliderEvent);
CustomSliderEvent::CustomSliderEvent(wxEventType commandEventType, int id)
:wxCommandEvent(commandEventType,id)
{
m_double = 0;
}
void CustomSliderEvent::SetDouble(double d)
{
m_double = d;
}
double CustomSliderEvent::GetDouble() const
{
return m_double;
}
CustomSlider::CustomSlider(wxWindow* par, wxWindowID id, const wxPoint& pos,
const wxSize& size, long style, const wxString& name)
:wxWindow(par,id,pos, size , style|wxFULL_REPAINT_ON_RESIZE, name)
{
wxSize emSize = GetTextExtent("M");
int emWidth = emSize.GetWidth();
if ( emWidth % 2 == 0 )
{
++emWidth;
}
SetMinSize(wxSize(-1,emSize.GetHeight()));
SetBackgroundStyle(wxBG_STYLE_PAINT);
Bind(wxEVT_PAINT, &CustomSlider::OnPaint, this);
Bind(wxEVT_LEFT_UP, &CustomSlider::OnLeftUp, this);
Bind(wxEVT_LEFT_DOWN, &CustomSlider::OnLeftDown, this);
Bind(wxEVT_MOUSE_CAPTURE_LOST, &CustomSlider::OnMouseCaptureLost, this);
Bind(wxEVT_SIZE,&CustomSlider::OnSize,this);
m_sliderPosition=0;
m_dragging = false;
m_thumbWidth = emWidth;
m_minPsn=0;
m_maxPsn=0;
m_value = 0.0;
}
void CustomSlider::SetValue(double d)
{
m_value = d;
m_sliderPosition = ValueToPosition(m_value);
Refresh();
}
double CustomSlider::GetValue() const
{
return m_value;
}
static int RoundDoubleToInt(double d)
{
return static_cast<int>(d+.5);
}
int CustomSlider::ValueToPosition(double d)
{
return RoundDoubleToInt((m_maxPsn-m_minPsn)*d)+m_minPsn;
}
double CustomSlider::PostionToValue(int i)
{
return static_cast<double>(i-m_minPsn) /
static_cast<double>(m_maxPsn-m_minPsn);
}
void CustomSlider::SetClampedPosition(int psn)
{
if ( psn < m_minPsn )
{
psn = m_minPsn;
}
if ( psn > m_maxPsn )
{
psn = m_maxPsn;
}
m_sliderPosition = psn;
}
void CustomSlider::OnSize(wxSizeEvent& event)
{
wxSize newSize = event.GetSize();
int width = newSize.GetWidth();
int thumbHalf = m_thumbWidth/2;
m_minPsn = thumbHalf;
m_maxPsn = width-1-thumbHalf;
m_sliderPosition = ValueToPosition(m_value);
event.Skip();
}
void CustomSlider::OnLeftDown(wxMouseEvent& event)
{
m_dragging = true;
Bind(wxEVT_MOTION,&CustomSlider::OnMouseMotion,this);
ProcessMotion(event.GetX());
CaptureMouse();
}
void CustomSlider::OnMouseMotion(wxMouseEvent& event)
{
ProcessMotion(event.GetX());
}
void CustomSlider::OnMouseCaptureLost(wxMouseCaptureLostEvent&)
{
FinishDrag();
Refresh();
}
void CustomSlider::FinishDrag()
{
m_dragging = false;
Unbind(wxEVT_MOTION,&CustomSlider::OnMouseMotion,this);
if ( HasCapture() )
{
ReleaseMouse();
}
}
void CustomSlider::ProcessMotion(int x)
{
double oldValue = m_value;
SetClampedPosition(x);
m_value = PostionToValue(m_sliderPosition);
Refresh();
if ( fabs(m_value-oldValue) > DBL_EPSILON )
{
CustomSliderEvent event(csSLIDER, GetId());
event.SetDouble(m_value);
ProcessWindowEvent(event);
}
}
void CustomSlider::OnLeftUp(wxMouseEvent& event)
{
if ( m_dragging )
{
FinishDrag();
}
ProcessMotion(event.GetX());
}
void CustomSlider::OnPaint(wxPaintEvent&)
{
wxAutoBufferedPaintDC dc(this);
wxRect clientRect = GetClientRect();
dc.Clear();
dc.SetPen(*wxWHITE_PEN);
dc.SetBrush(*wxWHITE_BRUSH);
dc.DrawRectangle(clientRect);
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(*wxBLACK_BRUSH);
wxRect rect(wxPoint(m_sliderPosition-m_thumbWidth/2,0),
wxSize(m_thumbWidth,clientRect.GetHeight()));
dc.DrawRectangle(rect);
}
As I said this a very basic and incomplete control. On windows it looks something like this
But hopefully it gives an example of how to do the drawing and event handling needed.