mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-14 00:26:00 +08:00
Add an option to show "rich tooltip" instead of system tooltip for setting controls.
supermerill/SuperSlicer#1720 supermerill/SuperSlicer#1291
This commit is contained in:
parent
f44e05091c
commit
b6f5dc40c5
@ -194,6 +194,15 @@ void AppConfig::set_defaults()
|
||||
|
||||
if (get("default_action_on_new_project").empty())
|
||||
set("default_action_on_new_project", "1");
|
||||
|
||||
if (get("use_rich_tooltip").empty())
|
||||
set("use_rich_tooltip",
|
||||
#ifndef WIN32
|
||||
"1"
|
||||
#else
|
||||
"0"
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
else {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <wx/numformatter.h>
|
||||
#include <wx/tooltip.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/richtooltip.h>
|
||||
#include <wx/tokenzr.h>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include "OG_CustomCtrl.hpp"
|
||||
@ -212,6 +213,77 @@ wxString Field::get_tooltip_text(const wxString& default_string)
|
||||
return tooltip_text;
|
||||
}
|
||||
|
||||
wxString Field::get_rich_tooltip_text(const wxString& default_string)
|
||||
{
|
||||
wxString tooltip_text("");
|
||||
wxString tooltip = _(m_opt.tooltip);
|
||||
update_Slic3r_string(tooltip);
|
||||
|
||||
std::string opt_id = m_opt_id;
|
||||
auto hash_pos = opt_id.find("#");
|
||||
if (hash_pos != std::string::npos) {
|
||||
opt_id.replace(hash_pos, 1, "[");
|
||||
opt_id += "]";
|
||||
}
|
||||
|
||||
if (tooltip.length() > 0)
|
||||
tooltip_text = tooltip + "\n" + _(L("default value")) + ": " +
|
||||
(boost::iends_with(opt_id, "_gcode") ? "\n" : "") + default_string;
|
||||
|
||||
return tooltip_text;
|
||||
}
|
||||
|
||||
wxString Field::get_rich_tooltip_title(const wxString& default_string)
|
||||
{
|
||||
|
||||
std::string opt_id = m_opt_id;
|
||||
auto hash_pos = opt_id.find("#");
|
||||
if (hash_pos != std::string::npos) {
|
||||
opt_id.replace(hash_pos, 1, "[");
|
||||
opt_id += "]";
|
||||
}
|
||||
|
||||
return opt_id + ":";
|
||||
}
|
||||
|
||||
void Field::set_tooltip(const wxString& default_string, wxWindow* window) {
|
||||
if (window == nullptr)
|
||||
window = getWindow();
|
||||
if (get_app_config()->get("use_rich_tooltip") == "1") {
|
||||
this->m_rich_tooltip_timer.m_value = default_string;
|
||||
window->Bind(wxEVT_ENTER_WINDOW, [this, window](wxMouseEvent& event) {
|
||||
if (wxGetActiveWindow() && !this->m_rich_tooltip_timer.IsRunning()) {
|
||||
this->m_rich_tooltip_timer.m_current_window = window;
|
||||
this->m_rich_tooltip_timer.m_is_rich_tooltip_ready = true;
|
||||
this->m_rich_tooltip_timer.StartOnce(500);
|
||||
}
|
||||
});
|
||||
window->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) {
|
||||
this->m_rich_tooltip_timer.m_is_rich_tooltip_ready = false;
|
||||
wxWindowList tipWindow = this->getWindow()->GetChildren();
|
||||
if (tipWindow.size() > 0) {
|
||||
wxWindow* tooltipWindow = tipWindow.GetLast()->GetData();
|
||||
if (tooltipWindow && tooltipWindow == this->m_rich_tooltip_timer.m_current_rich_tooltip)
|
||||
tooltipWindow->Hide();// DismissAndNotify();
|
||||
}
|
||||
});
|
||||
}else
|
||||
window->SetToolTip(get_tooltip_text(default_string));
|
||||
}
|
||||
|
||||
void RichTooltipTimer::Notify() {
|
||||
if (wxGetActiveWindow() && this->m_is_rich_tooltip_ready && m_current_window) {
|
||||
this->m_current_rich_tooltip = nullptr;
|
||||
wxRichToolTip richTooltip(
|
||||
m_field->get_rich_tooltip_title(this->m_value),
|
||||
m_field->get_rich_tooltip_text(this->m_value));
|
||||
richTooltip.SetTimeout(120000, 0);
|
||||
richTooltip.ShowFor(m_current_window);
|
||||
wxWindowList tipWindow = m_current_window->GetChildren();
|
||||
this->m_current_rich_tooltip = tipWindow.GetLast()->GetData();
|
||||
}
|
||||
}
|
||||
|
||||
bool Field::is_matched(const std::string& string, const std::string& pattern)
|
||||
{
|
||||
std::regex regex_pattern(pattern, std::regex_constants::icase); // use ::icase to make the matching case insensitive like /i in perl
|
||||
@ -511,7 +583,7 @@ void TextCtrl::BUILD() {
|
||||
m_last_meaningful_value = text_value;
|
||||
|
||||
const long style = m_opt.multiline ? wxTE_MULTILINE : wxTE_PROCESS_ENTER/*0*/;
|
||||
auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style);
|
||||
wxTextCtrl* temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style);
|
||||
if (parent_is_custom_ctrl && m_opt.height < 0)
|
||||
opt_height = (double)temp->GetSize().GetHeight()/m_em_unit;
|
||||
temp->SetFont(m_opt.is_code ?
|
||||
@ -526,8 +598,6 @@ void TextCtrl::BUILD() {
|
||||
temp->OSXDisableAllSmartSubstitutions();
|
||||
#endif // __WXOSX__
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(text_value));
|
||||
|
||||
if (style == wxTE_PROCESS_ENTER) {
|
||||
temp->Bind(wxEVT_TEXT_ENTER, ([this, temp](wxEvent& e)
|
||||
{
|
||||
@ -590,6 +660,8 @@ void TextCtrl::BUILD() {
|
||||
*/
|
||||
// recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
|
||||
this->set_tooltip(text_value);
|
||||
}
|
||||
|
||||
bool TextCtrl::value_was_changed()
|
||||
@ -736,10 +808,10 @@ void CheckBox::BUILD() {
|
||||
on_change_field();
|
||||
}), temp->GetId());
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(check_value ? "true" : "false"));
|
||||
|
||||
// recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
|
||||
this->set_tooltip(check_value ? "true" : "false");
|
||||
}
|
||||
|
||||
void CheckBox::set_value(const boost::any& value, bool change_event)
|
||||
@ -899,11 +971,12 @@ void SpinCtrl::BUILD() {
|
||||
}
|
||||
#endif
|
||||
}), temp->GetId());
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(text_value));
|
||||
|
||||
// recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
|
||||
//prblem: it has 2 window, with a child: the mouse enter event won't fire if in children!
|
||||
this->set_tooltip(text_value);
|
||||
}
|
||||
|
||||
void SpinCtrl::propagate_value()
|
||||
@ -1057,7 +1130,7 @@ void Choice::BUILD() {
|
||||
}), temp->GetId());
|
||||
}
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(temp->GetValue()));
|
||||
this->set_tooltip(temp->GetValue());
|
||||
}
|
||||
|
||||
void Choice::suppress_scroll()
|
||||
@ -1475,9 +1548,9 @@ void ColourPicker::BUILD()
|
||||
// // recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
|
||||
temp->Bind(wxEVT_COLOURPICKER_CHANGED, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
||||
window->Bind(wxEVT_COLOURPICKER_CHANGED, ([this](wxCommandEvent e) { on_change_field(); }), window->GetId());
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(clr.GetAsString()));
|
||||
this->set_tooltip(clr.GetAsString());
|
||||
}
|
||||
|
||||
void ColourPicker::set_undef_value(wxColourPickerCtrl* field)
|
||||
@ -1588,8 +1661,8 @@ void PointCtrl::BUILD()
|
||||
// // recast as a wxWindow to fit the calling convention
|
||||
sizer = dynamic_cast<wxSizer*>(temp);
|
||||
|
||||
x_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
|
||||
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
|
||||
this->set_tooltip(X + ", " + Y, x_textctrl);
|
||||
this->set_tooltip(X + ", " + Y, y_textctrl);
|
||||
}
|
||||
|
||||
void PointCtrl::msw_rescale()
|
||||
@ -1695,7 +1768,7 @@ void StaticText::BUILD()
|
||||
// // recast as a wxWindow to fit the calling convention
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(legend));
|
||||
this->set_tooltip(legend);
|
||||
}
|
||||
|
||||
void StaticText::msw_rescale()
|
||||
|
@ -39,6 +39,21 @@ using t_back_to_init = std::function<void(const std::string&)>;
|
||||
wxString double_to_string(double const value, const int max_precision = 6);
|
||||
wxString get_points_string(const std::vector<Vec2d>& values);
|
||||
|
||||
class Field;
|
||||
class RichTooltipTimer : public wxTimer
|
||||
{
|
||||
Field* m_field;
|
||||
public:
|
||||
bool m_is_rich_tooltip_ready = false;
|
||||
wxWindow* m_current_rich_tooltip = nullptr;
|
||||
wxString m_value;
|
||||
wxWindow* m_window2 = nullptr; //for point
|
||||
wxWindow* m_current_window = nullptr; //for point
|
||||
RichTooltipTimer(Field* field) : m_field(field) {};
|
||||
|
||||
void Notify() override;
|
||||
};
|
||||
|
||||
class Field {
|
||||
protected:
|
||||
// factory function to defer and enforce creation of derived type.
|
||||
@ -110,11 +125,16 @@ public:
|
||||
inline void toggle(bool en) { en ? enable() : disable(); }
|
||||
|
||||
virtual wxString get_tooltip_text(const wxString& default_string);
|
||||
// hack via richtooltip that are also hacked
|
||||
RichTooltipTimer m_rich_tooltip_timer;
|
||||
virtual wxString get_rich_tooltip_text(const wxString& default_string);
|
||||
virtual wxString get_rich_tooltip_title(const wxString& default_string);
|
||||
void set_tooltip(const wxString& default_string, wxWindow* window = nullptr);
|
||||
|
||||
void field_changed() { on_change_field(); }
|
||||
|
||||
Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {};
|
||||
Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {};
|
||||
Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id), m_rich_tooltip_timer(this) {};
|
||||
Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id), m_rich_tooltip_timer(this) {};
|
||||
virtual ~Field();
|
||||
|
||||
/// If you don't know what you are getting back, check both methods for nullptr.
|
||||
|
@ -151,6 +151,7 @@ void PreferencesDialog::build()
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("no_defaults") == "1" });
|
||||
option = Option(def, "no_defaults");
|
||||
m_optgroups_general.back()->append_single_option_line(option);
|
||||
m_values_need_restart.push_back("no_defaults");
|
||||
|
||||
def.label = L("Show incompatible print and filament presets");
|
||||
def.type = coBool;
|
||||
@ -263,6 +264,21 @@ void PreferencesDialog::build()
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_new_project") == "1" });
|
||||
option = Option(def, "default_action_on_new_project");
|
||||
m_optgroups_general.back()->append_single_option_line(option);
|
||||
|
||||
def.label = L("Use custom tooltip");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("On some OS like MacOS or some Linux, tooltips can't stay on for a long time. This setting replaces native tooltips with custom dialogs to improve readability (only for settings)."
|
||||
"\nNote that for the number controls, you need to hover the arrows to get the custom tooltip.");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->has("use_rich_tooltip") ? app_config->get("use_rich_tooltip") == "1":
|
||||
#if __APPLE__
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
});
|
||||
option = Option(def, "use_rich_tooltip");
|
||||
m_optgroups_general.back()->append_single_option_line(option);
|
||||
m_values_need_restart.push_back("use_rich_tooltip");
|
||||
}
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
#ifdef _WIN32
|
||||
@ -445,12 +461,12 @@ void PreferencesDialog::build()
|
||||
|
||||
def.label = L("Tab icon size");
|
||||
def.type = coInt;
|
||||
def.tooltip = L("Size of the tab icons, in pixels. Set to 0 to remove icons."
|
||||
"\nYou have to restart the application before any change will be taken into account.");
|
||||
def.tooltip = L("Size of the tab icons, in pixels. Set to 0 to remove icons.");
|
||||
def.set_default_value(new ConfigOptionInt{ atoi(app_config->get("tab_icon_size").c_str()) });
|
||||
option = Option(def, "tab_icon_size");
|
||||
option.opt.width = 6;
|
||||
m_optgroup_gui->append_single_option_line(option);
|
||||
m_values_need_restart.push_back("tab_icon_size");
|
||||
}
|
||||
|
||||
|
||||
@ -465,54 +481,54 @@ void PreferencesDialog::build()
|
||||
def.type = coString;
|
||||
def.tooltip = _u8L("Very dark color, in the RGB hex format.")
|
||||
+ " " + _u8L("Mainly used as background or dark text color.")
|
||||
+ "\n" + _u8L("You have to restart the application before any change will be taken into account.")
|
||||
+ "\n" + _u8L("Slic3r(yellow): ada230, PrusaSlicer(orange): c46737, SuperSlicer(blue): 0047c7");
|
||||
def.set_default_value(new ConfigOptionString{ app_config->get("color_very_dark") });
|
||||
option = Option(def, "color_very_dark");
|
||||
option.opt.width = 6;
|
||||
m_optgroup_gui->append_single_option_line(option);
|
||||
m_values_need_restart.push_back("color_very_dark");
|
||||
|
||||
def.label = L("Dark gui color");
|
||||
def.type = coString;
|
||||
def.tooltip = _u8L("Dark color, in the RGB hex format.")
|
||||
+ " " + _u8L("Mainly used as icon color.")
|
||||
+ "\n" + _u8L("You have to restart the application before any change will be taken into account.")
|
||||
+ "\n" + _u8L("Slic3r(yellow): cabe39, PrusaSlicer(orange): ed6b21, SuperSlicer(blue): 2172eb");
|
||||
def.set_default_value(new ConfigOptionString{ app_config->get("color_dark") });
|
||||
option = Option(def, "color_dark");
|
||||
option.opt.width = 6;
|
||||
m_optgroup_gui->append_single_option_line(option);
|
||||
m_values_need_restart.push_back("color_dark");
|
||||
|
||||
def.label = L("Gui color");
|
||||
def.type = coString;
|
||||
def.tooltip = _u8L("Main color, in the RGB hex format.")
|
||||
+ "\n" + _u8L("You have to restart the application before any change will be taken into account.")
|
||||
+ " " + _u8L("Slic3r(yellow): eddc21, PrusaSlicer(orange): fd7e42, SuperSlicer(blue): 428dfd");
|
||||
def.set_default_value(new ConfigOptionString{ app_config->get("color") });
|
||||
option = Option(def, "color");
|
||||
option.opt.width = 6;
|
||||
m_optgroup_gui->append_single_option_line(option);
|
||||
m_values_need_restart.push_back("color");
|
||||
|
||||
def.label = L("Light gui color");
|
||||
def.type = coString;
|
||||
def.tooltip = _u8L("Light color, in the RGB hex format.")
|
||||
+ "\n" + _u8L("You have to restart the application before any change will be taken into account.")
|
||||
+ " " + _u8L("Slic3r(yellow): ffee38, PrusaSlicer(orange): feac8b, SuperSlicer(blue): 8bb9fe");
|
||||
def.set_default_value(new ConfigOptionString{ app_config->get("color_light") });
|
||||
option = Option(def, "color_light");
|
||||
option.opt.width = 6;
|
||||
m_optgroup_gui->append_single_option_line(option);
|
||||
m_values_need_restart.push_back("color_light");
|
||||
|
||||
def.label = L("Very light gui color");
|
||||
def.type = coString;
|
||||
def.tooltip = _u8L("Very light color, in the RGB hex format.")
|
||||
+ " " + _u8L("Mainly used as light text color.")
|
||||
+ "\n" + _u8L("You have to restart the application before any change will be taken into account.")
|
||||
+ "\n" + _u8L("Slic3r(yellow): fef48b, PrusaSlicer(orange): ff7d38, SuperSlicer(blue): 428cff");
|
||||
def.set_default_value(new ConfigOptionString{ app_config->get("color_very_light") });
|
||||
option = Option(def, "color_very_light");
|
||||
option.opt.width = 6;
|
||||
m_optgroup_gui->append_single_option_line(option);
|
||||
m_values_need_restart.push_back("color_very_light");
|
||||
|
||||
activate_options_tab(m_optgroup_gui);
|
||||
|
||||
@ -557,7 +573,11 @@ void PreferencesDialog::build()
|
||||
|
||||
void PreferencesDialog::accept()
|
||||
{
|
||||
if (m_values.find("no_defaults") != m_values.end()) {
|
||||
bool need_restart = false;
|
||||
for (auto key : m_values_need_restart)
|
||||
if (m_values.find(key) != m_values.end())
|
||||
need_restart = true;
|
||||
if (need_restart) {
|
||||
warning_catcher(this, wxString::Format(_L("You need to restart %s to make the changes effective."), SLIC3R_APP_NAME));
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ class ConfigOptionsGroup;
|
||||
class PreferencesDialog : public DPIDialog
|
||||
{
|
||||
std::map<std::string, std::string> m_values;
|
||||
std::vector<std::string> m_values_need_restart;
|
||||
std::vector<std::shared_ptr<ConfigOptionsGroup>> m_optgroups_general;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_paths;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_camera;
|
||||
|
Loading…
x
Reference in New Issue
Block a user