I am writing an application using wxWidgets and C++, this is a test program to show the problem:
#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/graphics.h>
#include <wx/timer.h>
#include <wx/process.h>
// globals
wxString bkgPath = "C:/Dati/workspaces/codeblocks/ShapedButton/wallpaper.png";
wxString imgPath = "C:/Dati/workspaces/codeblocks/ShapedButton/Sample5.png";
class ImageButton : public wxControl
{
public:
ImageButton(wxWindow* parent, wxWindowID id, const wxBitmap& bitmap,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxControlNameStr)
: wxControl(parent, id, pos, size, style, validator, name),
m_bitmap(bitmap),
m_isDoubleClick(false),
m_isDragging(false),
m_lastMousePos(wxDefaultPosition),
m_capturedMouse(false)
{
SetSize(bitmap.GetWidth(), bitmap.GetHeight());
Bind(wxEVT_PAINT, &ImageButton::OnPaint, this);
Bind(wxEVT_LEFT_DOWN, &ImageButton::OnMouseDown, this);
Bind(wxEVT_LEFT_UP, &ImageButton::OnMouseUp, this);
#if 0
Bind(wxEVT_LEFT_DCLICK, &ImageButton::OnMouseDoubleClick, this);
#endif
Bind(wxEVT_MOTION, &ImageButton::OnMouseMove, this);
m_timer.Bind(wxEVT_TIMER, &ImageButton::OnTimer, this);
}
//------------------------------------------------------------------------
void OnPaint(wxPaintEvent& event)
{
wxBufferedPaintDC dc(this);
dc.DrawBitmap(m_bitmap, 0, 0, true);
}
//------------------------------------------------------------------------
void OnMouseDown(wxMouseEvent& event)
{
// Handle mouse down event
if(!m_capturedMouse && !m_isDragging) {
CaptureMouse();
m_isDragging = true;
}
m_lastMousePos = event.GetPosition();
}
//------------------------------------------------------------------------
void OnMouseUp(wxMouseEvent& event)
{
// Handle mouse up event
m_isDoubleClick = false;
if(m_capturedMouse) {
ReleaseMouse();
m_isDragging = false;
m_capturedMouse = false;
}
}
//------------------------------------------------------------------------
void OnMouseDoubleClick(wxMouseEvent& event)
{
// Handle double-click event
m_isDoubleClick = true;
wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
evt.SetEventObject(this);
ProcessEvent(evt);
// Launch cmd.exe
wxExecute("cmd.exe", wxEXEC_ASYNC);
}
//------------------------------------------------------------------------
void OnMouseMove(wxMouseEvent& event)
{
if (m_isDragging && event.Dragging())
{
// Get the current button position relative to the parent
wxPoint oldPos = GetPosition();
// Calculate the new position based on the mouse movement relative to the button's last position
wxPoint newPos = oldPos + (event.GetPosition() - m_lastMousePos);
// Invalidate the background at the old position of the button
GetParent()->RefreshRect(wxRect(oldPos, GetSize()), true);
// Move the button to the new position
Move(newPos);
// Invalidate the new position to repaint the background under the button's new location
GetParent()->RefreshRect(wxRect(newPos, GetSize()), true);
// Update the parent panel to ensure the background refresh is immediate
GetParent()->Update();
// Update the last mouse position for the next drag event
m_lastMousePos = event.GetPosition();
}
// Allow further processing of the event
// event.Skip();
}
void OnTimer(wxTimerEvent& event)
{
if (!m_isDoubleClick)
{
wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
evt.SetEventObject(this);
ProcessEvent(evt);
}
}
//------------------------------------------------------------------------
private:
wxBitmap m_bitmap;
wxTimer m_timer;
bool m_isDoubleClick;
bool m_isDragging;
wxPoint m_lastMousePos;
bool m_capturedMouse;
};
//------------------------------------------------------------------------
//------------------------------------------------------------------------
class ImagePanel : public wxPanel
{
public:
ImagePanel(wxWindow* parent, const wxBitmap& background)
: wxPanel(parent), m_background(background)
{
Bind(wxEVT_PAINT, &ImagePanel::OnPaint, this);
Bind(wxEVT_SIZE, &ImagePanel::OnSize, this);
}
void OnPaint(wxPaintEvent& event)
{
wxBufferedPaintDC dc(this);
wxSize panelSize = GetSize();
// First, draw the entire background, scaled to fit the panel size
wxImage scaledImage = m_background.ConvertToImage().Rescale(panelSize.GetWidth(), panelSize.GetHeight());
wxBitmap scaledBitmap(scaledImage);
dc.DrawBitmap(scaledBitmap, 0, 0, true);
// Now iterate over the children and handle ImageButton specifically
for (wxWindowList::Node* node = GetChildren().GetFirst(); node; node = node->GetNext())
{
wxWindow* child = node->GetData();
// Check if the child is an ImageButton
ImageButton* button = dynamic_cast<ImageButton*>(child);
if (button)
{
wxPoint childPos = child->GetPosition();
wxSize childSize = child->GetSize();
// Define the area of the background that needs to be redrawn beneath the child
wxRect backgroundRect(childPos, childSize);
// Ensure the backgroundRect is clipped to the panel bounds
backgroundRect.Intersect(wxRect(0, 0, panelSize.GetWidth(), panelSize.GetHeight()));
// Extract the part of the background image to be drawn behind the button
wxImage subImage = scaledImage.GetSubImage(backgroundRect);
// Draw the extracted background part at the correct position
dc.DrawBitmap(wxBitmap(subImage), childPos.x, childPos.y, true);
}
}
}
void OnSize(wxSizeEvent& event)
{
Refresh();
}
private:
wxBitmap m_background;
};
// Example usage
class MyFrame : public wxFrame
{
public:
MyFrame() : wxFrame(NULL, wxID_ANY, "Image Button Example", wxPoint(50, 50), wxSize(600, 400))
{
wxBitmap background(bkgPath, wxBITMAP_TYPE_PNG);
ImagePanel* panel = new ImagePanel(this, background);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(panel, 1, wxEXPAND);
SetSizer(sizer);
wxBitmap buttonBitmap(imgPath, wxBITMAP_TYPE_PNG);
ImageButton* button = new ImageButton(panel, wxID_ANY, buttonBitmap, wxPoint(100, 100));
}
};
//------------------------------------------------------------------------
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
wxInitAllImageHandlers();
// Initialize image handlers
if(!wxImage::FindHandler(wxBITMAP_TYPE_PNG)) {
wxImage::AddHandler(new wxPNGHandler());
}
if(!wxImage::FindHandler(wxBITMAP_TYPE_JPEG)) {
wxImage::AddHandler(new wxJPEGHandler());
}
MyFrame* frame = new MyFrame();
frame->Show(true);
return true;
}
};
//------------------------------------------------------------------------
wxIMPLEMENT_APP(MyApp);
//------------------------------------------------------------------------
//------------------------------------------------------------------------
I have a button that can have its shape defined by an image and it has to be draggable around over a window that has an image as background on a panel extended to all the window that is a wxFrame descendant. When the program starts puts the button over the panel and the transparent part is painted with the background but starting from (0,0) of the panel independently by the button position and when I drag it the background of the button does not change remaining always the same. I am testing it on Windows 10, any suggestion or the solution?
This is my actual result:
…and this is I would like to have: