mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 10:51:22 +00:00
Update translations. Code dedup and cleanup
This commit is contained in:
@@ -141,9 +141,6 @@ std::unique_ptr<AppAction> make_action(const std::string& plugin_key, const std:
|
||||
|
||||
// ---- built-in command actions (the speed dial "commands" section) ------
|
||||
|
||||
constexpr const char* kCommandPrefix = "orca_command";
|
||||
constexpr const char* kOrcaSourceKey = "orca";
|
||||
constexpr const char* kOrcaSourceName = "OrcaSlicer";
|
||||
constexpr const char* kSettingPrefix = "orca_setting";
|
||||
constexpr const char* kPlateGotoPrefix = "orca_plate_goto";
|
||||
constexpr const char* kRecentProjectPrefix = "orca_recent_project";
|
||||
@@ -212,29 +209,28 @@ struct SettingAction : AppAction
|
||||
}
|
||||
};
|
||||
|
||||
// A built-in command action. Thin value: identity + presentation come from the NativeCommands
|
||||
// catalog, and run() routes back to it - the catalog is the single source of truth for its
|
||||
// behaviour. The id is keyed by the stable catalog key (NOT the display title), so a rename or a
|
||||
// UI-language switch never re-keys the action; the title is display-only.
|
||||
struct CommandAction : AppAction
|
||||
// Seed one action's persisted state (favourite flag + frecency counters) from an already-parsed
|
||||
// stats blob and capped favourite list. Shared by the dynamic materialisers.
|
||||
void seed_from(const nlohmann::json& stats, const std::vector<std::string>& favs, const std::string& id, AppAction& a)
|
||||
{
|
||||
static std::unique_ptr<CommandAction> make(const NativeCommand& c) { return std::unique_ptr<CommandAction>(new CommandAction(c)); }
|
||||
|
||||
std::string command_key;
|
||||
|
||||
AppActionRunResult run(const std::string& param) const override { return NativeCommands::run(command_key, param); }
|
||||
|
||||
private:
|
||||
explicit CommandAction(const NativeCommand& c)
|
||||
: AppAction(AppActionId{AppAction::compose_id(kCommandPrefix, c.key, kOrcaSourceKey)}, c.title, kOrcaSourceKey, kOrcaSourceName)
|
||||
, command_key(c.key)
|
||||
{
|
||||
this->kind = AppActionKind::Command;
|
||||
this->group = c.group;
|
||||
this->input = c.input;
|
||||
this->icon = c.icon;
|
||||
a.favourite = std::find(favs.begin(), favs.end(), id) != favs.end();
|
||||
if (auto it = stats.find(id); it != stats.end() && it->is_object()) {
|
||||
a.count = it->value("count", 0);
|
||||
a.last = it->value("last", 0LL);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Drop actions whose id starts with `prefix` but that were not seen in this pass (a stale materialisation).
|
||||
void drop_stale(std::unordered_map<std::string, std::shared_ptr<AppAction>>& actions, const char* prefix,
|
||||
const std::unordered_set<std::string>& seen)
|
||||
{
|
||||
for (auto it = actions.begin(); it != actions.end();) {
|
||||
if (it->first.rfind(prefix, 0) == 0 && !seen.count(it->first))
|
||||
it = actions.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// A dynamic "Go to Plate N" action, one per live plate, rebuilt on every snapshot() (so a
|
||||
// rename/move immediately shows up). id is keyed by plate index, NOT the display title, so
|
||||
@@ -343,10 +339,10 @@ void ActionRegistry::init()
|
||||
|
||||
// Built-in palette commands (Save/Load, Preferences, Mode switch, Slice/Preview, Go to layer).
|
||||
// Register after plugins so the plugin ids win on any (unlikely) id collision - ids are distinct
|
||||
// by prefix, so this is order-independent. The catalog lives in NativeCommands - the registry
|
||||
// only materialises thin CommandAction values from it.
|
||||
// by prefix, so this is order-independent. The catalog (and its thin AppAction adapter) lives in
|
||||
// NativeCommands; the registry only stores and dispatches the result.
|
||||
for (const NativeCommand& c : NativeCommands::catalog())
|
||||
upsert(CommandAction::make(c));
|
||||
upsert(NativeCommands::make_action(c));
|
||||
}
|
||||
|
||||
void ActionRegistry::refresh_source(const std::string& plugin_key, ActionChange change)
|
||||
@@ -429,6 +425,14 @@ void ActionRegistry::seed_state(AppAction& a) const
|
||||
}
|
||||
}
|
||||
|
||||
void ActionRegistry::load_persisted(nlohmann::json& stats, std::vector<std::string>& favs) const
|
||||
{
|
||||
stats = read_section("stats", nlohmann::json::object());
|
||||
if (!stats.is_object())
|
||||
stats = nlohmann::json::object();
|
||||
favs = favourite_ids();
|
||||
}
|
||||
|
||||
// ---- read surface -----------------------------------------------------------
|
||||
|
||||
const AppAction* ActionRegistry::by_id(const std::string& id) const
|
||||
@@ -529,10 +533,9 @@ void ActionRegistry::materialize_setting_actions()
|
||||
|
||||
// Load the persisted per-action state ONCE (not per-option) so a re-materialised setting keeps
|
||||
// its recency/favourite; mirroring seed_state but amortised over the whole option set.
|
||||
nlohmann::json stats = read_section("stats", nlohmann::json::object());
|
||||
if (!stats.is_object())
|
||||
stats = nlohmann::json::object();
|
||||
const std::vector<std::string> favs = favourite_ids();
|
||||
nlohmann::json stats;
|
||||
std::vector<std::string> favs;
|
||||
load_persisted(stats, favs);
|
||||
|
||||
std::unordered_set<std::string> seen;
|
||||
for (const Search::Option& opt : options) {
|
||||
@@ -568,11 +571,7 @@ void ActionRegistry::materialize_setting_actions()
|
||||
}
|
||||
}
|
||||
|
||||
action->favourite = std::find(favs.begin(), favs.end(), id) != favs.end();
|
||||
if (auto it = stats.find(id); it != stats.end() && it->is_object()) {
|
||||
action->count = it->value("count", 0);
|
||||
action->last = it->value("last", 0LL);
|
||||
}
|
||||
seed_from(stats, favs, id, *action);
|
||||
auto const action_id = action->id();
|
||||
auto const app_action = std::shared_ptr<AppAction>(std::move(action));
|
||||
m_actions.insert_or_assign(action_id, app_action);
|
||||
@@ -580,12 +579,7 @@ void ActionRegistry::materialize_setting_actions()
|
||||
|
||||
// Drop SettingActions whose option no longer exists in the current configs (e.g. the printer
|
||||
// technology / UI mode changed). Non-setting actions are untouched.
|
||||
for (auto it = m_actions.begin(); it != m_actions.end();) {
|
||||
if (it->first.rfind(kSettingPrefix, 0) == 0 && !seen.count(it->first))
|
||||
it = m_actions.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
drop_stale(m_actions, kSettingPrefix, seen);
|
||||
}
|
||||
|
||||
void ActionRegistry::materialize_plate_actions()
|
||||
@@ -597,21 +591,15 @@ void ActionRegistry::materialize_plate_actions()
|
||||
Plater* plater = wxTheApp ? wxGetApp().plater() : nullptr;
|
||||
if (!plater || plater->printer_technology() != ptFFF || plater->only_gcode_mode()) {
|
||||
// Drop any stale plate actions (e.g. the printer technology switched to SLA).
|
||||
for (auto it = m_actions.begin(); it != m_actions.end();) {
|
||||
if (it->first.rfind(kPlateGotoPrefix, 0) == 0)
|
||||
it = m_actions.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
drop_stale(m_actions, kPlateGotoPrefix, {});
|
||||
return;
|
||||
}
|
||||
|
||||
// Persisted per-action state, read ONCE (mirrors materialize_setting_actions) so a relisted
|
||||
// "Go to Plate N" keeps its recency/favourite when the plate is renamed - the id is index-keyed.
|
||||
nlohmann::json stats = read_section("stats", nlohmann::json::object());
|
||||
if (!stats.is_object())
|
||||
stats = nlohmann::json::object();
|
||||
const std::vector<std::string> favs = favourite_ids();
|
||||
nlohmann::json stats;
|
||||
std::vector<std::string> favs;
|
||||
load_persisted(stats, favs);
|
||||
|
||||
const std::vector<PartPlate*>& list = plater->get_partplate_list().get_plate_list();
|
||||
std::unordered_set<std::string> seen;
|
||||
@@ -629,24 +617,15 @@ void ActionRegistry::materialize_plate_actions()
|
||||
if (!name.empty())
|
||||
title += " (" + name + ")";
|
||||
|
||||
auto action = std::make_unique<PlateAction>(int(i), title, kOrcaSourceName);
|
||||
action->favourite = std::find(favs.begin(), favs.end(), id) != favs.end();
|
||||
if (auto it = stats.find(id); it != stats.end() && it->is_object()) {
|
||||
action->count = it->value("count", 0);
|
||||
action->last = it->value("last", 0LL);
|
||||
}
|
||||
auto action = std::make_unique<PlateAction>(int(i), title, kOrcaSourceName);
|
||||
seed_from(stats, favs, id, *action);
|
||||
auto const action_id = action->id();
|
||||
auto const app_action = std::shared_ptr<AppAction>(std::move(action));
|
||||
m_actions.insert_or_assign(action_id, app_action);
|
||||
}
|
||||
|
||||
// Drop plate actions whose index no longer exists (a plate was deleted / moved to the front).
|
||||
for (auto it = m_actions.begin(); it != m_actions.end();) {
|
||||
if (it->first.rfind(kPlateGotoPrefix, 0) == 0 && !seen.count(it->first))
|
||||
it = m_actions.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
drop_stale(m_actions, kPlateGotoPrefix, seen);
|
||||
}
|
||||
|
||||
void ActionRegistry::materialize_recent_project_actions()
|
||||
@@ -655,10 +634,9 @@ void ActionRegistry::materialize_recent_project_actions()
|
||||
|
||||
// Persisted per-action state, read ONCE (mirrors materialize_plate_actions) so a relisted recent
|
||||
// project keeps its recency/favourite when the recents list reorders - the id is path-keyed.
|
||||
nlohmann::json stats = read_section("stats", nlohmann::json::object());
|
||||
if (!stats.is_object())
|
||||
stats = nlohmann::json::object();
|
||||
const std::vector<std::string> favs = favourite_ids();
|
||||
nlohmann::json stats;
|
||||
std::vector<std::string> favs;
|
||||
load_persisted(stats, favs);
|
||||
|
||||
// app_config stores recents oldest-first; the palette shows newest-first.
|
||||
std::vector<std::string> recents = wxGetApp().app_config->get_recent_projects();
|
||||
@@ -680,24 +658,15 @@ void ActionRegistry::materialize_recent_project_actions()
|
||||
if (title.empty())
|
||||
title = path;
|
||||
|
||||
auto action = std::make_unique<RecentProjectAction>(path, std::move(title), path);
|
||||
action->favourite = std::find(favs.begin(), favs.end(), id) != favs.end();
|
||||
if (auto it = stats.find(id); it != stats.end() && it->is_object()) {
|
||||
action->count = it->value("count", 0);
|
||||
action->last = it->value("last", 0LL);
|
||||
}
|
||||
auto action = std::make_unique<RecentProjectAction>(path, std::move(title), path);
|
||||
seed_from(stats, favs, id, *action);
|
||||
auto const action_id = action->id();
|
||||
auto const app_action = std::shared_ptr<AppAction>(std::move(action));
|
||||
m_actions.insert_or_assign(action_id, app_action);
|
||||
}
|
||||
|
||||
// Drop recent-project actions whose file no longer exists / was removed from the recents list.
|
||||
for (auto it = m_actions.begin(); it != m_actions.end();) {
|
||||
if (it->first.rfind(kRecentProjectPrefix, 0) == 0 && !seen.count(it->first))
|
||||
it = m_actions.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
drop_stale(m_actions, kRecentProjectPrefix, seen);
|
||||
}
|
||||
|
||||
bool ActionRegistry::should_ask(const std::string& id) const
|
||||
@@ -753,7 +722,6 @@ nlohmann::json ActionRegistry::snapshot()
|
||||
{"group", a->group},
|
||||
{"input", a->input},
|
||||
{"icon", a->icon},
|
||||
{"shortcut", ""},
|
||||
{"mode", mode_key(a->required_mode)}});
|
||||
};
|
||||
|
||||
@@ -816,7 +784,7 @@ nlohmann::json ActionRegistry::tab_options() const
|
||||
if (id.empty())
|
||||
continue;
|
||||
out.push_back({{"id", id.ToStdString()},
|
||||
{"title", notebook->GetPageText(i).ToStdString()},
|
||||
{"title", notebook->GetPageLabel(i).ToStdString()},
|
||||
{"icon", notebook->GetPageIcon(i)}});
|
||||
}
|
||||
return out;
|
||||
|
||||
@@ -114,6 +114,12 @@ private:
|
||||
std::string m_source_name; // display name of the action's source
|
||||
};
|
||||
|
||||
// Stable identity/display name of the built-in ("OrcaSlicer") action source. Shared by the native
|
||||
// command catalog and the dynamically materialised setting/plate/recent actions so every built-in
|
||||
// action re-keys together.
|
||||
inline constexpr const char* kOrcaSourceKey = "orca";
|
||||
inline constexpr const char* kOrcaSourceName = "OrcaSlicer";
|
||||
|
||||
// True when a setting at `setting_mode` cannot be edited in `current_mode` and the UI must switch
|
||||
// first. Developer settings are handled as a separate prompt by the Speed Dial.
|
||||
inline bool requires_mode_switch(ConfigOptionMode setting_mode, ConfigOptionMode current_mode)
|
||||
@@ -128,15 +134,17 @@ std::vector<std::string> cap_favourites(const std::vector<std::string>& ids, siz
|
||||
// Self-contained sink and single owner of runnable actions for the app session.
|
||||
//
|
||||
// Workflow:
|
||||
// 1. init() (once, UI thread) subscribes to the plugin loader and enumerates the
|
||||
// current script capabilities into actions.
|
||||
// 1. init() (once, UI thread) subscribes to the plugin loader and enumerates the current script
|
||||
// capabilities into actions, then materialises the static built-ins from the NativeCommands catalog.
|
||||
// 2. Loader load/unload callbacks route through refresh_source()/refresh_capability(),
|
||||
// which upsert()/remove() actions. The registry keeps the only action list and
|
||||
// restores persisted user state as actions arrive.
|
||||
// 3. Consumers use by_id(), snapshot(), and run() without knowing the source.
|
||||
// 3. Dynamic built-in families (settings, plates, recent projects) are re-materialised at the top of
|
||||
// snapshot(), because their membership follows live state (the current configs, plate list, recents).
|
||||
// 4. Consumers use by_id(), snapshot(), and run() without knowing the source.
|
||||
//
|
||||
// note: there is exactly one source (script plugins), so it lives inline here rather
|
||||
// than behind a polymorphic source interface.
|
||||
// note: the static catalog lives in NativeCommands; the registry owns the pool, persistence and
|
||||
// dispatch, and materialises the dynamic families inline rather than behind a source interface.
|
||||
class ActionRegistry
|
||||
{
|
||||
public:
|
||||
@@ -180,16 +188,20 @@ public:
|
||||
nlohmann::json snapshot();
|
||||
|
||||
// "Go to tab..." Speed Dial helper: enumerate the MainFrame notebook's current pages
|
||||
// as [{id,title},...]. Live by construction - built-in tabs (Home/Prepare/Preview/Device/
|
||||
// Project/Calibration) and plugin tabs (plugin.<key>.<name>) are all Notebook pages, so a
|
||||
// page appears/disappears with the notebook. Plugin tabs hidden in the overflow menu (many
|
||||
// plugins) aren't separate pages and are not listed. Call on the UI thread; null-safe.
|
||||
// as [{id,title,icon},...], using the page's real label (not the compact-blanked button text).
|
||||
// Live by construction - built-in tabs (Home/Prepare/Preview/Device/Project/Calibration) and
|
||||
// plugin tabs (plugin.<key>.<name>) are all Notebook pages, so a page appears/disappears with
|
||||
// the notebook. Plugin tabs hidden in the overflow menu (many plugins) aren't separate pages and
|
||||
// are not listed. Call on the UI thread; null-safe.
|
||||
nlohmann::json tab_options() const;
|
||||
|
||||
private:
|
||||
void seed_state(AppAction& a) const; // favourite/stats from config
|
||||
AppAction* find(const std::string& id);
|
||||
|
||||
// Read the persisted stats blob + capped favourite list once for a materialisation pass.
|
||||
void load_persisted(nlohmann::json& stats, std::vector<std::string>& favs) const;
|
||||
|
||||
// (Re)materialise the current visible config settings as SettingActions from the live
|
||||
// searcher (respecting printer-tech + user-mode + visibility filtering), removing stale ones.
|
||||
// Called at the top of snapshot() so the palette always reflects the current configs.
|
||||
|
||||
@@ -2616,6 +2616,10 @@ void GUI_App::init_app_config()
|
||||
}
|
||||
#endif // _WIN32
|
||||
}
|
||||
// Speed Dial opens on a bare Space from any page by default. Seed the flag so Preferences and the
|
||||
// MainFrame shortcut read the same value; an existing config (true or false) is left untouched.
|
||||
if (app_config->get("enable_speed_dial").empty())
|
||||
app_config->set_bool("enable_speed_dial", true);
|
||||
set_logging_level(Slic3r::level_string_to_boost(app_config->get("log_severity_level")));
|
||||
|
||||
}
|
||||
@@ -8163,6 +8167,22 @@ void GUI_App::save_mode(const /*ConfigOptionMode*/int mode)
|
||||
update_mode();
|
||||
}
|
||||
|
||||
void GUI_App::set_mode(ConfigOptionMode mode)
|
||||
{
|
||||
const bool was_developer = app_config->get_bool("developer_mode");
|
||||
if (was_developer)
|
||||
app_config->set_bool("developer_mode", false);
|
||||
save_mode(mode);
|
||||
if (was_developer)
|
||||
app_config->save();
|
||||
}
|
||||
|
||||
void GUI_App::enable_developer_mode()
|
||||
{
|
||||
app_config->set_bool("developer_mode", true);
|
||||
update_mode();
|
||||
}
|
||||
|
||||
// Update view mode according to selected menu
|
||||
void GUI_App::update_mode()
|
||||
{
|
||||
@@ -8316,17 +8336,32 @@ void GUI_App::refresh_plugins()
|
||||
// The metadata refresh blocks on disc discovery and a cloud round-trip, so run it on a worker
|
||||
// and report completion through the notification manager -- the speed dial needs no dialog.
|
||||
std::thread([]() {
|
||||
refresh_plugin_metadata_blocking(/*fetch_cloud=*/true);
|
||||
wxTheApp->CallAfter([]() {
|
||||
wxString error;
|
||||
try {
|
||||
refresh_plugin_metadata_blocking(/*fetch_cloud=*/true);
|
||||
} catch (const std::exception& ex) {
|
||||
error = from_u8(ex.what());
|
||||
} catch (...) {
|
||||
error = "Unknown error"; // plain literal: wx translation isn't safe off the UI thread
|
||||
}
|
||||
if (!wxTheApp)
|
||||
return;
|
||||
wxTheApp->CallAfter([error]() {
|
||||
if (wxGetApp().is_closing())
|
||||
return;
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (plater == nullptr)
|
||||
return;
|
||||
plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
into_u8(_L("Plugins refreshed.")));
|
||||
if (error.IsEmpty())
|
||||
plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
into_u8(_L("Plugins refreshed.")));
|
||||
else
|
||||
plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::ErrorNotificationLevel,
|
||||
into_u8(wxString::Format(_L("Failed to refresh plugins: %s"), error)));
|
||||
});
|
||||
}).detach();
|
||||
}
|
||||
|
||||
@@ -593,6 +593,11 @@ public:
|
||||
std::string get_saved_mode_str();
|
||||
std::string get_mode_str();
|
||||
void save_mode(const /*ConfigOptionMode*/int mode) ;
|
||||
// Switch to `mode` from the Speed Dial: a developer-mode override hides the saved mode
|
||||
// (get_mode returns comDevelop), so clear it first and persist the choice.
|
||||
void set_mode(ConfigOptionMode mode);
|
||||
// Turn the developer-mode override on and refresh the UI (used before jumping to a Developer setting).
|
||||
void enable_developer_mode();
|
||||
void update_mode();
|
||||
void update_internal_development();
|
||||
void show_ip_address_enter_dialog(wxString title = wxEmptyString);
|
||||
|
||||
@@ -199,8 +199,8 @@ void KBShortcutsDialog::fill_shortcuts()
|
||||
// Switch table page
|
||||
{ ctrl + L("Tab"), L("Switch table page")},
|
||||
// Open speed dial
|
||||
{ "Space", L("Open speed dial") },
|
||||
{ alt + "1..9,0", L("Run a Speed Dial favourite") },
|
||||
{ L_CONTEXT("Space", "Keyboard Shortcut"), L("Open speed dial") },
|
||||
{ alt + "1..9,0", L("Run a Speed Dial favourite (while the Speed Dial is open)") },
|
||||
//DEL
|
||||
#ifdef __APPLE__
|
||||
{"fn+⌫", L("Delete Selected")},
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "Preferences.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/ProgressDialog.hpp"
|
||||
#include "Widgets/StaticBox.hpp"
|
||||
#include "BindDialog.hpp"
|
||||
#include "../Utils/MacDarkMode.hpp"
|
||||
#include "../Utils/NetworkAgentFactory.hpp"
|
||||
@@ -109,17 +110,21 @@ namespace {
|
||||
|
||||
// Space opens the speed dial, but it is the activation key for buttons, checkboxes and other
|
||||
// controls. CHAR_HOOK runs before the focused child, so only take Space when the focused window has
|
||||
// no keyboard-activation meaning of its own. Canvases (GLCanvas3D) and panels are not wxControls and
|
||||
// fall through to "open"; Notebook and wxWebView are wxControls that don't use Space, so allow them.
|
||||
// no keyboard-activation meaning of its own. Canvases (GLCanvas3D) and panels are not controls and
|
||||
// fall through to "open"; the Notebook itself does too, so Space still opens the dial on any page.
|
||||
bool focus_keeps_space(wxWindow* focus)
|
||||
{
|
||||
if (!focus)
|
||||
return false;
|
||||
if (dynamic_cast<wxTextEntryBase*>(focus))
|
||||
return true; // typing a space into a text field
|
||||
if (dynamic_cast<wxWebView*>(focus))
|
||||
return true; // web content scrolls and hosts its own text fields
|
||||
if (dynamic_cast<::Button*>(focus))
|
||||
return true; // custom button: Space clicks it (it is a wxWindow, not a wxControl)
|
||||
if (dynamic_cast<wxControl*>(focus) && !dynamic_cast<Notebook*>(focus) && !dynamic_cast<wxWebView*>(focus))
|
||||
if (dynamic_cast<StaticBox*>(focus))
|
||||
return true; // custom composites (ComboBox, SpinInput, ...) activate with Space and are wxWindow
|
||||
if (dynamic_cast<wxControl*>(focus) && !dynamic_cast<Notebook*>(focus))
|
||||
return true; // stock button/checkbox/choice/list/etc. keep Space
|
||||
return false;
|
||||
}
|
||||
@@ -724,8 +729,10 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
|
||||
// Orca: open the speed dial from any page with a bare Space. Only when no modifier is held (so
|
||||
// editing shortcuts like Ctrl+Shift+Space in the canvas still reach it) and the focused window
|
||||
// doesn't use Space to activate itself (buttons, checkboxes, list/choice controls, text fields),
|
||||
// so a bare Space there still clicks/toggles instead of being hijacked.
|
||||
if (!evt.CmdDown() && !evt.ShiftDown() && !evt.AltDown() && evt.GetKeyCode() == WXK_SPACE) {
|
||||
// so a bare Space there still clicks/toggles instead of being hijacked. Gated by a preference
|
||||
// (default on) so users can hand Space back to the focused control entirely.
|
||||
if (wxGetApp().app_config->get_bool("enable_speed_dial") && !evt.CmdDown() && !evt.ShiftDown() &&
|
||||
!evt.AltDown() && evt.GetKeyCode() == WXK_SPACE) {
|
||||
if (focus_keeps_space(wxWindow::FindFocus())) {
|
||||
evt.Skip(); // let the focused control keep Space
|
||||
return;
|
||||
@@ -4325,7 +4332,7 @@ void MainFrame::technology_changed()
|
||||
// update menu titles
|
||||
PrinterTechnology pt = plater()->printer_technology();
|
||||
if (int id = m_menubar->FindMenu(pt == ptFFF ? _omitL("Material Settings") : _L("Filament settings")); id != wxNOT_FOUND)
|
||||
m_menubar->SetMenuLabel(id, pt == ptFFF ? _omitL("Material Settings") : _L("Filament settings"));
|
||||
m_menubar->SetMenuLabel(id, pt == ptSLA ? _omitL("Material Settings") : _L("Filament settings"));
|
||||
}
|
||||
|
||||
// Opens the calibration wizard for `calib_kind`. Single source of truth for the wizard lifecycle:
|
||||
|
||||
+112
-179
@@ -25,7 +25,7 @@
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
@@ -120,120 +120,43 @@ AppActionRunResult calib_command(CalibKind kind)
|
||||
return {AppActionRunResult::Level::Success};
|
||||
}
|
||||
|
||||
// Palette-only: developer mode overrides the saved mode (get_mode returns comDevelop), so choosing
|
||||
// Simple/Advanced/Expert must clear it first. Mirrors Preferences: persist the flag, then update.
|
||||
void select_mode(ConfigOptionMode mode)
|
||||
{
|
||||
GUI_App& app = wxGetApp();
|
||||
const bool was_developer = app.app_config->get_bool("developer_mode");
|
||||
if (was_developer)
|
||||
app.app_config->set_bool("developer_mode", false);
|
||||
app.save_mode(mode);
|
||||
if (was_developer)
|
||||
app.app_config->save();
|
||||
}
|
||||
constexpr const char* kCommandPrefix = "orca_command";
|
||||
|
||||
// Tile pictogram per command: the SVG base name of the icon the matching GUI control already uses
|
||||
// (menu/toolbar/sidebar). Absent key => blank tile. Keeping this as one table makes the curation
|
||||
// reviewable and lets a test check every value resolves to a real file.
|
||||
const std::map<std::string, std::string>& command_icons()
|
||||
// Thin AppAction wrapper for one catalog entry: identity and presentation come from the catalog,
|
||||
// run() routes back to it. The id is keyed by the stable catalog key (not the display title), so a
|
||||
// rename or a UI-language switch never re-keys the action.
|
||||
struct CommandAction : AppAction
|
||||
{
|
||||
static const std::map<std::string, std::string> icons = {
|
||||
// Slice & Export
|
||||
{"slice_and_preview", "media_play"},
|
||||
{"export_gcode", "menu_export_gcode"},
|
||||
{"export_stl", "menu_export_stl"},
|
||||
{"export_stl_multi", "menu_export_stl"},
|
||||
{"export_sliced_file", "menu_export_sliced_file"},
|
||||
{"export_all_sliced_file", "menu_export_sliced_file"},
|
||||
{"export_toolpaths_obj", "menu_export_toolpaths"},
|
||||
{"export_config", "menu_export_config"},
|
||||
{"export_3mf", "menu_save"},
|
||||
{"export_drc_single", "menu_export_stl"},
|
||||
{"export_drc_multi", "menu_export_stl"},
|
||||
// Commands
|
||||
{"load_project", "menu_open"},
|
||||
{"save_project", "menu_save"},
|
||||
{"save_project_as", "menu_save"},
|
||||
{"open_preferences", "cog"},
|
||||
{"go_to_layer", "height_range_layer"},
|
||||
// Mode: the sidebar mode toggle's own icon (ParamsPanel).
|
||||
{"mode_simple", "advanced"},
|
||||
{"mode_advanced", "advanced"},
|
||||
{"mode_expert", "advanced"},
|
||||
{"toggle_developer_mode", "advanced"},
|
||||
// Calibration
|
||||
{"calib_temperature", "calib_sf"},
|
||||
{"calib_max_volumetric", "calib_sf"},
|
||||
{"calib_pressure_advance", "calib_sf"},
|
||||
{"calib_flow_ratio", "calib_sf"},
|
||||
{"calib_retraction", "calib_sf"},
|
||||
{"calib_cornering", "calib_sf"},
|
||||
{"calib_input_shaping_freq", "calib_sf"},
|
||||
{"calib_input_shaping_damp", "calib_sf"},
|
||||
{"calib_vfa", "calib_sf"},
|
||||
// View
|
||||
{"reset_window_layout", "toolbar_reset"},
|
||||
// Object
|
||||
{"obj_delete", "menu_delete"},
|
||||
{"obj_delete_all", "menu_remove"},
|
||||
{"obj_mirror_x", "menu_mirror_x"},
|
||||
{"obj_mirror_y", "menu_mirror_y"},
|
||||
{"obj_mirror_z", "menu_mirror_z"},
|
||||
{"obj_split_objects", "menu_split_objects"},
|
||||
{"obj_split_parts", "menu_split_parts"},
|
||||
{"obj_drop", "toolbar_flatten"},
|
||||
{"obj_instances_up", "instance_add"},
|
||||
{"obj_instances_down", "instance_remove"},
|
||||
{"obj_arrange", "toolbar_arrange"},
|
||||
{"obj_orient", "toolbar_orient"},
|
||||
// Add Primitive
|
||||
{"add_primitive_cube", "menu_obj_cube"},
|
||||
{"add_primitive_cylinder", "menu_obj_cylinder"},
|
||||
{"add_primitive_sphere", "menu_obj_sphere"},
|
||||
{"add_primitive_cone", "menu_obj_cone"},
|
||||
{"add_primitive_disc", "menu_obj_disc"},
|
||||
{"add_primitive_torus", "menu_obj_torus"},
|
||||
{"add_primitive_text", "menu_obj_text"},
|
||||
{"add_primitive_svg", "menu_obj_svg"},
|
||||
// Plate
|
||||
{"plate_add", "toolbar_add_plate"},
|
||||
{"plate_duplicate", "menu_copy"},
|
||||
{"plate_delete", "menu_delete"},
|
||||
{"plate_rename", "plate_name_edit"},
|
||||
{"plate_toggle_lock", "lock_normal"},
|
||||
{"plate_goto", "go_next_plate"},
|
||||
// Printer / Presets
|
||||
{"sync_ams", "ams_fila_sync"},
|
||||
{"sync_presets", "printer_sync_ok"},
|
||||
{"preset_bundle", "menu_edit_preset"},
|
||||
// Import
|
||||
{"import_file", "menu_import"},
|
||||
{"import_zip_archive", "menu_import"},
|
||||
{"import_configs", "menu_import"},
|
||||
// Help
|
||||
{"help_open_config_folder", "folder-closed"},
|
||||
{"help_tip_of_the_day", "info"},
|
||||
{"help_check_updates", "ams_refresh_normal"},
|
||||
{"help_about", "OrcaSlicer_about"},
|
||||
{"open_wiki", "link_wiki_img"},
|
||||
};
|
||||
return icons;
|
||||
}
|
||||
std::string command_key;
|
||||
|
||||
AppActionRunResult run(const std::string& param) const override { return NativeCommands::run(command_key, param); }
|
||||
|
||||
explicit CommandAction(const NativeCommand& c)
|
||||
: AppAction(AppActionId{AppAction::compose_id(kCommandPrefix, c.key, kOrcaSourceKey)}, c.title, kOrcaSourceKey, kOrcaSourceName)
|
||||
, command_key(c.key)
|
||||
{
|
||||
this->kind = AppActionKind::Command;
|
||||
this->group = c.group;
|
||||
this->input = c.input;
|
||||
this->icon = c.icon;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<NativeCommand> build_command_catalog()
|
||||
{
|
||||
std::vector<NativeCommand> out;
|
||||
auto add = [&](std::string key, std::string title, std::string group, std::function<AppActionRunResult(const std::string&)> runner,
|
||||
std::string input = {}) {
|
||||
std::string icon;
|
||||
if (auto it = command_icons().find(key); it != command_icons().end())
|
||||
icon = it->second;
|
||||
std::string input = {}, std::string icon = {}) {
|
||||
out.push_back({std::move(key), std::move(title), std::move(group), std::move(input), std::move(icon), std::move(runner)});
|
||||
};
|
||||
// Presentation-first overload: keeps the tile icon next to the title/group it belongs to.
|
||||
auto add_with_icon = [&](std::string key, std::string title, std::string group, std::string icon,
|
||||
std::function<AppActionRunResult(const std::string&)> runner, std::string input = {}) {
|
||||
add(std::move(key), std::move(title), std::move(group), std::move(runner), std::move(input), std::move(icon));
|
||||
};
|
||||
|
||||
// ---- Slice & Export ----
|
||||
add("slice_and_preview", _u8L("Slice and Preview"), _u8L("Slice & Export"), [](const std::string&) {
|
||||
add_with_icon("slice_and_preview", _u8L("Slice and Preview"), _u8L("Slice & Export"), "media_play", [](const std::string&) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (plater) {
|
||||
plater->reslice();
|
||||
@@ -244,8 +167,8 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
|
||||
add(
|
||||
"go_to_layer", _u8L("Go to layer (percent)"), _u8L("Commands"),
|
||||
add_with_icon(
|
||||
"go_to_layer", _u8L("Go to layer (percent)"), _u8L("Commands"), "height_range_layer",
|
||||
[](const std::string& param) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (plater) {
|
||||
@@ -258,47 +181,52 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
},
|
||||
"percent");
|
||||
|
||||
// "go_to_tab" is two-phase: the palette collects the tab after activating it, so dispatch here
|
||||
// is a no-op (the jump goes through the go_to_tab web command).
|
||||
// "go_to_tab" is two-phase: the palette collects the tab after activating it, then hands the tab
|
||||
// id back as `param` (same contract as go_to_layer's percent).
|
||||
add(
|
||||
"go_to_tab", _u8L("Go to tab..."), _u8L("Commands"),
|
||||
[](const std::string&) { return AppActionRunResult{AppActionRunResult::Level::Success}; }, "tab");
|
||||
[](const std::string& param) {
|
||||
if (MainFrame* mf = wxGetApp().mainframe; mf && !param.empty())
|
||||
mf->select_tab(from_u8(param));
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
},
|
||||
"tab");
|
||||
|
||||
add("load_project", _u8L("Load Project"), _u8L("Commands"), [](const std::string&) {
|
||||
add_with_icon("load_project", _u8L("Load Project"), _u8L("Commands"), "menu_open", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->load_project();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("save_project", _u8L("Save Project"), _u8L("Commands"), [](const std::string&) {
|
||||
add_with_icon("save_project", _u8L("Save Project"), _u8L("Commands"), "menu_save", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->save_project(false);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("save_project_as", _u8L("Save Project As"), _u8L("Commands"), [](const std::string&) {
|
||||
add_with_icon("save_project_as", _u8L("Save Project As"), _u8L("Commands"), "menu_save", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->save_project(true);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("open_preferences", _u8L("Preferences"), _u8L("Commands"), [](const std::string&) {
|
||||
add_with_icon("open_preferences", _u8L("Preferences"), _u8L("Commands"), "cog", [](const std::string&) {
|
||||
wxGetApp().open_preferences();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
|
||||
// ---- Mode ----
|
||||
add("mode_simple", _u8L("Mode: Simple"), _u8L("Mode"), [](const std::string&) {
|
||||
select_mode(comSimple);
|
||||
add_with_icon("mode_simple", _u8L("Mode: Simple"), _u8L("Mode"), "advanced", [](const std::string&) {
|
||||
wxGetApp().set_mode(comSimple);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("mode_advanced", _u8L("Mode: Advanced"), _u8L("Mode"), [](const std::string&) {
|
||||
select_mode(comAdvanced);
|
||||
add_with_icon("mode_advanced", _u8L("Mode: Advanced"), _u8L("Mode"), "advanced", [](const std::string&) {
|
||||
wxGetApp().set_mode(comAdvanced);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("mode_expert", _u8L("Mode: Expert"), _u8L("Mode"), [](const std::string&) {
|
||||
select_mode(comExpert);
|
||||
add_with_icon("mode_expert", _u8L("Mode: Expert"), _u8L("Mode"), "advanced", [](const std::string&) {
|
||||
wxGetApp().set_mode(comExpert);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
// Mirrors Preferences > Developer > Developer mode: flip the flag, persist, refresh the UI.
|
||||
add("toggle_developer_mode", _u8L("Toggle Developer Mode"), _u8L("Mode"), [](const std::string&) {
|
||||
add_with_icon("toggle_developer_mode", _u8L("Toggle Developer Mode"), _u8L("Mode"), "advanced", [](const std::string&) {
|
||||
GUI_App& app = wxGetApp();
|
||||
const bool on = !app.app_config->get_bool("developer_mode");
|
||||
app.app_config->set_bool("developer_mode", on);
|
||||
@@ -308,50 +236,50 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
});
|
||||
|
||||
// ---- Export pipeline ----
|
||||
add("export_gcode", _u8L("Export G-code"), _u8L("Slice & Export"), [](const std::string&) {
|
||||
add_with_icon("export_gcode", _u8L("Export G-code"), _u8L("Slice & Export"), "menu_export_gcode", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->export_gcode(false);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("export_stl", _u8L("Export STL"), _u8L("Slice & Export"), [](const std::string&) {
|
||||
add_with_icon("export_stl", _u8L("Export STL"), _u8L("Slice & Export"), "menu_export_stl", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->export_stl();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("export_3mf", _u8L("Export 3MF"), _u8L("Slice & Export"), [](const std::string&) {
|
||||
add_with_icon("export_3mf", _u8L("Export 3MF"), _u8L("Slice & Export"), "menu_save", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->export_core_3mf();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("export_sliced_file", _u8L("Export Sliced File"), _u8L("Slice & Export"), [](const std::string&) {
|
||||
add_with_icon("export_sliced_file", _u8L("Export Sliced File"), _u8L("Slice & Export"), "menu_export_sliced_file", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->export_gcode_3mf(false);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("export_all_sliced_file", _u8L("Export All Sliced Files"), _u8L("Slice & Export"), [](const std::string&) {
|
||||
add_with_icon("export_all_sliced_file", _u8L("Export All Sliced Files"), _u8L("Slice & Export"), "menu_export_sliced_file", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->export_gcode_3mf(true);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
|
||||
// ---- Calibration ----
|
||||
add("calib_temperature", _u8L("Temperature Calibration"), _u8L("Calibration"),
|
||||
add_with_icon("calib_temperature", _u8L("Temperature Calibration"), _u8L("Calibration"), "calib_sf",
|
||||
[](const std::string&) { return calib_command(CalibKind::Temperature); });
|
||||
add("calib_max_volumetric", _u8L("Max Volumetric Speed Calibration"), _u8L("Calibration"),
|
||||
add_with_icon("calib_max_volumetric", _u8L("Max Volumetric Speed Calibration"), _u8L("Calibration"), "calib_sf",
|
||||
[](const std::string&) { return calib_command(CalibKind::MaxVolumetric); });
|
||||
add("calib_pressure_advance", _u8L("Pressure Advance Calibration"), _u8L("Calibration"),
|
||||
add_with_icon("calib_pressure_advance", _u8L("Pressure Advance Calibration"), _u8L("Calibration"), "calib_sf",
|
||||
[](const std::string&) { return calib_command(CalibKind::PressureAdvance); });
|
||||
add("calib_flow_ratio", _u8L("Flow Ratio Calibration"), _u8L("Calibration"),
|
||||
add_with_icon("calib_flow_ratio", _u8L("Flow Ratio Calibration"), _u8L("Calibration"), "calib_sf",
|
||||
[](const std::string&) { return calib_command(CalibKind::FlowRatio); });
|
||||
add("calib_retraction", _u8L("Retraction Calibration"), _u8L("Calibration"),
|
||||
add_with_icon("calib_retraction", _u8L("Retraction Calibration"), _u8L("Calibration"), "calib_sf",
|
||||
[](const std::string&) { return calib_command(CalibKind::Retraction); });
|
||||
add("calib_cornering", _u8L("Cornering Calibration"), _u8L("Calibration"),
|
||||
add_with_icon("calib_cornering", _u8L("Cornering Calibration"), _u8L("Calibration"), "calib_sf",
|
||||
[](const std::string&) { return calib_command(CalibKind::Cornering); });
|
||||
add("calib_input_shaping_freq", _u8L("Input Shaping Frequency Calibration"), _u8L("Calibration"),
|
||||
add_with_icon("calib_input_shaping_freq", _u8L("Input Shaping Frequency Calibration"), _u8L("Calibration"), "calib_sf",
|
||||
[](const std::string&) { return calib_command(CalibKind::InputShapingFreq); });
|
||||
add("calib_input_shaping_damp", _u8L("Input Shaping Damping Calibration"), _u8L("Calibration"),
|
||||
add_with_icon("calib_input_shaping_damp", _u8L("Input Shaping Damping Calibration"), _u8L("Calibration"), "calib_sf",
|
||||
[](const std::string&) { return calib_command(CalibKind::InputShapingDamp); });
|
||||
add("calib_vfa", _u8L("VFA Calibration"), _u8L("Calibration"), [](const std::string&) { return calib_command(CalibKind::VFA); });
|
||||
add_with_icon("calib_vfa", _u8L("VFA Calibration"), _u8L("Calibration"), "calib_sf", [](const std::string&) { return calib_command(CalibKind::VFA); });
|
||||
|
||||
// ---- View ----
|
||||
for (auto [key, dir, title] :
|
||||
@@ -386,39 +314,39 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
plater->get_camera().select_next_type();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("reset_window_layout", _u8L("Reset Window Layout"), _u8L("View"), [](const std::string&) {
|
||||
add_with_icon("reset_window_layout", _u8L("Reset Window Layout"), _u8L("View"), "toolbar_reset", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->reset_window_layout();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
|
||||
// ---- Object ----
|
||||
add("obj_delete", _u8L("Delete Selected"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_delete", _u8L("Delete Selected"), _u8L("Object"), "menu_delete", [](const std::string&) {
|
||||
return object_op(wxGetApp().plater(), [](Plater* p) { return !p->is_selection_empty(); }, [](Plater* p) { p->remove_selected(); });
|
||||
});
|
||||
add("obj_delete_all", _u8L("Delete All Objects"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_delete_all", _u8L("Delete All Objects"), _u8L("Object"), "menu_remove", [](const std::string&) {
|
||||
return object_op(
|
||||
wxGetApp().plater(), [](Plater* p) { return p->can_delete_all(); }, [](Plater* p) { p->delete_all_objects_from_model(); });
|
||||
});
|
||||
add("obj_mirror_x", _u8L("Mirror X"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_mirror_x", _u8L("Mirror X"), _u8L("Object"), "menu_mirror_x", [](const std::string&) {
|
||||
return object_op(wxGetApp().plater(), [](Plater* p) { return p->can_mirror(); }, [](Plater* p) { p->mirror(Axis::X); });
|
||||
});
|
||||
add("obj_mirror_y", _u8L("Mirror Y"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_mirror_y", _u8L("Mirror Y"), _u8L("Object"), "menu_mirror_y", [](const std::string&) {
|
||||
return object_op(wxGetApp().plater(), [](Plater* p) { return p->can_mirror(); }, [](Plater* p) { p->mirror(Axis::Y); });
|
||||
});
|
||||
add("obj_mirror_z", _u8L("Mirror Z"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_mirror_z", _u8L("Mirror Z"), _u8L("Object"), "menu_mirror_z", [](const std::string&) {
|
||||
return object_op(wxGetApp().plater(), [](Plater* p) { return p->can_mirror(); }, [](Plater* p) { p->mirror(Axis::Z); });
|
||||
});
|
||||
add("obj_split_objects", _u8L("Split to Objects"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_split_objects", _u8L("Split to Objects"), _u8L("Object"), "menu_split_objects", [](const std::string&) {
|
||||
return object_op(wxGetApp().plater(), [](Plater* p) { return p->can_split_to_objects(); }, [](Plater* p) { p->split_object(true); });
|
||||
});
|
||||
add("obj_split_parts", _u8L("Split to Parts"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_split_parts", _u8L("Split to Parts"), _u8L("Object"), "menu_split_parts", [](const std::string&) {
|
||||
return object_op(wxGetApp().plater(), [](Plater* p) { return p->can_split_to_volumes(); }, [](Plater* p) { p->split_volume(); });
|
||||
});
|
||||
add("obj_center", _u8L("Center Selected on Plate"), _u8L("Object"), [](const std::string&) {
|
||||
return object_op(wxGetApp().plater(), [](Plater* p) { return !p->is_selection_empty(); }, [](Plater* p) { p->center_selection(); });
|
||||
});
|
||||
add("obj_drop", _u8L("Drop to Bed"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_drop", _u8L("Drop to Bed"), _u8L("Object"), "toolbar_flatten", [](const std::string&) {
|
||||
return object_op(wxGetApp().plater(), [](Plater* p) { return !p->is_selection_empty(); }, [](Plater* p) { p->drop_selection(); });
|
||||
});
|
||||
add("obj_fit_volume", _u8L("Scale to Fit Print Volume"), _u8L("Object"), [](const std::string&) {
|
||||
@@ -426,24 +354,24 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
wxGetApp().plater(), [](Plater* p) { return p->can_scale_to_print_volume(); },
|
||||
[](Plater* p) { p->scale_selection_to_fit_print_volume(); });
|
||||
});
|
||||
add("obj_instances_up", _u8L("Increase Instances"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_instances_up", _u8L("Increase Instances"), _u8L("Object"), "instance_add", [](const std::string&) {
|
||||
return object_op(
|
||||
wxGetApp().plater(), [](Plater* p) { return p->can_increase_instances(); }, [](Plater* p) { p->increase_instances(); });
|
||||
});
|
||||
add("obj_instances_down", _u8L("Decrease Instances"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_instances_down", _u8L("Decrease Instances"), _u8L("Object"), "instance_remove", [](const std::string&) {
|
||||
return object_op(
|
||||
wxGetApp().plater(), [](Plater* p) { return p->can_decrease_instances(); }, [](Plater* p) { p->decrease_instances(); });
|
||||
});
|
||||
add("obj_arrange", _u8L("Auto-Arrange"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_arrange", _u8L("Auto-Arrange"), _u8L("Object"), "toolbar_arrange", [](const std::string&) {
|
||||
return object_op(wxGetApp().plater(), [](Plater* p) { return p->can_arrange(); }, [](Plater* p) { p->arrange(); });
|
||||
});
|
||||
add("obj_orient", _u8L("Auto-Orient"), _u8L("Object"), [](const std::string&) {
|
||||
add_with_icon("obj_orient", _u8L("Auto-Orient"), _u8L("Object"), "toolbar_orient", [](const std::string&) {
|
||||
return object_op(wxGetApp().plater(), [](Plater* p) { return p->can_arrange(); }, [](Plater* p) { p->orient(); });
|
||||
});
|
||||
|
||||
// ---- Add Primitive ---- (the Add > Add Primitive submenu; creates a new object)
|
||||
auto add_primitive = [&](std::string key, std::string title, const char* type_name) {
|
||||
add(std::move(key), std::move(title), _u8L("Add Primitive"), [type_name](const std::string&) {
|
||||
auto add_primitive = [&](std::string key, std::string title, std::string icon, const char* type_name) {
|
||||
add_with_icon(std::move(key), std::move(title), _u8L("Add Primitive"), std::move(icon), [type_name](const std::string&) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (plater) {
|
||||
ensure_3d_view(plater);
|
||||
@@ -453,13 +381,13 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
};
|
||||
add_primitive("add_primitive_cube", _u8L("Cube"), "Cube");
|
||||
add_primitive("add_primitive_cylinder", _u8L("Cylinder"), "Cylinder");
|
||||
add_primitive("add_primitive_sphere", _u8L("Sphere"), "Sphere");
|
||||
add_primitive("add_primitive_cone", _u8L("Cone"), "Cone");
|
||||
add_primitive("add_primitive_disc", _u8L("Disc"), "Disc");
|
||||
add_primitive("add_primitive_torus", _u8L("Torus"), "Torus");
|
||||
add("add_primitive_text", _u8L("Text"), _u8L("Add Primitive"), [](const std::string&) {
|
||||
add_primitive("add_primitive_cube", _u8L("Cube"), "menu_obj_cube", "Cube");
|
||||
add_primitive("add_primitive_cylinder", _u8L("Cylinder"), "menu_obj_cylinder", "Cylinder");
|
||||
add_primitive("add_primitive_sphere", _u8L("Sphere"), "menu_obj_sphere", "Sphere");
|
||||
add_primitive("add_primitive_cone", _u8L("Cone"), "menu_obj_cone", "Cone");
|
||||
add_primitive("add_primitive_disc", _u8L("Disc"), "menu_obj_disc", "Disc");
|
||||
add_primitive("add_primitive_torus", _u8L("Torus"), "menu_obj_torus", "Torus");
|
||||
add_with_icon("add_primitive_text", _u8L("Text"), _u8L("Add Primitive"), "menu_obj_text", [](const std::string&) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (plater) {
|
||||
ensure_3d_view(plater);
|
||||
@@ -469,7 +397,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
}
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("add_primitive_svg", _u8L("SVG"), _u8L("Add Primitive"), [](const std::string&) {
|
||||
add_with_icon("add_primitive_svg", _u8L("SVG"), _u8L("Add Primitive"), "menu_obj_svg", [](const std::string&) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (plater) {
|
||||
ensure_3d_view(plater);
|
||||
@@ -493,7 +421,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
}
|
||||
|
||||
// ---- Plate ----
|
||||
add("plate_add", _u8L("Add Plate"), _u8L("Plate"), [](const std::string&) {
|
||||
add_with_icon("plate_add", _u8L("Add Plate"), _u8L("Plate"), "toolbar_add_plate", [](const std::string&) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (!is_fff_plater(plater))
|
||||
return plate_unavailable();
|
||||
@@ -502,7 +430,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
plater->add_plate();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("plate_duplicate", _u8L("Duplicate Plate"), _u8L("Plate"), [](const std::string&) {
|
||||
add_with_icon("plate_duplicate", _u8L("Duplicate Plate"), _u8L("Plate"), "menu_copy", [](const std::string&) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (!is_fff_plater(plater))
|
||||
return plate_unavailable();
|
||||
@@ -511,7 +439,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
plater->duplicate_plate();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("plate_delete", _u8L("Delete Plate"), _u8L("Plate"), [](const std::string&) {
|
||||
add_with_icon("plate_delete", _u8L("Delete Plate"), _u8L("Plate"), "menu_delete", [](const std::string&) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (!is_fff_plater(plater))
|
||||
return plate_unavailable();
|
||||
@@ -520,7 +448,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
plater->delete_plate();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("plate_rename", _u8L("Rename Plate"), _u8L("Plate"), [](const std::string&) {
|
||||
add_with_icon("plate_rename", _u8L("Rename Plate"), _u8L("Plate"), "plate_name_edit", [](const std::string&) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (!is_fff_plater(plater))
|
||||
return plate_unavailable();
|
||||
@@ -531,7 +459,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
curr->set_plate_name(dlg.get_plate_name().ToUTF8().data());
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("plate_toggle_lock", _u8L("Toggle Plate Lock"), _u8L("Plate"), [](const std::string&) {
|
||||
add_with_icon("plate_toggle_lock", _u8L("Toggle Plate Lock"), _u8L("Plate"), "lock_normal", [](const std::string&) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (!is_fff_plater(plater))
|
||||
return plate_unavailable();
|
||||
@@ -541,7 +469,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
plates.lock_plate(index, !plates.is_locked(index));
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("plate_goto", _u8L("Go to Plate"), _u8L("Plate"), [](const std::string& param) {
|
||||
add_with_icon("plate_goto", _u8L("Go to Plate"), _u8L("Plate"), "go_next_plate", [](const std::string& param) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (!is_fff_plater(plater))
|
||||
return plate_unavailable();
|
||||
@@ -559,7 +487,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
});
|
||||
|
||||
// ---- Printer / device connection ----
|
||||
add("sync_ams", _u8L("Synchronize Filament List from AMS"), _u8L("Printer"), [](const std::string&) {
|
||||
add_with_icon("sync_ams", _u8L("Synchronize Filament List from AMS"), _u8L("Printer"), "ams_fila_sync", [](const std::string&) {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
DeviceManager* dev = wxGetApp().getDeviceManager();
|
||||
if (dev && dev->get_selected_machine() && plater) {
|
||||
@@ -570,11 +498,11 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
});
|
||||
|
||||
// ---- Presets / cloud ----
|
||||
add("preset_bundle", _u8L("Open Preset Bundle"), _u8L("Presets"), [](const std::string&) {
|
||||
add_with_icon("preset_bundle", _u8L("Open Preset Bundle"), _u8L("Presets"), "menu_edit_preset", [](const std::string&) {
|
||||
wxGetApp().open_presetbundledialog();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("sync_presets", _u8L("Sync Presets"), _u8L("Presets"), [](const std::string&) {
|
||||
add_with_icon("sync_presets", _u8L("Sync Presets"), _u8L("Presets"), "printer_sync_ok", [](const std::string&) {
|
||||
if (!wxGetApp().is_user_login())
|
||||
return AppActionRunResult{AppActionRunResult::Level::Info, _L("Sign in to sync presets.")};
|
||||
wxGetApp().restart_sync_user_preset();
|
||||
@@ -582,7 +510,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
});
|
||||
|
||||
// ---- Import ----
|
||||
add("import_file", _u8L("Import 3MF/STL/STEP/SVG/OBJ/AMF"), _u8L("Import"), [](const std::string&) {
|
||||
add_with_icon("import_file", _u8L("Import 3MF/STL/STEP/SVG/OBJ/AMF"), _u8L("Import"), "menu_import", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater()) {
|
||||
#ifdef __APPLE__
|
||||
plater->add_model();
|
||||
@@ -592,39 +520,39 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
}
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("import_zip_archive", _u8L("Import ZIP Archive"), _u8L("Import"), [](const std::string&) {
|
||||
add_with_icon("import_zip_archive", _u8L("Import ZIP Archive"), _u8L("Import"), "menu_import", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->import_zip_archive();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("import_configs", _u8L("Import Configs"), _u8L("Import"), [](const std::string&) {
|
||||
add_with_icon("import_configs", _u8L("Import Configs"), _u8L("Import"), "menu_import", [](const std::string&) {
|
||||
if (MainFrame* mf = wxGetApp().mainframe)
|
||||
mf->load_config_file();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
|
||||
// ---- Export extras ----
|
||||
add("export_stl_multi", _u8L("Export All Objects as STLs"), _u8L("Export"), [](const std::string&) {
|
||||
add_with_icon("export_stl_multi", _u8L("Export All Objects as STLs"), _u8L("Export"), "menu_export_stl", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->export_stl(false, false, true);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("export_drc_single", _u8L("Export All Objects as DRC (one file)"), _u8L("Export"), [](const std::string&) {
|
||||
add_with_icon("export_drc_single", _u8L("Export All Objects as DRC (one file)"), _u8L("Export"), "menu_export_stl", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->export_stl(false, false, false, FT_DRC);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("export_drc_multi", _u8L("Export All Objects as DRCs"), _u8L("Export"), [](const std::string&) {
|
||||
add_with_icon("export_drc_multi", _u8L("Export All Objects as DRCs"), _u8L("Export"), "menu_export_stl", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->export_stl(false, false, true, FT_DRC);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("export_toolpaths_obj", _u8L("Export Toolpaths as OBJ"), _u8L("Export"), [](const std::string&) {
|
||||
add_with_icon("export_toolpaths_obj", _u8L("Export Toolpaths as OBJ"), _u8L("Export"), "menu_export_toolpaths", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
plater->export_toolpaths_to_obj();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("export_config", _u8L("Export Preset Bundle"), _u8L("Export"), [](const std::string&) {
|
||||
add_with_icon("export_config", _u8L("Export Preset Bundle"), _u8L("Export"), "menu_export_config", [](const std::string&) {
|
||||
if (MainFrame* mf = wxGetApp().mainframe)
|
||||
mf->export_config();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
@@ -639,7 +567,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
wxGetApp().ShowUserGuide();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("help_open_config_folder", _u8L("Show Configuration Folder"), _u8L("Help"), [](const std::string&) {
|
||||
add_with_icon("help_open_config_folder", _u8L("Show Configuration Folder"), _u8L("Help"), "folder-closed", [](const std::string&) {
|
||||
Slic3r::GUI::desktop_open_datadir_folder();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
@@ -652,7 +580,7 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
dlg.ShowModal();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("help_tip_of_the_day", _u8L("Show Tip of the Day"), _u8L("Help"), [](const std::string&) {
|
||||
add_with_icon("help_tip_of_the_day", _u8L("Show Tip of the Day"), _u8L("Help"), "info", [](const std::string&) {
|
||||
if (Plater* plater = wxGetApp().plater()) {
|
||||
plater->get_dailytips()->open();
|
||||
if (GLCanvas3D* canvas = plater->get_current_canvas3D())
|
||||
@@ -660,15 +588,15 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
}
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("help_check_updates", _u8L("Check for Updates"), _u8L("Help"), [](const std::string&) {
|
||||
add_with_icon("help_check_updates", _u8L("Check for Updates"), _u8L("Help"), "ams_refresh_normal", [](const std::string&) {
|
||||
wxGetApp().check_new_version_sf(true, 1);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("help_about", _u8L("About OrcaSlicer"), _u8L("Help"), [](const std::string&) {
|
||||
add_with_icon("help_about", _u8L("About OrcaSlicer"), _u8L("Help"), "OrcaSlicer_about", [](const std::string&) {
|
||||
Slic3r::GUI::about();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("open_wiki", _u8L("Open Wiki"), _u8L("Help"), [](const std::string&) {
|
||||
add_with_icon("open_wiki", _u8L("Open Wiki"), _u8L("Help"), "link_wiki_img", [](const std::string&) {
|
||||
wxLaunchDefaultBrowser("https://www.orcaslicer.com/wiki/", wxBROWSER_NEW_WINDOW);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
@@ -706,6 +634,11 @@ const std::vector<NativeCommand>& NativeCommands::catalog()
|
||||
return commands;
|
||||
}
|
||||
|
||||
std::unique_ptr<AppAction> NativeCommands::make_action(const NativeCommand& command)
|
||||
{
|
||||
return std::make_unique<CommandAction>(command);
|
||||
}
|
||||
|
||||
AppActionRunResult NativeCommands::run(const std::string& key, const std::string& param)
|
||||
{
|
||||
GUI_App& app = wxGetApp();
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ActionRegistry.hpp" // for AppActionRunResult
|
||||
#include "ActionRegistry.hpp" // for AppAction / AppActionRunResult
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// A built-in speed-dial command: identity + how to run it. The registry keeps commands as thin
|
||||
// values (CommandAction) and routes run() here, so this catalog is the single source of truth for
|
||||
// the behaviour (runner => an owner method), the presentation (title/group/input), and the tile
|
||||
// pictogram (icon = an SVG base name under resources/images, "" for no icon).
|
||||
// A built-in speed-dial command: identity + how to run it. make_action() wraps a value as a thin
|
||||
// AppAction for the registry, so this catalog is the single source of truth for the behaviour
|
||||
// (runner => an owner method), the presentation (title/group/input), and the tile pictogram
|
||||
// (icon = an SVG base name under resources/images, "" for no icon).
|
||||
struct NativeCommand
|
||||
{
|
||||
std::string key;
|
||||
@@ -28,6 +29,11 @@ const std::vector<NativeCommand>& catalog();
|
||||
|
||||
// Dispatches `key` to its runner (unknown keys return a quiet Info). UI thread only.
|
||||
AppActionRunResult run(const std::string& key, const std::string& param = {});
|
||||
|
||||
// Materialises one catalog entry as a runnable AppAction. Keeps the catalog's identity,
|
||||
// presentation and behaviour as the single source of truth; ActionRegistry only stores and
|
||||
// dispatches the result. UI thread only.
|
||||
std::unique_ptr<AppAction> make_action(const NativeCommand& command);
|
||||
} // namespace NativeCommands
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
@@ -267,6 +267,12 @@ wxString ButtonsListCtrl::GetPageText(size_t n) const
|
||||
return btn->GetLabel();
|
||||
}
|
||||
|
||||
// ORCA
|
||||
wxString ButtonsListCtrl::GetPageLabel(size_t n) const
|
||||
{
|
||||
return n < m_pageLabels.size() ? m_pageLabels[n] : wxString();
|
||||
}
|
||||
|
||||
// ORCA
|
||||
void ButtonsListCtrl::SetOverflowButton(wxWindow* button)
|
||||
{
|
||||
|
||||
@@ -33,8 +33,14 @@ public:
|
||||
void SetPageText(size_t n, const wxString& strText);
|
||||
void SetCompact(size_t n, bool compact); // ORCA
|
||||
wxString GetPageText(size_t n) const;
|
||||
// ORCA: the full page label, unaffected by SetCompact() blanking the button text.
|
||||
wxString GetPageLabel(size_t n) const;
|
||||
// Resource name the page was inserted with (empty for plugin pages, which pass a wxBitmap).
|
||||
const std::string& GetPageIcon(size_t n) const { return m_pageIcons[n]; }
|
||||
const std::string& GetPageIcon(size_t n) const
|
||||
{
|
||||
static const std::string empty;
|
||||
return n < m_pageIcons.size() ? m_pageIcons[n] : empty;
|
||||
}
|
||||
wxFlexGridSizer* GetBtnsSizer(){return m_buttons_sizer;}; // ORCA
|
||||
// ORCA: a companion widget shown right after the tab buttons (before any side_tools), e.g.
|
||||
// an overflow indicator. Pass nullptr to remove it; ownership stays with the caller.
|
||||
@@ -244,6 +250,13 @@ public:
|
||||
return GetBtnsListCtrl()->GetPageText(n);
|
||||
}
|
||||
|
||||
// ORCA: the real page label. GetPageText() returns the button label, which SetCompact() blanks.
|
||||
wxString GetPageLabel(size_t n) const
|
||||
{
|
||||
wxCHECK_MSG(n < GetPageCount(), wxString(), wxS("Invalid page"));
|
||||
return GetBtnsListCtrl()->GetPageLabel(n);
|
||||
}
|
||||
|
||||
// Resource icon name the page was inserted with; empty for pages added with a wxBitmap.
|
||||
std::string GetPageIcon(size_t n) const
|
||||
{
|
||||
|
||||
@@ -543,15 +543,16 @@ bool install_local_plugin_package(const boost::filesystem::path& package_file, w
|
||||
});
|
||||
timer->Start(100);
|
||||
|
||||
bool finished = false;
|
||||
wxEventLoop loop;
|
||||
auto on_finish = [&finished, &loop]() {
|
||||
finished = true;
|
||||
if (loop.IsRunning())
|
||||
loop.Exit();
|
||||
// finished/loop live on the heap: the worker's completion callback is posted to the UI loop
|
||||
// and can still fire after this stack frame is gone, so it must not reference locals.
|
||||
struct WaitState
|
||||
{
|
||||
bool finished = false;
|
||||
wxEventLoop loop;
|
||||
};
|
||||
auto wait = std::make_shared<WaitState>();
|
||||
|
||||
std::thread([state, package_file, on_finish]() mutable {
|
||||
std::thread([state, package_file, wait]() mutable {
|
||||
std::string error;
|
||||
bool ok = false;
|
||||
try {
|
||||
@@ -570,11 +571,17 @@ bool install_local_plugin_package(const boost::filesystem::path& package_file, w
|
||||
state->ok = ok;
|
||||
state->error = std::move(error);
|
||||
}
|
||||
wxTheApp->CallAfter(on_finish);
|
||||
if (!wxTheApp)
|
||||
return;
|
||||
wxTheApp->CallAfter([wait]() {
|
||||
wait->finished = true;
|
||||
if (wait->loop.IsRunning())
|
||||
wait->loop.Exit();
|
||||
});
|
||||
}).detach();
|
||||
|
||||
if (!finished)
|
||||
loop.Run();
|
||||
if (!wait->finished)
|
||||
wait->loop.Run();
|
||||
|
||||
timer->Stop();
|
||||
delete timer;
|
||||
|
||||
@@ -1723,6 +1723,11 @@ void PreferencesDialog::create_items()
|
||||
auto item_multi_machine = create_item_checkbox(_L("Multi device management"), _L("With this option enabled, you can send a task to multiple devices at the same time and manage multiple devices."), "enable_multi_machine", _L("(Requires restart)"));
|
||||
g_sizer->Add(item_multi_machine);
|
||||
|
||||
auto item_speed_dial = create_item_checkbox(_L("Open the Speed Dial with the Space key"),
|
||||
_L("When enabled, pressing Space (with no other key held) opens the Speed Dial action search from any page."),
|
||||
"enable_speed_dial");
|
||||
g_sizer->Add(item_speed_dial);
|
||||
|
||||
#if 0
|
||||
g_sizer->Add(create_item_title(_L("Filament Grouping")), 1, wxEXPAND);
|
||||
//temporarily disable it
|
||||
|
||||
@@ -322,7 +322,9 @@ void OptionsSearcher::init(std::vector<InputInfo> input_values)
|
||||
|
||||
void OptionsSearcher::apply(DynamicPrintConfig *config, Preset::Type type, ConfigOptionMode mode)
|
||||
{
|
||||
if (options.empty()) return;
|
||||
// options_all_modes is a separate consumer (the Speed Dial), so "nothing initialised yet" means
|
||||
// both views are empty - the mode-filtered options can be empty while the all-modes view is not.
|
||||
if (options.empty() && options_all_modes.empty()) return;
|
||||
|
||||
options.erase(std::remove_if(options.begin(), options.end(), [type](Option opt) { return opt.type == type; }), options.end());
|
||||
options_all_modes.erase(std::remove_if(options_all_modes.begin(), options_all_modes.end(), [type](Option opt) { return opt.type == type; }),
|
||||
|
||||
@@ -156,10 +156,6 @@ public:
|
||||
void dlg_sys_color_changed();
|
||||
void dlg_msw_rescale();
|
||||
|
||||
// The full gated option set built by init() (after visibility/mode/printer-tech filtering).
|
||||
// Used by the Speed Dial to materialise config settings as first-class actions.
|
||||
const std::vector<Option>& all_options() const { return options; }
|
||||
|
||||
// Every option across all UI modes (Developer included), regardless of the current mode.
|
||||
// Used by the Speed Dial so it can list settings the user would have to switch mode to edit.
|
||||
const std::vector<Option>& all_modes_options() const { return options_all_modes; }
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#include "Plater.hpp"
|
||||
#include "Widgets/WebViewHostDialog.hpp"
|
||||
|
||||
#include <libslic3r/AppConfig.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <wx/display.h>
|
||||
@@ -156,15 +154,7 @@ void SpeedDialWebDialog::handle_web_command(const nlohmann::json& payload)
|
||||
run_action(payload.value("id", ""), payload.value("title", ""), payload.value("param", ""));
|
||||
else if (command == "search_tabs")
|
||||
search_tabs();
|
||||
else if (command == "go_to_tab") {
|
||||
// "Go to tab..." second phase: the page hands back the tab id it matched.
|
||||
const std::string tab_id = payload.value("id", "");
|
||||
if (!tab_id.empty()) {
|
||||
Hide();
|
||||
if (wxGetApp().mainframe)
|
||||
wxGetApp().mainframe->select_tab(from_u8(tab_id));
|
||||
}
|
||||
} else if (command == "resize")
|
||||
else if (command == "resize")
|
||||
resize_to_content(json_int_or(payload, "height", 0));
|
||||
}
|
||||
|
||||
@@ -218,8 +208,7 @@ void SpeedDialWebDialog::run_action(const std::string& id, const std::string& ti
|
||||
_L("Developer setting"), wxOK | wxCANCEL);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
wxGetApp().app_config->set_bool("developer_mode", true);
|
||||
wxGetApp().update_mode();
|
||||
wxGetApp().enable_developer_mode();
|
||||
} else {
|
||||
RichMessageDialog dlg(wxGetApp().mainframe,
|
||||
wxString::Format(_L("\"%s\" is a %s setting. Switch from %s mode to %s mode to edit it?"),
|
||||
|
||||
Reference in New Issue
Block a user