Setting full size of the dialog before showing it doesn't work, but setting its client size does. It could be better to change wxWindow::Fit() itself to do this, but this might have other effects, while this change should be pretty safe, i.e. not break anything for the other platforms while fixing a big usability problem with wxGTK/Wayland: as this dialog is not resizeable, making it too small initially, as happened before, meant that parts of it couldn't be used at all.
263 lines
6.8 KiB
C++
263 lines
6.8 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/generic/preferencesg.cpp
|
|
// Purpose: Implementation of wxPreferencesEditor.
|
|
// Author: Vaclav Slavik
|
|
// Created: 2013-02-19
|
|
// Copyright: (c) 2013 Vaclav Slavik <vslavik@fastmail.fm>
|
|
// Licence: wxWindows licence
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// for compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
|
|
#if wxUSE_PREFERENCES_EDITOR
|
|
|
|
#include "wx/private/preferences.h"
|
|
|
|
#ifndef wxHAS_PREF_EDITOR_NATIVE
|
|
|
|
#include "wx/app.h"
|
|
#include "wx/dialog.h"
|
|
#include "wx/notebook.h"
|
|
#include "wx/sizer.h"
|
|
#include "wx/scopeguard.h"
|
|
|
|
#include <memory>
|
|
|
|
namespace
|
|
{
|
|
|
|
class wxGenericPrefsDialog : public wxDialog
|
|
{
|
|
public:
|
|
wxGenericPrefsDialog(wxWindow *parent, const wxString& title)
|
|
: wxDialog(parent, wxID_ANY, title,
|
|
wxDefaultPosition, wxDefaultSize,
|
|
wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX))
|
|
{
|
|
wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
m_notebook = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_MULTILINE);
|
|
sizer->Add(m_notebook, wxSizerFlags(1).Expand().DoubleBorder());
|
|
|
|
#ifdef __WXGTK__
|
|
SetEscapeId(wxID_CLOSE);
|
|
sizer->Add(CreateButtonSizer(wxCLOSE), wxSizerFlags().Expand().DoubleBorder(wxBOTTOM));
|
|
#else
|
|
sizer->Add(CreateButtonSizer(wxOK | wxCANCEL),
|
|
wxSizerFlags().Expand().DoubleBorder(wxLEFT|wxRIGHT|wxBOTTOM));
|
|
#endif
|
|
SetSizer(sizer);
|
|
|
|
m_notebook->SetFocus();
|
|
}
|
|
|
|
void AddPage(wxPreferencesPage *page)
|
|
{
|
|
wxWindow *win = page->CreateWindow(m_notebook);
|
|
m_notebook->AddPage(win, page->GetName());
|
|
}
|
|
|
|
int GetSelectedPage() const
|
|
{
|
|
return m_notebook->GetSelection();
|
|
}
|
|
|
|
void SelectPage(int page)
|
|
{
|
|
m_notebook->ChangeSelection(page);
|
|
}
|
|
|
|
bool ShouldPreventAppExit() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void FitPages()
|
|
{
|
|
SetClientSize(GetSizer()->GetMinSize());
|
|
}
|
|
|
|
private:
|
|
wxNotebook *m_notebook;
|
|
};
|
|
|
|
|
|
class wxGenericPreferencesEditorImplBase : public wxPreferencesEditorImpl
|
|
{
|
|
public:
|
|
void SetTitle(const wxString& title)
|
|
{
|
|
m_title = title;
|
|
}
|
|
|
|
virtual void AddPage(wxPreferencesPage* page) override
|
|
{
|
|
m_pages.emplace_back(page);
|
|
}
|
|
|
|
protected:
|
|
wxGenericPrefsDialog *CreateDialog(wxWindow *parent)
|
|
{
|
|
if ( m_title.empty() )
|
|
{
|
|
// Use the default title, which should include the application name
|
|
// under both MSW and GTK (and OSX uses its own native
|
|
// implementation anyhow).
|
|
m_title.Printf(_("%s Preferences"), wxTheApp->GetAppDisplayName());
|
|
}
|
|
|
|
wxGenericPrefsDialog *dlg = new wxGenericPrefsDialog(parent, m_title);
|
|
|
|
// TODO: Don't create all pages immediately like this, do it on demand
|
|
// when a page is selected in the notebook (as is done on OS X).
|
|
//
|
|
// Currently, creating all pages is necessary so that the notebook
|
|
// can determine its best size. We'll need to extend
|
|
// wxPreferencesPage with a GetBestSize() virtual method to make
|
|
// it possible to defer the creation.
|
|
for ( const auto& page : m_pages )
|
|
{
|
|
dlg->AddPage(page.get());
|
|
}
|
|
|
|
dlg->FitPages();
|
|
|
|
return dlg;
|
|
}
|
|
|
|
std::vector<std::unique_ptr<wxPreferencesPage>> m_pages;
|
|
|
|
private:
|
|
wxString m_title;
|
|
};
|
|
|
|
|
|
#ifdef wxHAS_PREF_EDITOR_MODELESS
|
|
|
|
class wxModelessPreferencesEditorImpl : public wxGenericPreferencesEditorImplBase
|
|
{
|
|
public:
|
|
virtual ~wxModelessPreferencesEditorImpl()
|
|
{
|
|
// m_win may already be destroyed if this destructor is called from
|
|
// wxApp's destructor. In that case, all windows -- including this
|
|
// one -- would already be destroyed by now.
|
|
if ( m_win )
|
|
m_win->Destroy();
|
|
}
|
|
|
|
virtual void Show(wxWindow* parent) override
|
|
{
|
|
if ( !m_win )
|
|
{
|
|
wxWindow *win = CreateDialog(parent);
|
|
win->Show();
|
|
m_win = win;
|
|
}
|
|
else
|
|
{
|
|
// Ideally, we'd reparent the dialog under 'parent', but it's
|
|
// probably not worth the hassle. We know the old parent is still
|
|
// valid, because otherwise Dismiss() would have been called and
|
|
// m_win cleared.
|
|
m_win->Raise();
|
|
}
|
|
}
|
|
|
|
virtual void Dismiss() override
|
|
{
|
|
if ( m_win )
|
|
{
|
|
m_win->Close(/*force=*/true);
|
|
m_win = nullptr;
|
|
}
|
|
}
|
|
|
|
private:
|
|
wxWeakRef<wxWindow> m_win;
|
|
};
|
|
|
|
inline
|
|
wxGenericPreferencesEditorImplBase* NewGenericImpl()
|
|
{
|
|
return new wxModelessPreferencesEditorImpl;
|
|
}
|
|
|
|
#else // !wxHAS_PREF_EDITOR_MODELESS
|
|
|
|
class wxModalPreferencesEditorImpl : public wxGenericPreferencesEditorImplBase
|
|
{
|
|
public:
|
|
wxModalPreferencesEditorImpl()
|
|
{
|
|
m_dlg = nullptr;
|
|
m_currentPage = -1;
|
|
}
|
|
|
|
virtual void Show(wxWindow* parent) override
|
|
{
|
|
std::unique_ptr<wxGenericPrefsDialog> dlg(CreateDialog(parent));
|
|
|
|
// Store it for Dismiss() but ensure that the pointer is reset to nullptr
|
|
// when the dialog is destroyed on leaving this function.
|
|
m_dlg = dlg.get();
|
|
wxON_BLOCK_EXIT_NULL(m_dlg);
|
|
|
|
// Restore the previously selected page, if any.
|
|
if ( m_currentPage != -1 )
|
|
dlg->SelectPage(m_currentPage);
|
|
|
|
// Don't remember the last selected page if the dialog was cancelled.
|
|
if ( dlg->ShowModal() != wxID_CANCEL )
|
|
m_currentPage = dlg->GetSelectedPage();
|
|
}
|
|
|
|
virtual void Dismiss() override
|
|
{
|
|
if ( m_dlg )
|
|
{
|
|
m_dlg->EndModal(wxID_CANCEL);
|
|
m_dlg = nullptr;
|
|
}
|
|
}
|
|
|
|
private:
|
|
wxGenericPrefsDialog* m_dlg;
|
|
int m_currentPage;
|
|
|
|
wxDECLARE_NO_COPY_CLASS(wxModalPreferencesEditorImpl);
|
|
};
|
|
|
|
inline
|
|
wxGenericPreferencesEditorImplBase* NewGenericImpl()
|
|
{
|
|
return new wxModalPreferencesEditorImpl;
|
|
}
|
|
|
|
#endif // !wxHAS_PREF_EDITOR_MODELESS
|
|
|
|
} // anonymous namespace
|
|
|
|
/*static*/
|
|
wxPreferencesEditorImpl* wxPreferencesEditorImpl::Create(const wxString& title)
|
|
{
|
|
wxGenericPreferencesEditorImplBase* const impl = NewGenericImpl();
|
|
|
|
impl->SetTitle(title);
|
|
|
|
return impl;
|
|
}
|
|
|
|
#endif // !wxHAS_PREF_EDITOR_NATIVE
|
|
|
|
#endif // wxUSE_PREFERENCES_EDITOR
|