Merge branch 'autoscroll-drag' of github.com:wsu-cb/wxWidgets
Some checks failed
Unix builds / Ubuntu 18.04 wxGTK 3 compatible 3.0 (push) Has been cancelled
Unix builds / Ubuntu 24.04 wxGTK ASAN not compatible (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxGTK UTF-8 (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxQt (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxX11 (push) Has been cancelled
Unix builds / Ubuntu 20.04 wxGTK 3 with clang (push) Has been cancelled
Unix builds / Ubuntu 22.04 wxGTK with wx containers (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxDFB (push) Has been cancelled
Unix builds / Ubuntu 24.04 wxGTK UBSAN (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxGTK 3 static with gcc 4.8 (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxGTK 2 (push) Has been cancelled
CMake builds / Ubuntu 22.04 wxGTK 3 (push) Has been cancelled
CMake builds / macOS latest wxGTK 3 Unix Makefiles (push) Has been cancelled
CMake builds / MSW/MSVC wxMSW (push) Has been cancelled
CMake builds / MSW/Clang wxMSW (push) Has been cancelled
CMake builds / macOS latest wxOSX Ninja (push) Has been cancelled
CMake builds / macOS 14 wxOSX Xcode (push) Has been cancelled
CMake builds / macOS 14 wxIOS (push) Has been cancelled
CMake builds / MSW/MSVC wxQt 5.15 (push) Has been cancelled
CMake builds / MSW/MSVC wxQt 6.10 (push) Has been cancelled
Mac builds / wxMac ARM ASAN not compatible (push) Has been cancelled
Mac builds / wxMac Universal C++14 (push) Has been cancelled
Mac builds / wxiOS Simulator on Silicon Mac (push) Has been cancelled
Mac builds / wxiOS (push) Has been cancelled
Mac builds / wxMac Intel C++17 (push) Has been cancelled
Mac Xcode builds / iOS Simulator static (push) Has been cancelled
Mac Xcode builds / macOS dynamic Release (push) Has been cancelled
Mac Xcode builds / iOS static Debug (push) Has been cancelled
MSW builds / wxMSW vs2022 DLL Debug x64 (push) Has been cancelled
MSW builds / wxMSW vs2022 DLL Release x64 (push) Has been cancelled
MSW builds / wxMSW vs2022 Debug Win32 (push) Has been cancelled
MSW builds / wxMSW vs2022 Release arm64 (push) Has been cancelled
MSW builds / wxMSW vs2026 DLL Release x64 (push) Has been cancelled
MSW cross-builds / wxMSW 64 bits not compatible (push) Has been cancelled
MSW cross-builds / wxMSW/Univ (push) Has been cancelled
MSW cross-builds / wxMSW 32 bits (push) Has been cancelled
Code Checks / Check Spelling (push) Has been cancelled
Code Checks / Check Whitespace (push) Has been cancelled
Code Checks / Check Mixed EOL (push) Has been cancelled
Code Checks / Check C++ Style (push) Has been cancelled
Code Checks / Check All Headers In allheaders.h (push) Has been cancelled
Update Documentation / Update Online Documentation (push) Has been cancelled
No Response / no-response (push) Has been cancelled

wxScrolled<>: documentation and sample improvements.

See #26304.
This commit is contained in:
Vadim Zeitlin
2026-03-19 02:24:57 +01:00
4 changed files with 144 additions and 100 deletions

View File

@@ -168,7 +168,7 @@ public:
wxEventType& evtHorzScroll,
wxEventType& evtVertScroll) const;
// wxWidgets <= 3.3.1 mostly restricted autoscroll to the
// wxWidgets <= 3.3.2 mostly restricted autoscroll to the
// window holding the mouse capture. However, when dragging
// objects between windows, the destination window should be
// the window that is scrolling (to allow positioning the

View File

@@ -116,6 +116,18 @@ enum wxScrollbarVisibility
To disable autoscrolling completely, call DisableAutoScrollOutside()
without calling EnableAutoScrollInside().
wxWidgets <= 3.3.2 mostly restricted autoscroll to the window holding the
mouse capture. However, when dragging objects between windows, the
destination window, which may or may not be the capturing window, should be
the window that is scrolling (to allow positioning the dragged object).
To support this, as of wxWidgets 3.3.3, a window can call
EnableAutoscrollWithoutCapture() when processing a drag-enter, and
DisableAutoscrollWithoutCapture() when processing a drag-exit or drag-drop.
(If a window supports dragging content to another window, it should use
EnableAutoScrollInside() and DisableAutoScrollOutside() to avoid
autoscrolling while a destination window is also autoscrolling.) See the
shape frame portion of the @sample{dnd} for an example of dragging
from one window to another.
@beginStyleTable
@style{wxHSCROLL}
@@ -541,6 +553,23 @@ public:
*/
void Scroll(const wxPoint& pt);
/**
Set this window to autoscroll even if it has not captured the mouse
(assuming the mouse cursor is in its autoscroll zone). This is
intended to be called on drag-enter to support drag and drop between
windows.
@since 3.3.3
*/
void EnableAutoscrollWithoutCapture();
/**
Undo EnableAutoscrollWithoutCapture(). This is intended to be called on
drag-exit and drag-drop when supporting drag and drop between windows.
@since 3.3.3
*/
void DisableAutoscrollWithoutCapture();
/**
Set the horizontal and vertical scrolling increment only. See the
pixelsPerUnit parameter in SetScrollbars().

View File

@@ -84,10 +84,14 @@ must have been registered) and their values.
drop source will delete the corresponding data - which would lead to
data loss if you didn't paste it properly.
If wxScrolled<>::EnableAutoscrollWithoutCapture() was called previously,
wxScrolled<>::DisableAutoscrollWithoutCapture() should be called here.
c) OnEnter()
called when the mouse enters the window: you might use this function to
give some additional visual feedback.
give some additional visual feedback and/or call
wxScrolled<>::EnableAutoscrollWithoutCapture().
d) OnLeave()
@@ -140,7 +144,7 @@ blocking function which enters into its own message loop and may return after
an arbitrarily long time interval. During it, the QueryContinueDrag() is called
whenever the mouse or keyboard state changes. The default behaviour is quite
reasonable for 99% of cases: the drag operation is cancelled if the <Esc> key
is preessed and the drop is initiated if the mouse button is released.
is pressed and the drop is initiated if the mouse button is released.
b) After the end of d&d
@@ -160,6 +164,5 @@ wxDropSource::Move code.
@@@@ TODO: support tymed != TYMED_HGLOBAL;
better support of CF_BMP, CF_METAFILE
scrolling support!! (how?)
sample demonstrating use of user-defined formats
sample which really does something useful

View File

@@ -32,6 +32,7 @@
#endif
#if wxUSE_DRAG_AND_DROP
class DnDShapeCanvas;
// ----------------------------------------------------------------------------
// Derive two simple classes which just put in the listbox the strings (text or
@@ -381,17 +382,14 @@ public:
// well, it's a bit difficult to describe a triangle by position and
// size, but we're not doing geometry here, do we? ;-)
wxPoint p1(m_pos);
wxPoint p2(m_pos.x + m_size.x, m_pos.y);
wxPoint p3(m_pos.x, m_pos.y + m_size.y);
wxPoint p[3] = {
{ m_pos },
{ m_pos.x + m_size.x, m_pos.y },
{ m_pos.x, m_pos.y + m_size.y },
};
dc.DrawLine(p1, p2);
dc.DrawLine(p2, p3);
dc.DrawLine(p3, p1);
//works in multicolor modes; on GTK (at least) will fail in 16-bit color
dc.SetBrush(wxBrush(m_col));
dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
dc.DrawPolygon(3, p);
}
};
@@ -416,18 +414,8 @@ public:
{
DnDShape::Draw(dc);
wxPoint p1(m_pos);
wxPoint p2(p1.x + m_size.x, p1.y);
wxPoint p3(p2.x, p2.y + m_size.y);
wxPoint p4(p1.x, p3.y);
dc.DrawLine(p1, p2);
dc.DrawLine(p2, p3);
dc.DrawLine(p3, p4);
dc.DrawLine(p4, p1);
dc.SetBrush(wxBrush(m_col));
dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
dc.DrawRectangle(m_pos, m_size);
}
};
@@ -452,10 +440,8 @@ public:
{
DnDShape::Draw(dc);
dc.DrawEllipse(m_pos, m_size);
dc.SetBrush(wxBrush(m_col));
dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
dc.DrawEllipse(m_pos, m_size);
}
};
@@ -669,7 +655,7 @@ private:
class DnDShapeDialog : public wxDialog
{
public:
DnDShapeDialog(wxFrame *parent, DnDShape *shape);
DnDShapeDialog(wxWindow *parent, DnDShape *shape);
DnDShape *GetShape() const;
@@ -702,17 +688,34 @@ private:
// A frame for the shapes which can be drag-and-dropped between frames
// ----------------------------------------------------------------------------
class DnDShapeFrame : public wxScrolled<wxFrame>
class DnDShapeFrame : public wxFrame
{
public:
DnDShapeFrame(wxFrame *parent);
~DnDShapeFrame();
protected:
// The menu and updateUI events are sent to the frame, but
// I think implementing their handlers is more natural in
// the canvas. Use TryBefore() to forward those events to
// the canvas.
bool TryBefore(wxEvent& event) override;
private:
DnDShapeCanvas *m_canvas = nullptr;
};
// ----------------------------------------------------------------------------
// A canvas for the shapes which can be drag-and-dropped between frames
// ----------------------------------------------------------------------------
class DnDShapeCanvas : public wxScrolledCanvas
{
public:
DnDShapeCanvas(DnDShapeFrame *parent);
DnDShapeFrame *GetParent();
void SetShape(DnDShape *shape);
virtual bool SetShape(const wxRegion &region)
{
return wxFrame::SetShape( region );
}
// callbacks
void OnNewShape(wxCommandEvent& event);
@@ -730,9 +733,9 @@ public:
void OnDrop(wxCoord x, wxCoord y, DnDShape *shape);
private:
DnDShape *m_shape;
std::unique_ptr<DnDShape> m_shape;
static DnDShapeFrame *ms_lastDropTarget;
static DnDShapeCanvas *ms_lastDropTarget;
wxDECLARE_EVENT_TABLE();
};
@@ -744,31 +747,31 @@ private:
class DnDShapeDropTarget : public wxDropTarget
{
public:
DnDShapeDropTarget(DnDShapeFrame *frame)
DnDShapeDropTarget(DnDShapeCanvas *canvas)
: wxDropTarget(new DnDShapeDataObject)
{
m_frame = frame;
m_canvas = canvas;
}
// override base class (pure) virtuals
virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def) override
{
#if wxUSE_STATUSBAR
m_frame->SetStatusText("Mouse entered the frame");
m_canvas->GetParent()->SetStatusText("Mouse entered the frame");
#endif // wxUSE_STATUSBAR
m_frame->EnableAutoscrollWithoutCapture();
m_canvas->EnableAutoscrollWithoutCapture();
return OnDragOver(x, y, def);
}
virtual void OnLeave() override
{
#if wxUSE_STATUSBAR
m_frame->SetStatusText("Mouse left the frame");
m_canvas->GetParent()->SetStatusText("Mouse left the frame");
#endif // wxUSE_STATUSBAR
m_frame->DisableAutoscrollWithoutCapture();
m_canvas->DisableAutoscrollWithoutCapture();
}
virtual bool OnDrop(wxCoord x, wxCoord y) override
{
m_frame->DisableAutoscrollWithoutCapture();
m_canvas->DisableAutoscrollWithoutCapture();
return wxDropTarget::OnDrop(x, y);
}
virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def) override
@@ -780,14 +783,14 @@ public:
return wxDragNone;
}
m_frame->OnDrop(x, y,
m_canvas->OnDrop(x, y,
((DnDShapeDataObject *)GetDataObject())->GetShape());
return def;
}
private:
DnDShapeFrame *m_frame;
DnDShapeCanvas *m_canvas;
};
#endif // wxUSE_DRAG_AND_DROP
@@ -867,20 +870,20 @@ wxEND_EVENT_TABLE()
#if wxUSE_DRAG_AND_DROP
wxBEGIN_EVENT_TABLE(DnDShapeFrame, wxFrame)
EVT_MENU(Menu_Shape_New, DnDShapeFrame::OnNewShape)
EVT_MENU(Menu_Shape_Edit, DnDShapeFrame::OnEditShape)
EVT_MENU(Menu_Shape_Clear, DnDShapeFrame::OnClearShape)
wxBEGIN_EVENT_TABLE(DnDShapeCanvas, wxScrolledCanvas)
EVT_MENU(Menu_Shape_New, DnDShapeCanvas::OnNewShape)
EVT_MENU(Menu_Shape_Edit, DnDShapeCanvas::OnEditShape)
EVT_MENU(Menu_Shape_Clear, DnDShapeCanvas::OnClearShape)
EVT_MENU(Menu_ShapeClipboard_Copy, DnDShapeFrame::OnCopyShape)
EVT_MENU(Menu_ShapeClipboard_Paste, DnDShapeFrame::OnPasteShape)
EVT_MENU(Menu_ShapeClipboard_Copy, DnDShapeCanvas::OnCopyShape)
EVT_MENU(Menu_ShapeClipboard_Paste, DnDShapeCanvas::OnPasteShape)
EVT_UPDATE_UI(Menu_ShapeClipboard_Copy, DnDShapeFrame::OnUpdateUICopy)
EVT_UPDATE_UI(Menu_ShapeClipboard_Paste, DnDShapeFrame::OnUpdateUIPaste)
EVT_UPDATE_UI(Menu_ShapeClipboard_Copy, DnDShapeCanvas::OnUpdateUICopy)
EVT_UPDATE_UI(Menu_ShapeClipboard_Paste, DnDShapeCanvas::OnUpdateUIPaste)
EVT_LEFT_DOWN(DnDShapeFrame::OnDrag)
EVT_LEFT_DOWN(DnDShapeCanvas::OnDrag)
EVT_PAINT(DnDShapeFrame::OnPaint)
EVT_PAINT(DnDShapeCanvas::OnPaint)
wxEND_EVENT_TABLE()
wxBEGIN_EVENT_TABLE(DnDShapeDialog, wxDialog)
@@ -1596,7 +1599,7 @@ bool DnDFile::OnDropFiles(wxCoord, wxCoord, const wxArrayString& filenames)
// DnDShapeDialog
// ----------------------------------------------------------------------------
DnDShapeDialog::DnDShapeDialog(wxFrame *parent, DnDShape *shape)
DnDShapeDialog::DnDShapeDialog(wxWindow *parent, DnDShape *shape)
:wxDialog( parent, 6001, "Choose Shape", wxPoint( 10, 10 ),
wxSize( 40, 40 ),
wxDEFAULT_DIALOG_STYLE | wxRAISED_BORDER | wxRESIZE_BORDER )
@@ -1741,28 +1744,14 @@ void DnDShapeDialog::OnColour(wxCommandEvent& WXUNUSED(event))
// DnDShapeFrame
// ----------------------------------------------------------------------------
bool wxCreateScrolled(wxFrame* self,
wxWindow *parent, wxWindowID winid,
const wxPoint& WXUNUSED(pos), const wxSize& WXUNUSED(size),
long WXUNUSED(style), const wxString& name)
{
return self->Create(parent, winid, name);
}
DnDShapeFrame *DnDShapeFrame::ms_lastDropTarget = nullptr;
DnDShapeFrame::DnDShapeFrame(wxFrame *parent)
: wxScrolled<wxFrame>(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxScrolledWindowStyle, "Shape Frame")
: wxFrame(parent, wxID_ANY, "Shape Frame")
{
#if wxUSE_STATUSBAR
CreateStatusBar();
#endif // wxUSE_STATUSBAR
// this is completely arbitrary and is done just for illustration purposes
SetVirtualSize(1000, 1000);
SetScrollRate(20, 20);
EnableAutoScrollInside(20);
DisableAutoScrollOutside();
m_canvas = new DnDShapeCanvas(this);
wxMenu *menuShape = new wxMenu;
menuShape->Append(Menu_Shape_New, "&New default shape\tCtrl-S");
@@ -1783,30 +1772,53 @@ DnDShapeFrame::DnDShapeFrame(wxFrame *parent)
#if wxUSE_STATUSBAR
SetStatusText("Press Ctrl-S to create a new shape");
#endif // wxUSE_STATUSBAR
}
bool DnDShapeFrame::TryBefore(wxEvent& event)
{
if (event.GetEventType() == wxEVT_MENU ||
event.GetEventType() == wxEVT_UPDATE_UI)
{
wxPropagationDisabler disablePropagation(event);
if (m_canvas && m_canvas->ProcessWindowEvent(event))
{
return true;
}
}
return wxFrame::TryBefore(event);
}
DnDShapeCanvas *DnDShapeCanvas::ms_lastDropTarget = nullptr;
DnDShapeCanvas::DnDShapeCanvas(DnDShapeFrame *parent)
: wxScrolledCanvas(parent)
{
// this is completely arbitrary and is done just for illustration purposes
SetVirtualSize(1000, 1000);
SetScrollRate(20, 20);
EnableAutoScrollInside(20);
DisableAutoScrollOutside();
SetDropTarget(new DnDShapeDropTarget(this));
m_shape = nullptr;
SetBackgroundColour(*wxWHITE);
}
DnDShapeFrame::~DnDShapeFrame()
DnDShapeFrame *DnDShapeCanvas::GetParent()
{
if (m_shape)
delete m_shape;
wxWindow *parent = wxScrolledCanvas::GetParent();
return wxCheckCast<DnDShapeFrame>(parent);
}
void DnDShapeFrame::SetShape(DnDShape *shape)
void DnDShapeCanvas::SetShape(DnDShape *shape)
{
if (m_shape)
delete m_shape;
m_shape = shape;
m_shape.reset(shape);
Refresh();
}
// callbacks
void DnDShapeFrame::OnDrag(wxMouseEvent& event)
void DnDShapeCanvas::OnDrag(wxMouseEvent& event)
{
if ( !m_shape )
{
@@ -1816,7 +1828,7 @@ void DnDShapeFrame::OnDrag(wxMouseEvent& event)
}
// start drag operation
DnDShapeDataObject shapeData(m_shape);
DnDShapeDataObject shapeData(m_shape.get());
wxDropSource source(shapeData, this);
wxString msg;
@@ -1829,7 +1841,7 @@ void DnDShapeFrame::OnDrag(wxMouseEvent& event)
case wxDragNone:
#if wxUSE_STATUSBAR
SetStatusText("Nothing happened");
GetParent()->SetStatusText("Nothing happened");
#endif // wxUSE_STATUSBAR
break;
@@ -1848,7 +1860,7 @@ void DnDShapeFrame::OnDrag(wxMouseEvent& event)
case wxDragCancel:
#if wxUSE_STATUSBAR
SetStatusText("Drag and drop operation cancelled");
GetParent()->SetStatusText("Drag and drop operation cancelled");
#endif // wxUSE_STATUSBAR
break;
}
@@ -1856,13 +1868,13 @@ void DnDShapeFrame::OnDrag(wxMouseEvent& event)
if (msg.length() )
{
#if wxUSE_STATUSBAR
SetStatusText(wxString("Shape successfully ") + msg);
GetParent()->SetStatusText(wxString("Shape successfully ") + msg);
#endif // wxUSE_STATUSBAR
}
//else: status text already set
}
void DnDShapeFrame::OnDrop(wxCoord x, wxCoord y, DnDShape *shape)
void DnDShapeCanvas::OnDrop(wxCoord x, wxCoord y, DnDShape *shape)
{
ms_lastDropTarget = this;
@@ -1872,16 +1884,16 @@ void DnDShapeFrame::OnDrop(wxCoord x, wxCoord y, DnDShape *shape)
#if wxUSE_STATUSBAR
wxString s;
s.Printf("Shape dropped at (%d, %d)", pt.x, pt.y);
SetStatusText(s);
GetParent()->SetStatusText(s);
#endif // wxUSE_STATUSBAR
shape->Move(pt);
SetShape(shape);
}
void DnDShapeFrame::OnEditShape(wxCommandEvent& WXUNUSED(event))
void DnDShapeCanvas::OnEditShape(wxCommandEvent& WXUNUSED(event))
{
DnDShapeDialog dlg(this, m_shape);
DnDShapeDialog dlg(this, m_shape.get());
if ( dlg.ShowModal() == wxID_OK )
{
SetShape(dlg.GetShape());
@@ -1889,27 +1901,27 @@ void DnDShapeFrame::OnEditShape(wxCommandEvent& WXUNUSED(event))
#if wxUSE_STATUSBAR
if ( m_shape )
{
SetStatusText("You can now drag the shape to another frame");
GetParent()->SetStatusText("You can now drag the shape to another frame");
}
#endif // wxUSE_STATUSBAR
}
}
void DnDShapeFrame::OnNewShape(wxCommandEvent& WXUNUSED(event))
void DnDShapeCanvas::OnNewShape(wxCommandEvent& WXUNUSED(event))
{
SetShape(new DnDEllipticShape(wxPoint(10, 10), wxSize(80, 60), *wxRED));
#if wxUSE_STATUSBAR
SetStatusText("You can now drag the shape to another frame");
GetParent()->SetStatusText("You can now drag the shape to another frame");
#endif // wxUSE_STATUSBAR
}
void DnDShapeFrame::OnClearShape(wxCommandEvent& WXUNUSED(event))
void DnDShapeCanvas::OnClearShape(wxCommandEvent& WXUNUSED(event))
{
SetShape(nullptr);
}
void DnDShapeFrame::OnCopyShape(wxCommandEvent& WXUNUSED(event))
void DnDShapeCanvas::OnCopyShape(wxCommandEvent& WXUNUSED(event))
{
if ( m_shape )
{
@@ -1921,11 +1933,11 @@ void DnDShapeFrame::OnCopyShape(wxCommandEvent& WXUNUSED(event))
return;
}
wxTheClipboard->AddData(new DnDShapeDataObject(m_shape));
wxTheClipboard->AddData(new DnDShapeDataObject(m_shape.get()));
}
}
void DnDShapeFrame::OnPasteShape(wxCommandEvent& WXUNUSED(event))
void DnDShapeCanvas::OnPasteShape(wxCommandEvent& WXUNUSED(event))
{
wxClipboardLocker clipLocker;
if ( !clipLocker )
@@ -1946,17 +1958,17 @@ void DnDShapeFrame::OnPasteShape(wxCommandEvent& WXUNUSED(event))
}
}
void DnDShapeFrame::OnUpdateUICopy(wxUpdateUIEvent& event)
void DnDShapeCanvas::OnUpdateUICopy(wxUpdateUIEvent& event)
{
event.Enable( m_shape != nullptr );
}
void DnDShapeFrame::OnUpdateUIPaste(wxUpdateUIEvent& event)
void DnDShapeCanvas::OnUpdateUIPaste(wxUpdateUIEvent& event)
{
event.Enable( wxTheClipboard->IsSupported(wxDataFormat(ShapeFormatId())) );
}
void DnDShapeFrame::OnPaint(wxPaintEvent& WXUNUSED(event))
void DnDShapeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
{
wxPaintDC dc(this);
DoPrepareDC(dc);