MSW: DarkMode: Improvements for message Dialogs

* A little bit reworked MsgDialog: Checkbox and buttons are placed under the Static line
 * Implemented wrapper for wxRichMessageDialog
 + Implemented wrapper for wxStaticLine
This commit is contained in:
YuSanka 2021-11-05 15:18:10 +01:00
parent 8b150ae235
commit bbcedae5fd
6 changed files with 221 additions and 53 deletions

View file

@ -22,6 +22,7 @@
#include "../GUI/GUI_App.hpp"
#include "../GUI/I18N.hpp"
#include "../GUI/MainFrame.hpp"
#include "../GUI/MsgDialog.hpp"
#include <wx/richmsgdlg.h>
@ -591,7 +592,7 @@ bool take_config_snapshot_cancel_on_error(const AppConfig &app_config, Snapshot:
SnapshotDB::singleton().take_snapshot(app_config, reason, comment);
return true;
} catch (std::exception &err) {
wxRichMessageDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe),
RichMessageDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe),
_L("PrusaSlicer has encountered an error while taking a configuration snapshot.") + "\n\n" + from_u8(err.what()) + "\n\n" + from_u8(message),
_L("PrusaSlicer error"),
wxYES_NO);

View file

@ -555,7 +555,7 @@ PagePrinters::PagePrinters(ConfigWizard *parent,
wizard_p()->on_printer_pick(this, evt);
});
append(new wxStaticLine(this));
append(new StaticLine(this));
append(picker);
printer_pickers.push_back(picker);
@ -2800,11 +2800,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
auto *vsizer = new wxBoxSizer(wxVERTICAL);
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
wxStaticLine* hline = nullptr;
#ifdef _MSW_DARK_MODE
if (!NppDarkMode::IsEnabled())
#endif //_MSW_DARK_MODE
hline = new wxStaticLine(this);
auto* hline = new StaticLine(this);
p->btnsizer = new wxBoxSizer(wxHORIZONTAL);
// Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling.
@ -2880,8 +2876,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
p->index->go_to(size_t{0});
vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
if (hline)
vsizer->Add(hline, 0, wxEXPAND);
vsizer->Add(hline, 0, wxEXPAND | wxLEFT | wxRIGHT, VERTICAL_SPACING);
vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN);
SetSizer(vsizer);

View file

@ -411,7 +411,7 @@ bool static check_old_linux_datadir(const wxString& app_name) {
"location again.\n\n"
"What do you want to do now?")) % SLIC3R_APP_NAME % new_path % old_path).str());
wxString caption = from_u8((boost::format(_u8L("%s - BREAKING CHANGE")) % SLIC3R_APP_NAME).str());
wxRichMessageDialog dlg(nullptr, msg, caption, wxYES_NO);
RichMessageDialog dlg(nullptr, msg, caption, wxYES_NO);
dlg.SetYesNoLabels(_L("Quit, I will move my data now"), _L("Start the application"));
if (dlg.ShowModal() != wxID_NO)
return false;
@ -846,7 +846,7 @@ bool GUI_App::check_older_app_config(Semver current_version, bool backup)
return false;
BOOST_LOG_TRIVIAL(info) << "last app config file used: " << m_older_data_dir_path;
// ask about using older data folder
wxRichMessageDialog msg(nullptr, backup ?
RichMessageDialog msg(nullptr, backup ?
wxString::Format(_L("PrusaSlicer detected another configuration folder at %s."
"\nIts version is %s."
"\nLast version you used in current configuration folder is %s."
@ -936,7 +936,7 @@ bool GUI_App::on_init_inner()
// win32 build on win64 and viceversa
#ifdef _WIN64
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "") {
wxRichMessageDialog dlg(nullptr,
RichMessageDialog dlg(nullptr,
_L("You have started PrusaSlicer for 64-bit architecture on 32-bit system."
"\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/."
"\nDo you wish to continue?"),
@ -946,7 +946,7 @@ bool GUI_App::on_init_inner()
}
#elif _WIN32
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "64") {
wxRichMessageDialog dlg(nullptr,
RichMessageDialog dlg(nullptr,
_L("You have started PrusaSlicer for 32-bit architecture on 64-bit system."
"\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/."
"\nDo you wish to continue?"),
@ -991,7 +991,7 @@ bool GUI_App::on_init_inner()
bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes" && ssl_cert_store == Http::tls_system_cert_store();
if (!msg.empty() && !ssl_accept) {
wxRichMessageDialog
RichMessageDialog
dlg(nullptr,
wxString::Format(_L("%s\nDo you want to continue?"), msg),
"PrusaSlicer", wxICON_QUESTION | wxYES_NO);
@ -2855,7 +2855,7 @@ bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/*
bool launch = true;
if (get_app_config()->get("suppress_hyperlinks").empty()) {
wxRichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO);
RichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO);
dialog.ShowCheckBox(_L("Remember my choice"));
int answer = dialog.ShowModal();
launch = answer == wxID_YES;

View file

@ -27,7 +27,7 @@ namespace GUI {
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id, wxBitmap bitmap)
: wxDialog(parent ? parent : dynamic_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, boldfont(wxGetApp().normal_font()/*wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)*/)
, boldfont(wxGetApp().normal_font())
, content_sizer(new wxBoxSizer(wxVERTICAL))
, btn_sizer(new wxBoxSizer(wxHORIZONTAL))
{
@ -36,6 +36,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
this->SetFont(wxGetApp().normal_font());
this->CenterOnParent();
auto *main_sizer = new wxBoxSizer(wxVERTICAL);
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
auto *rightsizer = new wxBoxSizer(wxVERTICAL);
@ -46,6 +47,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
rightsizer->AddSpacer(VERT_SPACING);
rightsizer->Add(content_sizer, 1, wxEXPAND);
btn_sizer->AddStretchSpacer();
if (button_id != wxID_NONE) {
auto *button = new wxButton(this, button_id);
@ -53,8 +55,6 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
btn_sizer->Add(button);
}
rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT);
if (! bitmap.IsOk()) {
bitmap = create_scaled_bitmap("PrusaSlicer_192px.png", this, 192);
}
@ -64,7 +64,11 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
topsizer->Add(logo, 0, wxALL, BORDER);
topsizer->Add(rightsizer, 1, wxTOP | wxBOTTOM | wxRIGHT | wxEXPAND, BORDER);
SetSizerAndFit(topsizer);
main_sizer->Add(topsizer, 1, wxEXPAND);
main_sizer->Add(new StaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT, HORIZ_SPACING);
main_sizer->Add(btn_sizer, 0, wxALL | wxEXPAND, VERT_SPACING);
SetSizerAndFit(main_sizer);
}
void MsgDialog::add_btn(wxWindowID btn_id, bool set_focus /*= false*/)
@ -72,7 +76,7 @@ void MsgDialog::add_btn(wxWindowID btn_id, bool set_focus /*= false*/)
wxButton* btn = new wxButton(this, btn_id);
if (set_focus)
btn->SetFocus();
btn_sizer->Add(btn, 0, wxRIGHT, HORIZ_SPACING);
btn_sizer->Add(btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, HORIZ_SPACING);
btn->Bind(wxEVT_BUTTON, [this, btn_id](wxCommandEvent&) { this->EndModal(btn_id); });
};
@ -209,33 +213,38 @@ MessageDialog::MessageDialog(wxWindow* parent,
apply_style(style);
finalize();
}
#endif
// MessageWithCheckDialog
// RichMessageDialog
MessageWithCheckDialog::MessageWithCheckDialog( wxWindow* parent,
const wxString& message,
const wxString& checkbox_label,
const wxString& caption/* = wxEmptyString*/,
long style/* = wxOK*/)
RichMessageDialog::RichMessageDialog(wxWindow* parent,
const wxString& message,
const wxString& caption/* = wxEmptyString*/,
long style/* = wxOK*/)
: MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s info"), SLIC3R_APP_NAME) : caption, wxEmptyString, wxID_NONE)
{
add_msg_content(this, content_sizer, message);
m_check = new wxCheckBox(this, wxID_ANY, checkbox_label);
content_sizer->Add(m_check, 0, wxTOP, 10);
m_checkBox = new wxCheckBox(this, wxID_ANY, m_checkBoxText);
m_checkBox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) { m_checkBoxValue = m_checkBox->GetValue(); });
btn_sizer->Insert(0, m_checkBox, wxALIGN_CENTER_VERTICAL);
apply_style(style);
finalize();
}
bool MessageWithCheckDialog::GetCheckVal()
int RichMessageDialog::ShowModal()
{
if (m_check)
return m_check->GetValue();
return false;
if (m_checkBoxText.IsEmpty())
m_checkBox->Hide();
else
m_checkBox->SetLabelText(m_checkBoxText);
Layout();
return wxDialog::ShowModal();
}
#endif
// InfoDialog

View file

@ -8,6 +8,8 @@
#include <wx/font.h>
#include <wx/bitmap.h>
#include <wx/msgdlg.h>
#include <wx/richmsgdlg.h>
#include <wx/textctrl.h>
class wxBoxSizer;
class wxCheckBox;
@ -17,7 +19,6 @@ namespace Slic3r {
namespace GUI {
// A message / query dialog with a bitmap on the left and any content on the right
// with buttons underneath.
struct MsgDialog : wxDialog
@ -87,6 +88,23 @@ public:
};
#ifdef _WIN32
// Generic static line, used intead of wxStaticLine
class StaticLine: public wxTextCtrl
{
public:
StaticLine( wxWindow* parent,
wxWindowID id = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxLI_HORIZONTAL,
const wxString& name = wxASCII_STR(wxTextCtrlNameStr))
: wxTextCtrl(parent, id, wxEmptyString, pos, size!=wxDefaultSize ? size : (style == wxLI_HORIZONTAL ? wxSize(10, 1) : wxSize(1, 10)), wxSIMPLE_BORDER, wxDefaultValidator, name)
{
this->Enable(false);
}
~StaticLine() {}
};
// Generic message dialog, used intead of wxMessageDialog
class MessageDialog : public MsgDialog
{
@ -101,7 +119,158 @@ public:
MessageDialog &operator=(const MessageDialog&) = delete;
virtual ~MessageDialog() = default;
};
// Generic rich message dialog, used intead of wxRichMessageDialog
class RichMessageDialog : public MsgDialog
{
wxCheckBox* m_checkBox{ nullptr };
wxString m_checkBoxText;
bool m_checkBoxValue{ false };
public:
RichMessageDialog( wxWindow *parent,
const wxString& message,
const wxString& caption = wxEmptyString,
long style = wxOK);
RichMessageDialog(RichMessageDialog&&) = delete;
RichMessageDialog(const RichMessageDialog&) = delete;
RichMessageDialog &operator=(RichMessageDialog&&) = delete;
RichMessageDialog &operator=(const RichMessageDialog&) = delete;
virtual ~RichMessageDialog() = default;
int ShowModal() override;
void ShowCheckBox(const wxString& checkBoxText, bool checked = false)
{
m_checkBoxText = checkBoxText;
m_checkBoxValue = checked;
}
wxString GetCheckBoxText() const { return m_checkBoxText; }
bool IsCheckBoxChecked() const { return m_checkBoxValue; }
// This part o fcode isported from the "wx\msgdlg.h"
using wxMD = wxMessageDialogBase;
// customization of the message box buttons
virtual bool SetYesNoLabels(const wxMD::ButtonLabel& yes, const wxMD::ButtonLabel& no)
{
DoSetCustomLabel(m_yes, yes);
DoSetCustomLabel(m_no, no);
return true;
}
virtual bool SetYesNoCancelLabels(const wxMD::ButtonLabel& yes,
const wxMD::ButtonLabel& no,
const wxMD::ButtonLabel& cancel)
{
DoSetCustomLabel(m_yes, yes);
DoSetCustomLabel(m_no, no);
DoSetCustomLabel(m_cancel, cancel);
return true;
}
virtual bool SetOKLabel(const wxMD::ButtonLabel& ok)
{
DoSetCustomLabel(m_ok, ok);
return true;
}
virtual bool SetOKCancelLabels(const wxMD::ButtonLabel& ok,
const wxMD::ButtonLabel& cancel)
{
DoSetCustomLabel(m_ok, ok);
DoSetCustomLabel(m_cancel, cancel);
return true;
}
virtual bool SetHelpLabel(const wxMD::ButtonLabel& help)
{
DoSetCustomLabel(m_help, help);
return true;
}
// test if any custom labels were set
bool HasCustomLabels() const
{
return !(m_ok.empty() && m_cancel.empty() && m_help.empty() &&
m_yes.empty() && m_no.empty());
}
// these functions return the label to be used for the button which is
// either a custom label explicitly set by the user or the default label,
// i.e. they always return a valid string
wxString GetYesLabel() const
{
return m_yes.empty() ? GetDefaultYesLabel() : m_yes;
}
wxString GetNoLabel() const
{
return m_no.empty() ? GetDefaultNoLabel() : m_no;
}
wxString GetOKLabel() const
{
return m_ok.empty() ? GetDefaultOKLabel() : m_ok;
}
wxString GetCancelLabel() const
{
return m_cancel.empty() ? GetDefaultCancelLabel() : m_cancel;
}
wxString GetHelpLabel() const
{
return m_help.empty() ? GetDefaultHelpLabel() : m_help;
}
protected:
// this function is called by our public SetXXXLabels() and should assign
// the value to var with possibly some transformation (e.g. Cocoa version
// currently uses this to remove any accelerators from the button strings
// while GTK+ one handles stock items specifically here)
void DoSetCustomLabel(wxString& var, const wxMD::ButtonLabel& label)
{
var = label.GetAsString();
}
// these functions return the custom label or empty string and should be
// used only in specific circumstances such as creating the buttons with
// these labels (in which case it makes sense to only use a custom label if
// it was really given and fall back on stock label otherwise), use the
// Get{Yes,No,OK,Cancel}Label() methods above otherwise
const wxString& GetCustomYesLabel() const { return m_yes; }
const wxString& GetCustomNoLabel() const { return m_no; }
const wxString& GetCustomOKLabel() const { return m_ok; }
const wxString& GetCustomHelpLabel() const { return m_help; }
const wxString& GetCustomCancelLabel() const { return m_cancel; }
private:
// these functions may be overridden to provide different defaults for the
// default button labels (this is used by wxGTK)
virtual wxString GetDefaultYesLabel() const { return wxGetTranslation("Yes"); }
virtual wxString GetDefaultNoLabel() const { return wxGetTranslation("No"); }
virtual wxString GetDefaultOKLabel() const { return wxGetTranslation("OK"); }
virtual wxString GetDefaultCancelLabel() const { return wxGetTranslation("Cancel"); }
virtual wxString GetDefaultHelpLabel() const { return wxGetTranslation("Help"); }
// labels for the buttons, initially empty meaning that the defaults should
// be used, use GetYes/No/OK/CancelLabel() to access them
wxString m_yes,
m_no,
m_ok,
m_cancel,
m_help;
};
#else
// just a wrapper for wxStaticLine to use the same code on all platforms
class StaticLine : public wxStaticLine
{
public:
StaticLine(wxWindow* parent,
wxWindowID id = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxLI_HORIZONTAL,
const wxString& name = wxASCII_STR(wxStaticLineNameStr))
: wxStaticLine(parent, id, pos, size, style, name) {}
~StaticLine() {}
};
// just a wrapper to wxMessageBox to use the same code on all platforms
class MessageDialog : public wxMessageDialog
{
@ -113,25 +282,19 @@ public:
: wxMessageDialog(parent, message, caption, style) {}
~MessageDialog() {}
};
#endif
class MessageWithCheckDialog : public MsgDialog
// just a wrapper to wxRichMessageBox to use the same code on all platforms
class RichMessageDialog : public wxRichMessageDialog
{
wxCheckBox* m_check{ nullptr };
public:
MessageWithCheckDialog(wxWindow* parent,
RichMessageDialog(wxWindow* parent,
const wxString& message,
const wxString& checkbox_label,
const wxString& caption = wxEmptyString,
long style = wxOK);
MessageWithCheckDialog(MessageWithCheckDialog&&) = delete;
MessageWithCheckDialog(const MessageWithCheckDialog&) = delete;
MessageWithCheckDialog& operator=(MessageWithCheckDialog&&) = delete;
MessageWithCheckDialog& operator=(const MessageWithCheckDialog&) = delete;
virtual ~MessageWithCheckDialog() = default;
bool GetCheckVal();
long style = wxOK)
: wxRichMessageDialog(parent, message, caption, style) {}
~RichMessageDialog() {}
};
#endif
// Generic info dialog, used for displaying exceptions
class InfoDialog : public MsgDialog

View file

@ -2482,15 +2482,15 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
model.convert_from_meters(true);
};
if (answer_convert_from_meters == wxOK_DEFAULT) {
MessageWithCheckDialog dlg(q, format_wxstr(_L_PLURAL(
RichMessageDialog dlg(q, format_wxstr(_L_PLURAL(
"The dimensions of the object from file %s seem to be defined in meters.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?",
"The dimensions of some objects from file %s seem to be defined in meters.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n",
_L("Apply to all the remaining small objects being loaded."),
_L("The object is too small"), wxICON_WARNING | wxYES | wxNO);
dlg.ShowCheckBox(_L("Apply to all the remaining small objects being loaded."));
int answer = dlg.ShowModal();
if (dlg.GetCheckVal())
if (dlg.IsCheckBoxChecked())
answer_convert_from_meters = answer;
else
convert_model_if(model, answer == wxID_YES);
@ -2504,15 +2504,15 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
convert_from_imperial_units(model, true);
};
if (answer_convert_from_imperial_units == wxOK_DEFAULT) {
MessageWithCheckDialog dlg(q, format_wxstr(_L_PLURAL(
RichMessageDialog dlg(q, format_wxstr(_L_PLURAL(
"The dimensions of the object from file %s seem to be defined in inches.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?",
"The dimensions of some objects from file %s seem to be defined in inches.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n",
_L("Apply to all the remaining small objects being loaded."),
_L("The object is too small"), wxICON_WARNING | wxYES | wxNO);
dlg.ShowCheckBox(_L("Apply to all the remaining small objects being loaded."));
int answer = dlg.ShowModal();
if (dlg.GetCheckVal())
if (dlg.IsCheckBoxChecked())
answer_convert_from_imperial_units = answer;
else
convert_model_if(model, answer == wxID_YES);