diff options
author | Caolán McNamara <caolanm@redhat.com> | 2019-10-09 13:55:46 +0100 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2019-10-10 20:25:07 +0200 |
commit | bb6bcabda53dbd64dda43f09fab9f4ad6f34eba6 (patch) | |
tree | 4fe51458a061c92587c22be397fd74a2e4436aaf | |
parent | 68549e00d5e23aa22bc974a8151d93cd948444b3 (diff) |
support screenshotting .ui files with GtkBuilder
so...
SAL_USE_VCLPLUGIN=gtk3 make screenshot
for the "unknown dialog" cases of make screenshot, which loads .ui files and
tries to render them, will render them with the native gtk infrastructure
Change-Id: Ifc8fe264155887c4d01b7ce0e2aa53e12acbfcb0
Reviewed-on: https://gerrit.libreoffice.org/80545
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
-rw-r--r-- | include/test/screenshot_test.hxx | 15 | ||||
-rw-r--r-- | include/vcl/dialog.hxx | 18 | ||||
-rw-r--r-- | include/vcl/syswin.hxx | 18 | ||||
-rw-r--r-- | include/vcl/weld.hxx | 57 | ||||
-rw-r--r-- | test/source/screenshot_test.cxx | 106 | ||||
-rw-r--r-- | vcl/UIConfig_vcl.mk | 1 | ||||
-rw-r--r-- | vcl/source/app/salvtables.cxx | 124 | ||||
-rw-r--r-- | vcl/source/window/dialog.cxx | 53 | ||||
-rw-r--r-- | vcl/source/window/syswin.cxx | 54 | ||||
-rw-r--r-- | vcl/uiconfig/ui/screenshotparent.ui | 54 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkinst.cxx | 377 |
11 files changed, 511 insertions, 366 deletions
diff --git a/include/test/screenshot_test.hxx b/include/test/screenshot_test.hxx index 74bcfc721d53..3e1f3f84a06f 100644 --- a/include/test/screenshot_test.hxx +++ b/include/test/screenshot_test.hxx @@ -15,7 +15,8 @@ #include <unotest/macros_test.hxx> #include <com/sun/star/lang/XComponent.hpp> #include <osl/file.hxx> -#include <vcl/dialog.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/weld.hxx> #include <map> class VclAbstractDialog; @@ -30,11 +31,18 @@ private: /// the set of known dialogs and their ID for usage in createDialogByID mapType maKnownDialogs; + /// parent for non-dialog buildables + weld::GenericDialogController maParent; + std::unique_ptr<weld::Container> mxParentWidget; + private: /// helpers void implSaveScreenshot(const BitmapEx& rScreenshot, const OString& rScreenshotId); void saveScreenshot(VclAbstractDialog const & rDialog); - void saveScreenshot(Dialog& rDialog); + void saveScreenshot(weld::Window& rDialog); + + /// helper method to create and dump a dialog based on Builder contents. + void dumpDialogToPath(weld::Builder& rDialog); /// helper method to populate maKnownDialogs, called in setUp(). Needs to be /// written and has to add entries to maKnownDialogs @@ -58,9 +66,6 @@ public: /// version for AbstractDialogs, the ones created in AbstractDialogFactories void dumpDialogToPath(VclAbstractDialog& rDialog); - /// version for pure vcl-based dialogs - void dumpDialogToPath(Dialog& rDialog); - /// fallback version for dialogs for which only the UXMLDescription is known. /// This should be used with care - no active layouting will be done, only the /// VclBuilder will be activated for layouting. Result can thus vary drastically diff --git a/include/vcl/dialog.hxx b/include/vcl/dialog.hxx index cc5355d58132..ee2560abc475 100644 --- a/include/vcl/dialog.hxx +++ b/include/vcl/dialog.hxx @@ -50,7 +50,6 @@ private: bool mbInSyncExecute; bool mbInClose; bool mbModalMode; - bool mbPaintComplete; bool const mbForceBorderWindow; InitFlag const mnInitFlag; // used for deferred init @@ -70,11 +69,6 @@ private: DECL_DLLPRIVATE_LINK(ImplAsyncCloseHdl, void*, void); DECL_DLLPRIVATE_LINK(ResponseHdl, Button*, void); - // ensureRepaint - triggers Application::Yield until the dialog is - // completely repainted. Sometimes needed for dialogs showing progress - // during actions - void ensureRepaint(); - protected: using Window::ImplInit; void ImplInit( vcl::Window* pParent, WinBits nStyle, InitFlag eFlag = InitFlag::Default ); @@ -95,6 +89,7 @@ protected: protected: friend class VclBuilder; + friend class SalInstanceBuilder; void set_action_area(VclButtonBox* pBox); virtual void set_content_area(VclBox* pBox); @@ -119,17 +114,6 @@ public: virtual bool Close() override; - // try to extract content and return as Bitmap. To do that reliably, a Yield-loop - // like in Execute() has to be executed and it is necessary to detect when the - // paint is finished - virtual void PrePaint(vcl::RenderContext& rRenderContext) override; - virtual void PostPaint(vcl::RenderContext& rRenderContext) override; - - // Screenshot interface - virtual std::vector<OString> getAllPageUIXMLDescriptions() const; - virtual bool selectPageByUIXMLDescription(const OString& rUIXMLDescription); - void createScreenshot(VirtualDevice& rOutput); - virtual short Execute(); bool IsInExecute() const { return mbInExecute; } // Return true when dialog is synchronously executed (calling ::Execute()) diff --git a/include/vcl/syswin.hxx b/include/vcl/syswin.hxx index 402aebeb381e..cb6d15d27ead 100644 --- a/include/vcl/syswin.hxx +++ b/include/vcl/syswin.hxx @@ -141,6 +141,7 @@ private: bool mbHideBtn; bool mbSysChild; bool mbIsCalculatingInitialLayoutSize; + bool mbPaintComplete; MenuBarMode mnMenuBarMode; sal_uInt16 mnIcon; std::unique_ptr<ImplData> mpImplData; @@ -159,6 +160,17 @@ private: SAL_DLLPRIVATE void setPosSizeOnContainee(Size aSize, Window &rBox); DECL_DLLPRIVATE_LINK( ImplHandleLayoutTimerHdl, Timer*, void ); + // try to extract content and return as Bitmap. To do that reliably, a Yield-loop + // like in Execute() has to be executed and it is necessary to detect when the + // paint is finished + virtual void PrePaint(vcl::RenderContext& rRenderContext) override; + virtual void PostPaint(vcl::RenderContext& rRenderContext) override; + + // ensureRepaint - triggers Application::Yield until the dialog is + // completely repainted. Sometimes needed for dialogs showing progress + // during actions + SAL_DLLPRIVATE void ensureRepaint(); + protected: // Single argument ctors shall be explicit. explicit SystemWindow(WindowType nType); @@ -171,6 +183,7 @@ protected: SAL_DLLPRIVATE void DoInitialLayout(); SAL_DLLPRIVATE void SetIdleDebugName( const sal_Char *pDebugName ); + public: virtual ~SystemWindow() override; virtual void dispose() override; @@ -270,6 +283,11 @@ public: SAL_DLLPRIVATE bool hasPendingLayout() const { return maLayoutIdle.IsActive(); } virtual void doDeferredInit(WinBits nBits); + + // Screenshot interface + virtual std::vector<OString> getAllPageUIXMLDescriptions() const; + virtual bool selectPageByUIXMLDescription(const OString& rUIXMLDescription); + void createScreenshot(VirtualDevice& rOutput); }; inline void SystemWindow::SetIdleDebugName( const sal_Char *pDebugName ) diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx index 20c8d9e5751e..be9cf23f0779 100644 --- a/include/vcl/weld.hxx +++ b/include/vcl/weld.hxx @@ -351,6 +351,26 @@ public: void connect_enter_page(const Link<const OString&, void>& rLink) { m_aEnterPageHdl = rLink; } }; +class VCL_DLLPUBLIC ScreenShotEntry +{ +public: + ScreenShotEntry(const OString& rHelpId, const basegfx::B2IRange& rB2IRange) + : msHelpId(rHelpId) + , maB2IRange(rB2IRange) + { + } + + const basegfx::B2IRange& getB2IRange() const { return maB2IRange; } + + const OString& GetHelpId() const { return msHelpId; } + +private: + OString msHelpId; + basegfx::B2IRange maB2IRange; +}; + +typedef std::vector<ScreenShotEntry> ScreenShotCollection; + class VCL_DLLPUBLIC Window : virtual public Container { protected: @@ -397,6 +417,11 @@ public: virtual SystemEnvData get_system_data() const = 0; virtual void resize_to_request() = 0; + + // render the dialog for a screenshot + virtual void draw(VirtualDevice& rOutput) = 0; + // collect positions of widgets and their help ids for screenshot purposes + virtual ScreenShotCollection collect_screenshot_data() = 0; }; class VCL_DLLPUBLIC WaitObject @@ -420,26 +445,6 @@ public: class Button; -class VCL_DLLPUBLIC ScreenShotEntry -{ -public: - ScreenShotEntry(const OString& rHelpId, const basegfx::B2IRange& rB2IRange) - : msHelpId(rHelpId) - , maB2IRange(rB2IRange) - { - } - - const basegfx::B2IRange& getB2IRange() const { return maB2IRange; } - - const OString& GetHelpId() const { return msHelpId; } - -private: - OString msHelpId; - basegfx::B2IRange maB2IRange; -}; - -typedef std::vector<ScreenShotEntry> ScreenShotCollection; - class VCL_DLLPUBLIC Dialog : virtual public Window { private: @@ -467,11 +472,6 @@ public: // undo previous dialog collapse virtual void undo_collapse() = 0; - // render the dialog for a screenshot - virtual void draw(VirtualDevice& rOutput) = 0; - // collect positions of widgets and their help ids for screenshot purposes - virtual ScreenShotCollection collect_screenshot_data() = 0; - virtual void SetInstallLOKNotifierHdl(const Link<void*, vcl::ILibreOfficeKitNotifier*>& rLink) = 0; }; @@ -1937,6 +1937,13 @@ public: virtual std::unique_ptr<Toolbar> weld_toolbar(const OString& id, bool bTakeOwnership = true) = 0; virtual std::unique_ptr<SizeGroup> create_size_group() = 0; + /* return a Dialog suitable to take a screenshot of containing the contents of the .ui file. + + If the toplevel element is a dialog, that will be returned + If the toplevel is not a dialog, a dialog will be created and the contents of the .ui + inserted into it + */ + virtual std::unique_ptr<Window> create_screenshot_window() = 0; virtual ~Builder() {} }; diff --git a/test/source/screenshot_test.cxx b/test/source/screenshot_test.cxx index 42dd194d6fe1..6f4d49cc88bd 100644 --- a/test/source/screenshot_test.cxx +++ b/test/source/screenshot_test.cxx @@ -20,6 +20,7 @@ #include <vcl/pngwrite.hxx> #include <vcl/svapp.hxx> #include <vcl/virdev.hxx> +#include <vcl/weld.hxx> #include <unotools/configmgr.hxx> #include <tools/stream.hxx> @@ -45,6 +46,8 @@ static constexpr OUStringLiteral g_aScreenshotDirectory("screenshots"); ScreenshotTest::ScreenshotTest() : maKnownDialogs() + , maParent(nullptr, "vcl/ui/screenshotparent.ui", "ScreenShot") + , mxParentWidget(maParent.getDialog()->weld_content_area()) { maCurrentLanguage = OUString::fromUtf8(getenv("LO_TEST_LOCALE")); } @@ -109,20 +112,17 @@ void ScreenshotTest::saveScreenshot(VclAbstractDialog const & rDialog) } } -void ScreenshotTest::saveScreenshot(Dialog& rDialog) +void ScreenshotTest::saveScreenshot(weld::Window& rDialog) { VclPtr<VirtualDevice> xDialogSurface(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT)); - rDialog.createScreenshot(*xDialogSurface); + rDialog.draw(*xDialogSurface); const BitmapEx aScreenshot(xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel())); if (!aScreenshot.IsEmpty()) { - const OString aScreenshotId = rDialog.GetScreenshotId(); - - if (!aScreenshotId.isEmpty()) - { - implSaveScreenshot(aScreenshot, aScreenshotId); - } + const OString aScreenshotId = rDialog.get_help_id(); + assert(!aScreenshotId.isEmpty()); + implSaveScreenshot(aScreenshot, aScreenshotId); } } @@ -162,17 +162,29 @@ void ScreenshotTest::dumpDialogToPath(VclAbstractDialog& rDialog) } } -void ScreenshotTest::dumpDialogToPath(Dialog& rDialog) +void ScreenshotTest::dumpDialogToPath(weld::Builder& rBuilder) { - const std::vector<OString> aPageDescriptions(rDialog.getAllPageUIXMLDescriptions()); + std::unique_ptr<weld::Window> xDialog(rBuilder.create_screenshot_window()); - if (!aPageDescriptions.empty()) + auto xTabCtrl = rBuilder.weld_notebook("tabcontrol"); + + int nPages = xTabCtrl ? xTabCtrl->get_n_pages() : 0; + if (nPages) { - for (size_t a(0); a < aPageDescriptions.size(); a++) + for (int i = 0; i < nPages; ++i) { - if (rDialog.selectPageByUIXMLDescription(aPageDescriptions[a])) + OString sIdent(xTabCtrl->get_page_ident(i)); + xTabCtrl->set_current_page(sIdent); + if (xTabCtrl->get_current_page_ident() == sIdent) { - saveScreenshot(rDialog); + OString sOrigHelpId(xDialog->get_help_id()); + // skip empty pages + weld::Container* pPage = xTabCtrl->get_page(sIdent); + OString sBuildableName(pPage->get_buildable_name()); + if (!sBuildableName.isEmpty() && !sBuildableName.startsWith("__")) + xDialog->set_help_id(pPage->get_help_id()); + saveScreenshot(*xDialog); + xDialog->set_help_id(sOrigHelpId); } else { @@ -182,7 +194,7 @@ void ScreenshotTest::dumpDialogToPath(Dialog& rDialog) } else { - saveScreenshot(rDialog); + saveScreenshot(*xDialog); } } @@ -190,41 +202,33 @@ void ScreenshotTest::dumpDialogToPath(const OString& rUIXMLDescription) { if (!rUIXMLDescription.isEmpty()) { - VclPtrInstance<Dialog> pDialog(Application::GetDefDialogParent(), WB_STDDIALOG | WB_SIZEABLE, Dialog::InitFlag::NoParent); - - { - VclPtr<vcl::Window> aOwnedToplevel; - - bool bLegacy = rUIXMLDescription == "fps/ui/remotefilesdialog.ui" || - rUIXMLDescription == "modules/swriter/ui/sidebarstylepresets.ui" || - rUIXMLDescription == "modules/swriter/ui/sidebartheme.ui" || - rUIXMLDescription == "sfx/ui/startcenter.ui" || - rUIXMLDescription == "svx/ui/datanavigator.ui"; - std::unique_ptr<VclBuilder> xBuilder(new VclBuilder(pDialog, VclBuilderContainer::getUIRootDir(), OStringToOUString(rUIXMLDescription, RTL_TEXTENCODING_UTF8), OString(), css::uno::Reference<css::frame::XFrame>(), bLegacy)); - vcl::Window *pRoot = xBuilder->get_widget_root(); - Dialog *pRealDialog = dynamic_cast<Dialog*>(pRoot); - - if (!pRealDialog) - { - pRealDialog = pDialog; - } - else - { - aOwnedToplevel.set(pRoot); - xBuilder->drop_ownership(pRoot); - } - - pRealDialog->SetText(utl::ConfigManager::getProductName()); - pRealDialog->SetStyle(pDialog->GetStyle() | WB_CLOSEABLE); - - dumpDialogToPath(*pRealDialog); - - if (VclBuilderContainer* pOwnedToplevel = dynamic_cast<VclBuilderContainer*>(aOwnedToplevel.get())) - pOwnedToplevel->m_pUIBuilder = std::move(xBuilder); - aOwnedToplevel.disposeAndClear(); - } - - pDialog.disposeAndClear(); + bool bNonConforming = rUIXMLDescription == "modules/swriter/ui/sidebarstylepresets.ui" || + rUIXMLDescription == "modules/swriter/ui/sidebarpage.ui" || + rUIXMLDescription == "modules/swriter/ui/sidebartheme.ui" || + rUIXMLDescription == "modules/swriter/ui/sidebarwrap.ui" || + rUIXMLDescription == "modules/swriter/ui/notebookbar.ui" || + rUIXMLDescription == "modules/scalc/ui/sidebaralignment.ui" || + rUIXMLDescription == "modules/scalc/ui/sidebarcellappearance.ui" || + rUIXMLDescription == "modules/scalc/ui/sidebarnumberformat.ui" || + rUIXMLDescription == "sfx/ui/helpbookmarkpage.ui" || + rUIXMLDescription == "sfx/ui/helpcontentpage.ui" || + rUIXMLDescription == "sfx/ui/helpindexpage.ui" || + rUIXMLDescription == "sfx/ui/helpsearchpage.ui" || + rUIXMLDescription == "sfx/ui/startcenter.ui" || + rUIXMLDescription == "svx/ui/datanavigator.ui" || + rUIXMLDescription == "svx/ui/xformspage.ui" || + rUIXMLDescription == "modules/dbreport/ui/conditionwin.ui"; + if (bNonConforming) // skip these broken ones + return; + bool bLegacy = rUIXMLDescription == "fps/ui/explorerfiledialog.ui" || + rUIXMLDescription == "fps/ui/remotefilesdialog.ui" || + rUIXMLDescription == "sfx/ui/floatingrecord.ui"; + std::unique_ptr<weld::Builder> xBuilder; + if (bLegacy) + xBuilder.reset(Application::CreateInterimBuilder(mxParentWidget.get(), OStringToOUString(rUIXMLDescription, RTL_TEXTENCODING_UTF8))); + else + xBuilder.reset(Application::CreateBuilder(mxParentWidget.get(), OStringToOUString(rUIXMLDescription, RTL_TEXTENCODING_UTF8))); + dumpDialogToPath(*xBuilder); } } @@ -273,7 +277,7 @@ void ScreenshotTest::processDialogBatchFile(const OUString& rFile) else { // unknown dialog, try fallback to generic created - // VclBuilder-generated instance. Keep in mind that Dialogs + // Builder-generated instance. Keep in mind that Dialogs // using this mechanism will probably not be layouted well // since the setup/initialization part is missing. Thus, // only use for fallback when only the UI file is available. diff --git a/vcl/UIConfig_vcl.mk b/vcl/UIConfig_vcl.mk index 638fc9a537ae..c00d0d461f23 100644 --- a/vcl/UIConfig_vcl.mk +++ b/vcl/UIConfig_vcl.mk @@ -23,6 +23,7 @@ $(eval $(call gb_UIConfig_add_uifiles,vcl,\ vcl/uiconfig/ui/printerpropertiesdialog \ vcl/uiconfig/ui/printprogressdialog \ vcl/uiconfig/ui/querydialog \ + vcl/uiconfig/ui/screenshotparent \ )) $(eval $(call gb_UIConfig_add_a11yerrors_uifiles,vcl,\ diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index 416f81071e93..d12c3464c75d 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -35,6 +35,7 @@ #include <messagedialog.hxx> #include <treeglue.hxx> #include <unotools/accessiblerelationsethelper.hxx> +#include <unotools/configmgr.hxx> #include <utility> #include <tools/helpers.hxx> #include <vcl/abstdlg.hxx> @@ -1059,6 +1060,34 @@ std::unique_ptr<weld::Container> SalInstanceWidget::weld_parent() const return std::make_unique<SalInstanceContainer>(pParent, m_pBuilder, false); } +namespace +{ + void CollectChildren(const vcl::Window& rCurrent, const basegfx::B2IPoint& rTopLeft, weld::ScreenShotCollection& rControlDataCollection) + { + if (rCurrent.IsVisible()) + { + const Point aCurrentPos(rCurrent.GetPosPixel()); + const Size aCurrentSize(rCurrent.GetSizePixel()); + const basegfx::B2IPoint aCurrentTopLeft(rTopLeft.getX() + aCurrentPos.X(), rTopLeft.getY() + aCurrentPos.Y()); + const basegfx::B2IRange aCurrentRange(aCurrentTopLeft, aCurrentTopLeft + basegfx::B2IPoint(aCurrentSize.Width(), aCurrentSize.Height())); + + if (!aCurrentRange.isEmpty()) + { + rControlDataCollection.emplace_back(rCurrent.GetHelpId(), aCurrentRange); + } + + for (sal_uInt16 a(0); a < rCurrent.GetChildCount(); a++) + { + vcl::Window* pChild = rCurrent.GetChild(a); + if (nullptr != pChild) + { + CollectChildren(*pChild, aCurrentTopLeft, rControlDataCollection); + } + } + } + } +} + class SalInstanceWindow : public SalInstanceContainer, public virtual weld::Window { private: @@ -1221,6 +1250,26 @@ public: SalInstanceContainer::HandleEventListener(rEvent); } + virtual void draw(VirtualDevice& rOutput) override + { + SystemWindow* pSysWin = dynamic_cast<SystemWindow*>(m_xWindow.get()); + assert(pSysWin); + pSysWin->createScreenshot(rOutput); + } + + virtual weld::ScreenShotCollection collect_screenshot_data() override + { + weld::ScreenShotCollection aRet; + + // collect all children. Choose start pos to be negative + // of target dialog's position to get all positions relative to (0,0) + const Point aParentPos(m_xWindow->GetPosPixel()); + const basegfx::B2IPoint aTopLeft(-aParentPos.X(), -aParentPos.Y()); + CollectChildren(*m_xWindow, aTopLeft, aRet); + + return aRet; + } + virtual ~SalInstanceWindow() override { clear_child_help(m_xWindow); @@ -1258,34 +1307,6 @@ namespace } } -namespace -{ - void CollectChildren(const vcl::Window& rCurrent, const basegfx::B2IPoint& rTopLeft, weld::ScreenShotCollection& rControlDataCollection) - { - if (rCurrent.IsVisible()) - { - const Point aCurrentPos(rCurrent.GetPosPixel()); - const Size aCurrentSize(rCurrent.GetSizePixel()); - const basegfx::B2IPoint aCurrentTopLeft(rTopLeft.getX() + aCurrentPos.X(), rTopLeft.getY() + aCurrentPos.Y()); - const basegfx::B2IRange aCurrentRange(aCurrentTopLeft, aCurrentTopLeft + basegfx::B2IPoint(aCurrentSize.Width(), aCurrentSize.Height())); - - if (!aCurrentRange.isEmpty()) - { - rControlDataCollection.emplace_back(rCurrent.GetHelpId(), aCurrentRange); - } - - for (sal_uInt16 a(0); a < rCurrent.GetChildCount(); a++) - { - vcl::Window* pChild = rCurrent.GetChild(a); - if (nullptr != pChild) - { - CollectChildren(*pChild, aCurrentTopLeft, rControlDataCollection); - } - } - } - } -} - class SalInstanceDialog : public SalInstanceWindow, public virtual weld::Dialog { private: @@ -1474,23 +1495,6 @@ public: return new SalInstanceContainer(m_xDialog->get_content_area(), m_pBuilder, false); } - virtual void draw(VirtualDevice& rOutput) override - { - m_xDialog->createScreenshot(rOutput); - } - - virtual weld::ScreenShotCollection collect_screenshot_data() override - { - weld::ScreenShotCollection aRet; - - // collect all children. Choose start pos to be negative - // of target dialog's position to get all positions relative to (0,0) - const Point aParentPos(m_xDialog->GetPosPixel()); - const basegfx::B2IPoint aTopLeft(-aParentPos.X(), -aParentPos.Y()); - CollectChildren(*m_xDialog, aTopLeft, aRet); - - return aRet; - } }; IMPL_LINK(SalInstanceDialog, PopupScreenShotMenuHdl, const CommandEvent&, rCEvt, bool) @@ -5759,6 +5763,34 @@ public: return pRet; } + virtual std::unique_ptr<weld::Window> create_screenshot_window() override + { + assert(!m_aOwnedToplevel && "only one toplevel per .ui allowed"); + + vcl::Window *pRoot = m_xBuilder->get_widget_root(); + if (SystemWindow *pWindow = dynamic_cast<SystemWindow*>(pRoot)) + { + std::unique_ptr<weld::Window> xRet(new SalInstanceWindow(pWindow, this, false)); + m_aOwnedToplevel.set(pWindow); + m_xBuilder->drop_ownership(pWindow); + return xRet; + } + + VclPtrInstance<Dialog> xDialog(nullptr, WB_HIDE | WB_STDDIALOG | WB_SIZEABLE | WB_CLOSEABLE, Dialog::InitFlag::NoParent); + xDialog->SetText(utl::ConfigManager::getProductName()); + + auto xContentArea = VclPtr<VclVBox>::Create(xDialog, false, 12); + pRoot->SetParent(xContentArea); + assert(pRoot == xContentArea->GetWindow(GetWindowType::FirstChild)); + xContentArea->Show(); + pRoot->Show(); + xDialog->SetHelpId(pRoot->GetHelpId()); + + m_aOwnedToplevel.set(xDialog); + + return std::unique_ptr<weld::Dialog>(new SalInstanceDialog(xDialog, this, false)); + } + virtual std::unique_ptr<weld::Window> weld_window(const OString &id, bool bTakeOwnership) override { SystemWindow* pWindow = m_xBuilder->get<SystemWindow>(id); @@ -5799,6 +5831,8 @@ public: virtual std::unique_ptr<weld::Notebook> weld_notebook(const OString &id, bool bTakeOwnership) override { vcl::Window* pNotebook = m_xBuilder->get<vcl::Window>(id); + if (!pNotebook) + return nullptr; if (pNotebook->GetType() == WindowType::TABCONTROL) return std::make_unique<SalInstanceNotebook>(static_cast<TabControl*>(pNotebook), this, bTakeOwnership); if (pNotebook->GetType() == WindowType::VERTICALTABCONTROL) diff --git a/vcl/source/window/dialog.cxx b/vcl/source/window/dialog.cxx index 679403c9efbc..714141ea8131 100644 --- a/vcl/source/window/dialog.cxx +++ b/vcl/source/window/dialog.cxx @@ -386,7 +386,6 @@ void Dialog::ImplInitDialogData() mbInSyncExecute = false; mbInClose = false; mbModalMode = false; - mbPaintComplete = false; mpContentArea.clear(); mpActionArea.clear(); mnMousePositioned = 0; @@ -1013,58 +1012,6 @@ void Dialog::ImplEndExecuteModal() pSVData->maAppData.mnModalMode--; } -void Dialog::PrePaint(vcl::RenderContext& rRenderContext) -{ - SystemWindow::PrePaint(rRenderContext); - mbPaintComplete = false; -} - -void Dialog::PostPaint(vcl::RenderContext& rRenderContext) -{ - SystemWindow::PostPaint(rRenderContext); - mbPaintComplete = true; -} - -std::vector<OString> Dialog::getAllPageUIXMLDescriptions() const -{ - // default has no pages - return std::vector<OString>(); -} - -bool Dialog::selectPageByUIXMLDescription(const OString& /*rUIXMLDescription*/) -{ - // default cannot select anything (which is okay, return true) - return true; -} - -void Dialog::ensureRepaint() -{ - // ensure repaint - Invalidate(); - mbPaintComplete = false; - - while (!mbPaintComplete) - { - Application::Yield(); - } -} - -void Dialog::createScreenshot(VirtualDevice& rOutput) -{ - // same prerequisites as in Execute() - setDeferredProperties(); - ImplAdjustNWFSizes(); - Show(); - ToTop(); - ensureRepaint(); - - Point aPos; - Size aSize(GetOutputSizePixel()); - - rOutput.SetOutputSizePixel(aSize); - rOutput.DrawOutDev(aPos, aSize, aPos, aSize, *this); -} - short Dialog::Execute() { // Once the Android app is based on same idea as the iOS one currently diff --git a/vcl/source/window/syswin.cxx b/vcl/source/window/syswin.cxx index aa0964524e35..c6ecb2109a74 100644 --- a/vcl/source/window/syswin.cxx +++ b/vcl/source/window/syswin.cxx @@ -31,6 +31,7 @@ #include <vcl/taskpanelist.hxx> #include <vcl/tabctrl.hxx> #include <vcl/tabpage.hxx> +#include <vcl/virdev.hxx> #include <rtl/strbuf.hxx> @@ -67,6 +68,7 @@ SystemWindow::SystemWindow(WindowType nType) , mbHideBtn(false) , mbSysChild(false) , mbIsCalculatingInitialLayoutSize(false) + , mbPaintComplete(false) , mnMenuBarMode(MenuBarMode::Normal) , mnIcon(0) , mpImplData(new ImplData) @@ -1152,4 +1154,56 @@ void SystemWindow::doDeferredInit(WinBits /*nBits*/) SAL_WARN("vcl.layout", "SystemWindow in layout without doDeferredInit impl"); } +std::vector<OString> SystemWindow::getAllPageUIXMLDescriptions() const +{ + // default has no pages + return std::vector<OString>(); +} + +bool SystemWindow::selectPageByUIXMLDescription(const OString& /*rUIXMLDescription*/) +{ + // default cannot select anything (which is okay, return true) + return true; +} + +void SystemWindow::createScreenshot(VirtualDevice& rOutput) +{ + // same prerequisites as in Execute() + setDeferredProperties(); + ImplAdjustNWFSizes(); + Show(); + ToTop(); + ensureRepaint(); + + Point aPos; + Size aSize(GetOutputSizePixel()); + + rOutput.SetOutputSizePixel(aSize); + rOutput.DrawOutDev(aPos, aSize, aPos, aSize, *this); +} + +void SystemWindow::PrePaint(vcl::RenderContext& rRenderContext) +{ + Window::PrePaint(rRenderContext); + mbPaintComplete = false; +} + +void SystemWindow::PostPaint(vcl::RenderContext& rRenderContext) +{ + Window::PostPaint(rRenderContext); + mbPaintComplete = true; +} + +void SystemWindow::ensureRepaint() +{ + // ensure repaint + Invalidate(); + mbPaintComplete = false; + + while (!mbPaintComplete) + { + Application::Yield(); + } +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/uiconfig/ui/screenshotparent.ui b/vcl/uiconfig/ui/screenshotparent.ui new file mode 100644 index 000000000000..722a1e810839 --- /dev/null +++ b/vcl/uiconfig/ui/screenshotparent.ui @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.1 --> +<interface domain="vcl"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkDialog" id="ScreenShot"> + <property name="can_focus">False</property> + <property name="border_width">6</property> + <property name="modal">True</property> + <property name="default_width">0</property> + <property name="default_height">0</property> + <property name="type_hint">dialog</property> + <child> + <placeholder/> + </child> + <child internal-child="vbox"> + <object class="GtkBox" id="ScreenShotBox"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="internalarea"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="cancel"> + <property name="label">gtk-close</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-6">cancel</action-widget> + </action-widgets> + </object> +</interface> diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx index 1ddbc7f0b180..9dd62384c2da 100644 --- a/vcl/unx/gtk3/gtk3gtkinst.cxx +++ b/vcl/unx/gtk3/gtk3gtkinst.cxx @@ -3223,6 +3223,126 @@ namespace } } +namespace +{ + struct ButtonOrder + { + const char * m_aType; + int m_nPriority; + }; + + int getButtonPriority(const OString &rType) + { + static const size_t N_TYPES = 6; + static const ButtonOrder aDiscardCancelSave[N_TYPES] = + { + { "/discard", 0 }, + { "/cancel", 1 }, + { "/no", 2 }, + { "/save", 3 }, + { "/yes", 3 }, + { "/ok", 3 } + }; + + static const ButtonOrder aSaveDiscardCancel[N_TYPES] = + { + { "/save", 0 }, + { "/yes", 0 }, + { "/ok", 0 }, + { "/discard", 1 }, + { "/no", 1 }, + { "/cancel", 2 } + }; + + const ButtonOrder* pOrder = &aDiscardCancelSave[0]; + + const OUString &rEnv = Application::GetDesktopEnvironment(); + + if (rEnv.equalsIgnoreAsciiCase("windows") || + rEnv.equalsIgnoreAsciiCase("tde") || + rEnv.startsWithIgnoreAsciiCase("kde")) + { + pOrder = &aSaveDiscardCancel[0]; + } + + for (size_t i = 0; i < N_TYPES; ++i, ++pOrder) + { + if (rType.endsWith(pOrder->m_aType)) + return pOrder->m_nPriority; + } + + return -1; + } + + bool sortButtons(const GtkWidget* pA, const GtkWidget* pB) + { + //order within groups according to platform rules + return getButtonPriority(::get_help_id(pA)) < getButtonPriority(::get_help_id(pB)); + } + + void sort_native_button_order(GtkBox* pContainer) + { + std::vector<GtkWidget*> aChildren; + GList* pChildren = gtk_container_get_children(GTK_CONTAINER(pContainer)); + for (GList* pChild = g_list_first(pChildren); pChild; pChild = g_list_next(pChild)) + aChildren.push_back(static_cast<GtkWidget*>(pChild->data)); + g_list_free(pChildren); + + //sort child order within parent so that we match the platform button order + std::stable_sort(aChildren.begin(), aChildren.end(), sortButtons); + + for (size_t pos = 0; pos < aChildren.size(); ++pos) + gtk_box_reorder_child(pContainer, aChildren[pos], pos); + } + + Point get_csd_offset(GtkWidget* pTopLevel) + { + // try and omit drawing CSD under wayland + GList* pChildren = gtk_container_get_children(GTK_CONTAINER(pTopLevel)); + GList* pChild = g_list_first(pChildren); + + int x, y; + gtk_widget_translate_coordinates(GTK_WIDGET(pChild->data), + GTK_WIDGET(pTopLevel), + 0, 0, &x, &y); + + int innerborder = gtk_container_get_border_width(GTK_CONTAINER(pChild->data)); + g_list_free(pChildren); + + int outerborder = gtk_container_get_border_width(GTK_CONTAINER(pTopLevel)); + int totalborder = outerborder + innerborder; + x -= totalborder; + y -= totalborder; + + return Point(x, y); + } + + void do_collect_screenshot_data(GtkWidget* pItem, gpointer data) + { + GtkWidget* pTopLevel = gtk_widget_get_toplevel(pItem); + + int x, y; + gtk_widget_translate_coordinates(pItem, pTopLevel, 0, 0, &x, &y); + + Point aOffset = get_csd_offset(pTopLevel); + + GtkAllocation alloc; + gtk_widget_get_allocation(pItem, &alloc); + + const basegfx::B2IPoint aCurrentTopLeft(x - aOffset.X(), y - aOffset.Y()); + const basegfx::B2IRange aCurrentRange(aCurrentTopLeft, aCurrentTopLeft + basegfx::B2IPoint(alloc.width, alloc.height)); + + if (!aCurrentRange.isEmpty()) + { + weld::ScreenShotCollection* pCollection = static_cast<weld::ScreenShotCollection*>(data); + pCollection->emplace_back(::get_help_id(pItem), aCurrentRange); + } + + if (GTK_IS_CONTAINER(pItem)) + gtk_container_forall(GTK_CONTAINER(pItem), do_collect_screenshot_data, data); + } +} + class GtkInstanceWindow : public GtkInstanceContainer, public virtual weld::Window { private: @@ -3434,87 +3554,68 @@ public: g_signal_handler_unblock(m_pWidget, m_nToplevelFocusChangedSignalId); } - virtual ~GtkInstanceWindow() override - { - if (m_nToplevelFocusChangedSignalId) - g_signal_handler_disconnect(m_pWindow, m_nToplevelFocusChangedSignalId); - if (m_xWindow.is()) - m_xWindow->clear(); - } -}; - -namespace -{ - struct ButtonOrder - { - const char * m_aType; - int m_nPriority; - }; - - int getButtonPriority(const OString &rType) + virtual void draw(VirtualDevice& rOutput) override { - static const size_t N_TYPES = 6; - static const ButtonOrder aDiscardCancelSave[N_TYPES] = + // detect if we have to manually setup its size + bool bAlreadyRealized = gtk_widget_get_realized(GTK_WIDGET(m_pWindow)); + // has to be visible for draw to work + bool bAlreadyVisible = gtk_widget_get_visible(GTK_WIDGET(m_pWindow)); + if (!bAlreadyVisible) { - { "/discard", 0 }, - { "/cancel", 1 }, - { "/no", 2 }, - { "/save", 3 }, - { "/yes", 3 }, - { "/ok", 3 } - }; + if (GTK_IS_DIALOG(m_pWindow)) + sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(m_pWindow)))); + gtk_widget_show(GTK_WIDGET(m_pWindow)); + } - static const ButtonOrder aSaveDiscardCancel[N_TYPES] = + if (!bAlreadyRealized) { - { "/save", 0 }, - { "/yes", 0 }, - { "/ok", 0 }, - { "/discard", 1 }, - { "/no", 1 }, - { "/cancel", 2 } - }; + GtkAllocation allocation; + gtk_widget_realize(GTK_WIDGET(m_pWindow)); + gtk_widget_get_allocation(GTK_WIDGET(m_pWindow), &allocation); + gtk_widget_size_allocate(GTK_WIDGET(m_pWindow), &allocation); + } - const ButtonOrder* pOrder = &aDiscardCancelSave[0]; + rOutput.SetOutputSizePixel(get_size()); + cairo_surface_t* pSurface = get_underlying_cairo_surface(rOutput); + cairo_t* cr = cairo_create(pSurface); - const OUString &rEnv = Application::GetDesktopEnvironment(); + Point aOffset = get_csd_offset(GTK_WIDGET(m_pWindow)); - if (rEnv.equalsIgnoreAsciiCase("windows") || - rEnv.equalsIgnoreAsciiCase("tde") || - rEnv.startsWithIgnoreAsciiCase("kde")) - { - pOrder = &aSaveDiscardCancel[0]; - } +#if defined(GDK_WINDOWING_X11) + GdkDisplay *pDisplay = gtk_widget_get_display(GTK_WIDGET(m_pWindow)); + if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay)) + assert(aOffset.X() == 0 && aOffset.Y() == 0 && "expected offset of 0 under X"); +#endif - for (size_t i = 0; i < N_TYPES; ++i, ++pOrder) - { - if (rType.endsWith(pOrder->m_aType)) - return pOrder->m_nPriority; - } + cairo_translate(cr, -aOffset.X(), -aOffset.Y()); - return -1; - } + gtk_widget_draw(GTK_WIDGET(m_pWindow), cr); - bool sortButtons(const GtkWidget* pA, const GtkWidget* pB) - { - //order within groups according to platform rules - return getButtonPriority(::get_help_id(pA)) < getButtonPriority(::get_help_id(pB)); + cairo_destroy(cr); + + if (!bAlreadyVisible) + gtk_widget_hide(GTK_WIDGET(m_pWindow)); + if (!bAlreadyRealized) + gtk_widget_unrealize(GTK_WIDGET(m_pWindow)); } - void sort_native_button_order(GtkBox* pContainer) + virtual weld::ScreenShotCollection collect_screenshot_data() override { - std::vector<GtkWidget*> aChildren; - GList* pChildren = gtk_container_get_children(GTK_CONTAINER(pContainer)); - for (GList* pChild = g_list_first(pChildren); pChild; pChild = g_list_next(pChild)) - aChildren.push_back(static_cast<GtkWidget*>(pChild->data)); - g_list_free(pChildren); + weld::ScreenShotCollection aRet; - //sort child order within parent so that we match the platform button order - std::stable_sort(aChildren.begin(), aChildren.end(), sortButtons); + gtk_container_foreach(GTK_CONTAINER(m_pWindow), do_collect_screenshot_data, &aRet); - for (size_t pos = 0; pos < aChildren.size(); ++pos) - gtk_box_reorder_child(pContainer, aChildren[pos], pos); + return aRet; } -} + + virtual ~GtkInstanceWindow() override + { + if (m_nToplevelFocusChangedSignalId) + g_signal_handler_disconnect(m_pWindow, m_nToplevelFocusChangedSignalId); + if (m_xWindow.is()) + m_xWindow->clear(); + } +}; class GtkInstanceDialog; @@ -3861,54 +3962,6 @@ private: return false; } - static Point get_csd_offset(GtkWidget* pTopLevel) - { - // try and omit drawing CSD under wayland - GList* pChildren = gtk_container_get_children(GTK_CONTAINER(pTopLevel)); - GList* pChild = g_list_first(pChildren); - - int x, y; - gtk_widget_translate_coordinates(GTK_WIDGET(pChild->data), - GTK_WIDGET(pTopLevel), - 0, 0, &x, &y); - - int innerborder = gtk_container_get_border_width(GTK_CONTAINER(pChild->data)); - g_list_free(pChildren); - - int outerborder = gtk_container_get_border_width(GTK_CONTAINER(pTopLevel)); - int totalborder = outerborder + innerborder; - x -= totalborder; - y -= totalborder; - - return Point(x, y); - } - - static void do_collect_screenshot_data(GtkWidget* pItem, gpointer data) - { - GtkWidget* pTopLevel = gtk_widget_get_toplevel(pItem); - - int x, y; - gtk_widget_translate_coordinates(pItem, pTopLevel, 0, 0, &x, &y); - - Point aOffset = get_csd_offset(pTopLevel); - - GtkAllocation alloc; - gtk_widget_get_allocation(pItem, &alloc); - - const basegfx::B2IPoint aCurrentTopLeft(x - aOffset.X(), y - aOffset.Y()); - const basegfx::B2IRange aCurrentRange(aCurrentTopLeft, aCurrentTopLeft + basegfx::B2IPoint(alloc.width, alloc.height)); - - if (!aCurrentRange.isEmpty()) - { - weld::ScreenShotCollection* pCollection = static_cast<weld::ScreenShotCollection*>(data); - pCollection->emplace_back(::get_help_id(pItem), aCurrentRange); - } - - if (GTK_IS_CONTAINER(pItem)) - gtk_container_forall(GTK_CONTAINER(pItem), do_collect_screenshot_data, data); - } - - public: GtkInstanceDialog(GtkWindow* pDialog, GtkInstanceBuilder* pBuilder, bool bTakeOwnership) : GtkInstanceWindow(pDialog, pBuilder, bTakeOwnership) @@ -4125,59 +4178,6 @@ public: //not implemented for the gtk variant } - virtual void draw(VirtualDevice& rOutput) override - { - // detect if we have to manually setup its size - bool bAlreadyRealized = gtk_widget_get_realized(GTK_WIDGET(m_pDialog)); - // has to be visible for draw to work - bool bAlreadyVisible = gtk_widget_get_visible(GTK_WIDGET(m_pDialog)); - if (!bAlreadyVisible) - { - sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(m_pDialog)))); - gtk_widget_show(GTK_WIDGET(m_pDialog)); - } - - if (!bAlreadyRealized) - { - GtkAllocation allocation; - gtk_widget_realize(GTK_WIDGET(m_pDialog)); - gtk_widget_get_allocation(GTK_WIDGET(m_pDialog), &allocation); - gtk_widget_size_allocate(GTK_WIDGET(m_pDialog), &allocation); - } - - rOutput.SetOutputSizePixel(get_size()); - cairo_surface_t* pSurface = get_underlying_cairo_surface(rOutput); - cairo_t* cr = cairo_create(pSurface); - - Point aOffset = get_csd_offset(GTK_WIDGET(m_pDialog)); - -#if defined(GDK_WINDOWING_X11) - GdkDisplay *pDisplay = gtk_widget_get_display(GTK_WIDGET(m_pDialog)); - if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay)) - assert(aOffset.X() == 0 && aOffset.Y() == 0 && "expected offset of 0 under X"); -#endif - - cairo_translate(cr, -aOffset.X(), -aOffset.Y()); - - gtk_widget_draw(GTK_WIDGET(m_pDialog), cr); - - cairo_destroy(cr); - - if (!bAlreadyVisible) - gtk_widget_hide(GTK_WIDGET(m_pDialog)); - if (!bAlreadyRealized) - gtk_widget_unrealize(GTK_WIDGET(m_pDialog)); - } - - virtual weld::ScreenShotCollection collect_screenshot_data() override - { - weld::ScreenShotCollection aRet; - - gtk_container_foreach(GTK_CONTAINER(m_pDialog), do_collect_screenshot_data, &aRet); - - return aRet; - } - virtual ~GtkInstanceDialog() override { if (!m_aHiddenWidgets.empty()) @@ -11748,7 +11748,6 @@ public: return sPageHelpId; } - virtual ~GtkInstanceBuilder() override { g_slist_free(m_pObjectList); @@ -11795,12 +11794,50 @@ public: virtual std::unique_ptr<weld::Dialog> weld_dialog(const OString &id, bool bTakeOwnership) override { - GtkDialog* pDialog = GTK_DIALOG(gtk_builder_get_object(m_pBuilder, id.getStr())); + GtkWindow* pDialog = GTK_WINDOW(gtk_builder_get_object(m_pBuilder, id.getStr())); if (!pDialog) return nullptr; if (m_pParentWidget) - gtk_window_set_transient_for(GTK_WINDOW(pDialog), GTK_WINDOW(gtk_widget_get_toplevel(m_pParentWidget))); - return std::make_unique<GtkInstanceDialog>(GTK_WINDOW(pDialog), this, bTakeOwnership); + gtk_window_set_transient_for(pDialog, GTK_WINDOW(gtk_widget_get_toplevel(m_pParentWidget))); + return std::make_unique<GtkInstanceDialog>(pDialog, this, bTakeOwnership); + } + + virtual std::unique_ptr<weld::Window> create_screenshot_window() override + { + GtkWidget* pTopLevel = nullptr; + + for (GSList* l = m_pObjectList; l; l = g_slist_next(l)) + { + GObject* pObj = static_cast<GObject*>(l->data); + + if (!GTK_IS_WIDGET(pObj) || gtk_widget_get_parent(GTK_WIDGET(pObj))) + continue; + + if (!pTopLevel) + pTopLevel = GTK_WIDGET(pObj); + else if (GTK_IS_WINDOW(pObj)) + pTopLevel = GTK_WIDGET(pObj); + } + + if (!pTopLevel) + return nullptr; + + GtkWindow* pDialog; + if (GTK_IS_WINDOW(pTopLevel)) + pDialog = GTK_WINDOW(pTopLevel); + else + { + pDialog = GTK_WINDOW(gtk_dialog_new()); + ::set_help_id(GTK_WIDGET(pDialog), ::get_help_id(pTopLevel)); + + GtkWidget* pContentArea = gtk_dialog_get_content_area(GTK_DIALOG(pDialog)); + gtk_container_add(GTK_CONTAINER(pContentArea), pTopLevel); + gtk_widget_show_all(pTopLevel); + } + + if (m_pParentWidget) + gtk_window_set_transient_for(pDialog, GTK_WINDOW(gtk_widget_get_toplevel(m_pParentWidget))); + return std::make_unique<GtkInstanceDialog>(pDialog, this, true); } virtual std::unique_ptr<weld::Window> weld_window(const OString &id, bool bTakeOwnership) override |