From 279af795edcfd503468c365b413f59b02d451158 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 25 Mar 2022 01:39:39 +0100 Subject: [PATCH 1/3] Make transparency window in the shaped sample work under MSW Still show transparent window even if IsTransparentBackgroundSupported() returns false, just don't make their background transparent and only use SetTransparent() in this case. In fact, don't try to use transparent background style at all by default and add a separate menu item to do it if wanted. Also add a slider to allow changing opacity of the window. Finally, make the window big enough in high DPI under MSW by using FromDIP(). --- samples/shaped/shaped.cpp | 122 +++++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 36 deletions(-) diff --git a/samples/shaped/shaped.cpp b/samples/shaped/shaped.cpp index 95011739ed..f5aadf1dab 100644 --- a/samples/shaped/shaped.cpp +++ b/samples/shaped/shaped.cpp @@ -36,6 +36,9 @@ #include "wx/dcclient.h" #include "wx/graphics.h" #include "wx/image.h" +#include "wx/scopedptr.h" +#include "wx/sizer.h" +#include "wx/slider.h" #ifndef wxHAS_IMAGES_IN_RESOURCES #include "../sample.xpm" @@ -50,6 +53,7 @@ enum { Show_Shaped = 100, Show_Transparent, + Show_TransparentBg, // must be consecutive and in the same order as wxShowEffect enum elements Show_Effect_First, @@ -134,12 +138,14 @@ private: class SeeThroughFrame : public wxFrame { public: - void Create(); + void Create(wxWindow* parent); private: // event handlers (these functions should _not_ be virtual) void OnPaint(wxPaintEvent& evt); + void OnAlpha(wxCommandEvent& event); + // any class wishing to process wxWidgets events must use this macro wxDECLARE_EVENT_TABLE(); }; @@ -238,6 +244,7 @@ bool MyApp::OnInit() wxBEGIN_EVENT_TABLE(MainFrame, wxFrame) EVT_MENU(Show_Shaped, MainFrame::OnShowShaped) EVT_MENU(Show_Transparent, MainFrame::OnShowTransparent) + EVT_MENU(Show_TransparentBg, MainFrame::OnShowTransparent) EVT_MENU_RANGE(Show_Effect_First, Show_Effect_Last, MainFrame::OnShowEffect) EVT_MENU(wxID_EXIT, MainFrame::OnExit) wxEND_EVENT_TABLE() @@ -252,6 +259,8 @@ MainFrame::MainFrame() wxMenu * const menuFrames = new wxMenu; menuFrames->Append(Show_Shaped, "Show &shaped window\tCtrl-S"); menuFrames->Append(Show_Transparent, "Show &transparent window\tCtrl-T"); + menuFrames->Append(Show_TransparentBg, + "Show with &transparent background\tShift-Ctrl-T"); menuFrames->AppendSeparator(); menuFrames->Append(Show_Effect_Roll, "Show &rolled effect\tCtrl-R"); menuFrames->Append(Show_Effect_Slide, "Show s&lide effect\tCtrl-L"); @@ -272,19 +281,27 @@ void MainFrame::OnShowShaped(wxCommandEvent& WXUNUSED(event)) shapedFrame->Show(true); } -void MainFrame::OnShowTransparent(wxCommandEvent& WXUNUSED(event)) +void MainFrame::OnShowTransparent(wxCommandEvent& event) { - wxString reason; - if (IsTransparentBackgroundSupported(&reason)) + SeeThroughFrame *seeThroughFrame = new SeeThroughFrame; + + if ( event.GetId() == Show_TransparentBg ) { - SeeThroughFrame *seeThroughFrame = new SeeThroughFrame; - seeThroughFrame->Create(); - seeThroughFrame->Show(true); - } - else - { - wxLogError("%s, can't create transparent window.", reason); + wxString reason; + if (IsTransparentBackgroundSupported(&reason)) + { + seeThroughFrame->SetBackgroundStyle(wxBG_STYLE_TRANSPARENT); + } + else + { + wxLogError("Can't use transparent background: %s", reason); + delete seeThroughFrame; + return; + } } + + seeThroughFrame->Create(this); + seeThroughFrame->Show(true); } void MainFrame::OnShowEffect(wxCommandEvent& event) @@ -474,44 +491,77 @@ wxBEGIN_EVENT_TABLE(SeeThroughFrame, wxFrame) EVT_PAINT(SeeThroughFrame::OnPaint) wxEND_EVENT_TABLE() -void SeeThroughFrame::Create() +void +SeeThroughFrame::Create(wxWindow* parent) { - SetBackgroundStyle(wxBG_STYLE_TRANSPARENT); - wxFrame::Create(NULL, wxID_ANY, "Transparency test", - wxPoint(100, 30), wxSize(300, 300), + wxFrame::Create(parent, wxID_ANY, "Transparency test", + wxDefaultPosition, wxWindow::FromDIP(wxSize(300, 300), parent), wxDEFAULT_FRAME_STYLE | wxFULL_REPAINT_ON_RESIZE | wxSTAY_ON_TOP); SetBackgroundColour(*wxWHITE); + + const int initialAlpha = wxALPHA_OPAQUE / 2; + + // Create control for choosing alpha and put it in the middle of the window. + wxPanel* panel = new wxPanel(this); + wxSlider* slider = new wxSlider + ( + panel, + wxID_ANY, + initialAlpha, + wxALPHA_TRANSPARENT, + wxALPHA_OPAQUE, + wxDefaultPosition, + FromDIP(wxSize(256, -1)), + wxSL_HORIZONTAL | wxSL_LABELS + ); + slider->Bind(wxEVT_SLIDER, &SeeThroughFrame::OnAlpha, this); + + const wxSizerFlags center = wxSizerFlags().Center().Border(); + + wxSizer* sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(new wxStaticText(panel, wxID_ANY, "Window opacity:"), center); + sizer->Add(slider, center); + panel->SetSizer(sizer); + + wxSizer* sizerTop = new wxBoxSizer(wxVERTICAL); + sizerTop->AddStretchSpacer(); + sizerTop->Add(panel, center); + sizerTop->AddStretchSpacer(); + SetSizer(sizerTop); + + // Note that this must be called before the frame is shown. + SetTransparent(initialAlpha); } // Paints a grid of varying hue and alpha void SeeThroughFrame::OnPaint(wxPaintEvent& WXUNUSED(evt)) { wxPaintDC dc(this); - dc.SetPen(wxNullPen); + wxScopedPtr gc(wxGraphicsContext::Create(dc)); - int xcount = 8; - int ycount = 8; + // Draw 3 bands: one opaque, one semi-transparent and one transparent. + const wxSize size = GetClientSize(); + const double h = size.y / 3.0; - double xstep = 1.0 / xcount; - double ystep = 1.0 / ycount; + wxGraphicsPath path = gc->CreatePath(); + path.AddRectangle(0, 0, size.x, h); + gc->SetBrush(wxBrush(wxColour(0, 0, 255, wxALPHA_OPAQUE))); + gc->FillPath(path); - int width = GetClientSize().GetWidth(); - int height = GetClientSize().GetHeight(); + gc->SetBrush(wxBrush(wxColour(0, 0, 255, wxALPHA_OPAQUE / 2))); + gc->Translate(0, h); + gc->FillPath(path); - for ( double x = 0; x < 1; x += xstep ) - { - for ( double y = 0; y < 1; y += ystep ) - { - wxImage::RGBValue v = wxImage::HSVtoRGB(wxImage::HSVValue(x, 1., 1.)); - dc.SetBrush(wxBrush(wxColour(v.red, v.green, v.blue, - (int)(255*(1. - y))))); - int x1 = (int)(x * width); - int y1 = (int)(y * height); - int x2 = (int)((x + xstep) * width); - int y2 = (int)((y + ystep) * height); - dc.DrawRectangle(x1, y1, x2 - x1, y2 - y1); - } - } + // This blue won't actually be seen and instead the white background will + // be visible, because this brush is fully transparent. + gc->SetBrush(wxBrush(wxColour(0, 0, 255, wxALPHA_TRANSPARENT))); + gc->Translate(0, h); + gc->FillPath(path); +} + +void SeeThroughFrame::OnAlpha(wxCommandEvent& event) +{ + SetTransparent(event.GetInt()); } From fffe5af90375ba1c375cfba24c68fa887fd9d846 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 25 Mar 2022 01:48:16 +0100 Subject: [PATCH 2/3] Document SetTransparent() limitation under wxGTK The first call to this function must happen before showing the window. --- interface/wx/toplevel.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/wx/toplevel.h b/interface/wx/toplevel.h index 68ba34827e..dd326d9f8d 100644 --- a/interface/wx/toplevel.h +++ b/interface/wx/toplevel.h @@ -585,6 +585,10 @@ public: /** If the platform supports it will set the window to be translucent. + Note that in wxGTK this function must be called before the window is + shown the first time it's called (but it can be called again after + showing the window too). + @param alpha Determines how opaque or transparent the window will be, if the platform supports the operation. A value of 0 sets the window to be From 80a056a58bd51e2348937805ddb16c061206b97f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 25 Mar 2022 01:54:11 +0100 Subject: [PATCH 3/3] Improve "shaped" sample documentation Also link to it from the functions shown in this sample. --- docs/doxygen/mainpages/samples.h | 8 ++++++-- interface/wx/toplevel.h | 3 +++ interface/wx/window.h | 4 +++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/doxygen/mainpages/samples.h b/docs/doxygen/mainpages/samples.h index 0b5c3b576a..9dd572587e 100644 --- a/docs/doxygen/mainpages/samples.h +++ b/docs/doxygen/mainpages/samples.h @@ -690,10 +690,14 @@ utility under macOS). @section page_samples_shaped Shaped Window Sample -@sampleabout{how to implement a shaped or transparent window\, and a window showing/hiding with effect} +@sampleabout{Showing unusual, e.g. shaped or semi-transparent windows} + +This sample shows windows with non-rectangular shapes and non-opaque windows as +well as how the windows can be shown with a special effect. @see wxTopLevelWindow::SetShape(), wxTopLevelWindow::SetTransparent(), -wxWindow::ShowWithEffect(), wxWindow::HideWithEffect() +wxWindow::ShowWithEffect(), wxWindow::HideWithEffect(), +wxWindow::SetBackgroundStyle() @sampledir{shaped} diff --git a/interface/wx/toplevel.h b/interface/wx/toplevel.h index dd326d9f8d..64cc29a233 100644 --- a/interface/wx/toplevel.h +++ b/interface/wx/toplevel.h @@ -589,6 +589,9 @@ public: shown the first time it's called (but it can be called again after showing the window too). + See @ref page_samples_shaped "the shaped sample" for an example of + using this function. + @param alpha Determines how opaque or transparent the window will be, if the platform supports the operation. A value of 0 sets the window to be diff --git a/interface/wx/window.h b/interface/wx/window.h index 1688fd5ed8..4dfebe1fef 100644 --- a/interface/wx/window.h +++ b/interface/wx/window.h @@ -2395,7 +2395,9 @@ public: Under wxGTK and wxOSX, you can use ::wxBG_STYLE_TRANSPARENT to obtain full transparency of the window background. Note that wxGTK supports this only since GTK 2.12 with a compositing manager enabled, call - IsTransparentBackgroundSupported() to check whether this is the case. + IsTransparentBackgroundSupported() to check whether this is the case, + see the example of doing it in @ref page_samples_shaped "the shaped + sample". Also, in order for @c SetBackgroundStyle(wxBG_STYLE_TRANSPARENT) to work, it must be called before Create(). If you're using your own