mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-22 00:12:34 +00:00
378 lines
21 KiB
C++
378 lines
21 KiB
C++
#include "Shortcuts.hpp"
|
|
|
|
#include "I18N.hpp"
|
|
#include "libslic3r/AppConfig.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <map>
|
|
#include <utility>
|
|
|
|
namespace Slic3r { namespace GUI {
|
|
|
|
namespace {
|
|
|
|
constexpr uint8_t GLOBAL = context_bit(ShortcutContext::Global);
|
|
constexpr uint8_t PLATER = context_bit(ShortcutContext::Plater);
|
|
constexpr uint8_t PREVIEW = context_bit(ShortcutContext::Preview);
|
|
constexpr uint8_t OBJECT_LIST = context_bit(ShortcutContext::ObjectList);
|
|
constexpr uint8_t PAINTING = context_bit(ShortcutContext::Painting);
|
|
constexpr uint8_t CANVAS = PLATER | PREVIEW;
|
|
|
|
constexpr int CTRL = wxMOD_CONTROL;
|
|
constexpr int SHIFT = wxMOD_SHIFT;
|
|
constexpr int CTRL_SHIFT = wxMOD_CONTROL | wxMOD_SHIFT;
|
|
|
|
#ifdef __APPLE__
|
|
constexpr KeyChord PREFERENCES_CHORD{ ',', CTRL };
|
|
constexpr KeyChord DELETE_CHORD{ WXK_BACK };
|
|
constexpr KeyChord MOUSE3D_CHORD{ 'M', CTRL_SHIFT };
|
|
#else
|
|
constexpr KeyChord PREFERENCES_CHORD{ 'P', CTRL };
|
|
constexpr KeyChord DELETE_CHORD{ WXK_DELETE };
|
|
constexpr KeyChord MOUSE3D_CHORD{ 'M', CTRL };
|
|
#endif
|
|
|
|
// Each row: enum value, AppConfig key, description shown in the dialog, contexts the key is
|
|
// looked up in, and the default chord. Rows are in the order the dialog lists them, within
|
|
// the sections of the Shortcut enum.
|
|
// SHORTCUT runs once per key press.
|
|
// REPEATING runs again on every auto-repeat of a held key.
|
|
// STEPPING repeats, and Shift or Ctrl held with its key select a step variant of it instead
|
|
// of another shortcut (ShortcutInfo::modifier_variants).
|
|
#define SHORTCUT(id, key, name, contexts, ...) ShortcutInfo{ Shortcut::id, key, name, contexts, __VA_ARGS__, false, false }
|
|
#define REPEATING(id, key, name, contexts, ...) ShortcutInfo{ Shortcut::id, key, name, contexts, __VA_ARGS__, true, false }
|
|
#define STEPPING(id, key, name, contexts, ...) ShortcutInfo{ Shortcut::id, key, name, contexts, __VA_ARGS__, true, true }
|
|
|
|
constexpr std::array<ShortcutInfo, size_t(Shortcut::Count)> shortcut_table = {{
|
|
// Project
|
|
SHORTCUT(NewProject, "new_project", L("New Project"), GLOBAL, { 'N', CTRL }),
|
|
SHORTCUT(OpenProject, "open_project", L("Open Project"), GLOBAL, { 'O', CTRL }),
|
|
SHORTCUT(SaveProject, "save_project", L("Save Project"), GLOBAL, { 'S', CTRL }),
|
|
SHORTCUT(SaveProjectAs, "save_project_as", L("Save Project as"), GLOBAL, { 'S', CTRL_SHIFT }),
|
|
SHORTCUT(ImportModel, "import_model", L("Import geometry data from STL/STEP/3MF/OBJ/AMF files"), GLOBAL, { 'I', CTRL }),
|
|
SHORTCUT(Publish3mf, "publish_3mf", L("Publish 3MF"), GLOBAL, { 'E', CTRL_SHIFT }),
|
|
|
|
// Slicing and printing
|
|
SHORTCUT(SlicePlate, "slice_plate", L("Slice plate"), GLOBAL, { 'R', CTRL }),
|
|
SHORTCUT(ExportSlicedFile, "export_sliced_file", L("Export plate sliced file"), GLOBAL, { 'G', CTRL }),
|
|
SHORTCUT(PrintPlate, "print_plate", L("Print plate"), GLOBAL, { 'G', CTRL_SHIFT }),
|
|
SHORTCUT(PrintHostQueue, "print_host_queue", L("Print host upload queue"), GLOBAL, { 'J', CTRL }),
|
|
|
|
// Selection
|
|
SHORTCUT(SelectAll, "select_all", L("Select all objects"), PLATER | OBJECT_LIST, { 'A', CTRL }),
|
|
SHORTCUT(SelectAllPlates, "select_all_plates", L("Select all objects on all plates"), PLATER, { 'A', CTRL_SHIFT }),
|
|
|
|
// Editing
|
|
REPEATING(Undo, "undo", L("Undo"), PLATER | OBJECT_LIST, { 'Z', CTRL }),
|
|
REPEATING(Redo, "redo", L("Redo"), PLATER | OBJECT_LIST, { 'Y', CTRL }),
|
|
SHORTCUT(Cut, "cut", L("Cut"), PLATER | OBJECT_LIST, { 'X', CTRL }),
|
|
SHORTCUT(Copy, "copy", L("Copy to clipboard"), PLATER | OBJECT_LIST, { 'C', CTRL }),
|
|
SHORTCUT(Paste, "paste", L("Paste from clipboard"), PLATER | OBJECT_LIST, { 'V', CTRL }),
|
|
SHORTCUT(DeleteSelected, "delete_selected", L("Delete Selected"), PLATER | OBJECT_LIST, DELETE_CHORD),
|
|
SHORTCUT(DeleteAll, "delete_all", L("Delete All"), PLATER, { 'D', CTRL }),
|
|
SHORTCUT(CloneSelected, "clone_selected", L("Clone Selected"), PLATER | OBJECT_LIST, { 'K', CTRL }),
|
|
|
|
// Objects
|
|
REPEATING(AddInstance, "add_instance", L("Add instance"), PLATER | OBJECT_LIST, { '+' }),
|
|
REPEATING(RemoveInstance, "remove_instance", L("Remove instance"), PLATER | OBJECT_LIST, { '-' }),
|
|
SHORTCUT(TogglePrintable, "toggle_printable", L("Toggle printable for object/part"), PLATER | OBJECT_LIST, { 'V' }),
|
|
SHORTCUT(ToggleAutoDrop, "toggle_auto_drop", L("Auto Drop"), OBJECT_LIST, { 'D' }),
|
|
|
|
// Placement
|
|
SHORTCUT(Arrange, "arrange", L("Arrange all objects"), PLATER, { 'A' }),
|
|
SHORTCUT(ArrangePlate, "arrange_plate", L("Arrange objects on selected plates"), PLATER, { 'A', SHIFT }),
|
|
SHORTCUT(Orient, "orient", L("Auto orient all/selected objects"), PLATER, { 'Q' }),
|
|
SHORTCUT(OrientPlate, "orient_plate", L("Auto orient all objects on current plate"), PLATER, { 'Q', SHIFT }),
|
|
STEPPING(MoveSelectionLeft, "move_selection_left", L("Move selection 10mm in negative X direction"), PLATER, { WXK_LEFT }),
|
|
STEPPING(MoveSelectionRight, "move_selection_right", L("Move selection 10mm in positive X direction"), PLATER, { WXK_RIGHT }),
|
|
STEPPING(MoveSelectionUp, "move_selection_up", L("Move selection 10mm in positive Y direction"), PLATER, { WXK_UP }),
|
|
STEPPING(MoveSelectionDown, "move_selection_down", L("Move selection 10mm in negative Y direction"), PLATER, { WXK_DOWN }),
|
|
REPEATING(RotateSelectionLeft, "rotate_selection_left", L("Rotate selection 45 degrees counterclockwise"), PLATER, { WXK_PAGEUP }),
|
|
REPEATING(RotateSelectionRight, "rotate_selection_right", L("Rotate selection 45 degrees clockwise"), PLATER, { WXK_PAGEDOWN }),
|
|
|
|
// Gizmos
|
|
SHORTCUT(GizmoMove, "gizmo_move", L("Gizmo move"), PLATER, { 'M' }),
|
|
SHORTCUT(GizmoRotate, "gizmo_rotate", L("Gizmo rotate"), PLATER, { 'R' }),
|
|
SHORTCUT(GizmoScale, "gizmo_scale", L("Gizmo scale"), PLATER, { 'S' }),
|
|
SHORTCUT(GizmoFlatten, "gizmo_flatten", L("Gizmo place face on bed"), PLATER, { 'F' }),
|
|
SHORTCUT(GizmoCut, "gizmo_cut", L("Gizmo cut"), PLATER, { 'C' }),
|
|
SHORTCUT(GizmoMeshBoolean, "gizmo_mesh_boolean", L("Gizmo mesh boolean"), PLATER, { 'B' }),
|
|
SHORTCUT(GizmoFdmSupports, "gizmo_fdm_supports", L("Gizmo FDM paint-on supports"), PLATER, { 'L' }),
|
|
SHORTCUT(GizmoSeam, "gizmo_seam", L("Gizmo FDM paint-on seam"), PLATER, { 'P' }),
|
|
SHORTCUT(GizmoFuzzySkin, "gizmo_fuzzy_skin", L("Gizmo FDM paint-on fuzzy skin"), PLATER, { 'H' }),
|
|
SHORTCUT(GizmoMmuSegmentation, "gizmo_mmu_segmentation", L("Gizmo multi-material painting"), PLATER, { 'N' }),
|
|
SHORTCUT(GizmoEmboss, "gizmo_emboss", L("Gizmo text emboss/engrave"), PLATER, { 'T' }),
|
|
SHORTCUT(GizmoMeasure, "gizmo_measure", L("Gizmo measure"), PLATER, { 'U' }),
|
|
SHORTCUT(GizmoAssembly, "gizmo_assembly", L("Gizmo assemble"), PLATER, { 'Y' }),
|
|
SHORTCUT(GizmoBrimEars, "gizmo_brim_ears", L("Gizmo brim ears"), PLATER, { 'E' }),
|
|
|
|
// Sliders
|
|
SHORTCUT(GoToLayer, "go_to_layer", L("Jump to layer"), PREVIEW, { 'G', SHIFT }),
|
|
STEPPING(LayerSliderUp, "layer_slider_up", L("Vertical slider - Move active thumb Up"), PREVIEW, { WXK_UP }),
|
|
STEPPING(LayerSliderDown, "layer_slider_down", L("Vertical slider - Move active thumb Down"), PREVIEW, { WXK_DOWN }),
|
|
STEPPING(MovesSliderLeft, "moves_slider_left", L("Horizontal slider - Move active thumb Left"), PREVIEW, { WXK_LEFT }),
|
|
STEPPING(MovesSliderRight, "moves_slider_right", L("Horizontal slider - Move active thumb Right"), PREVIEW, { WXK_RIGHT }),
|
|
REPEATING(MovesSliderStart, "moves_slider_start", L("Horizontal slider - Move to start position"), PREVIEW, { WXK_HOME }),
|
|
REPEATING(MovesSliderEnd, "moves_slider_end", L("Horizontal slider - Move to last position"), PREVIEW, { WXK_END }),
|
|
|
|
// Painting tools
|
|
SHORTCUT(PaintToolCircle, "paint_tool_circle", L("Circle"), PAINTING, { 'C' }),
|
|
SHORTCUT(PaintToolSphere, "paint_tool_sphere", L("Sphere"), PAINTING, { 'S' }),
|
|
SHORTCUT(PaintToolFill, "paint_tool_fill", L("Fill"), PAINTING, { 'F' }),
|
|
SHORTCUT(PaintToolGapFill, "paint_tool_gap_fill", L("Gap Fill"), PAINTING, { 'G' }),
|
|
SHORTCUT(PaintToolTriangle, "paint_tool_triangle", L("Triangle"), PAINTING, { 'T' }),
|
|
SHORTCUT(PaintToolHeightRange, "paint_tool_height_range", L("Height Range"), PAINTING, { 'H' }),
|
|
|
|
// Camera
|
|
SHORTCUT(ViewDefault, "view_default", L("Camera view - Default"), GLOBAL, { '0', CTRL }),
|
|
SHORTCUT(ViewTop, "view_top", L("Camera view - Top"), GLOBAL, { '1', CTRL }),
|
|
SHORTCUT(ViewBottom, "view_bottom", L("Camera view - Bottom"), GLOBAL, { '2', CTRL }),
|
|
SHORTCUT(ViewFront, "view_front", L("Camera view - Front"), GLOBAL, { '3', CTRL }),
|
|
SHORTCUT(ViewRear, "view_rear", L("Camera view - Behind"), GLOBAL, { '4', CTRL }),
|
|
SHORTCUT(ViewLeft, "view_left", L("Camera Angle - Left side"), GLOBAL, { '5', CTRL }),
|
|
SHORTCUT(ViewRight, "view_right", L("Camera Angle - Right side"), GLOBAL, { '6', CTRL }),
|
|
SHORTCUT(ViewPlate, "view_plate", L("Camera view - Current plate"), GLOBAL, { '7', CTRL }),
|
|
REPEATING(ZoomIn, "zoom_in", L("Zoom in"), CANVAS, { 'I' }),
|
|
REPEATING(ZoomOut, "zoom_out", L("Zoom out"), CANVAS, { 'O' }),
|
|
SHORTCUT(Mouse3DSettings, "mouse3d_settings", L("Show/Hide 3Dconnexion devices settings dialog"), CANVAS, MOUSE3D_CHORD),
|
|
|
|
// Display
|
|
SHORTCUT(ShowLabels, "show_labels", L("Show object labels in 3D scene."), GLOBAL, { 'E', CTRL }),
|
|
SHORTCUT(ShowWireframe, "show_wireframe", L("Show/Hide wireframe"), CANVAS, { WXK_RETURN, CTRL_SHIFT }),
|
|
SHORTCUT(ToggleGcodeWindow, "toggle_gcode_window", L("On/Off G-code window"), PREVIEW, { 'C' }),
|
|
SHORTCUT(ToggleOneLayerMode, "toggle_one_layer_mode", L("On/Off one layer mode of the vertical slider"), PREVIEW, { 'L' }),
|
|
|
|
// Application
|
|
SHORTCUT(Preferences, "preferences", L("Preferences"), GLOBAL, PREFERENCES_CHORD),
|
|
SHORTCUT(Search, "search", L("Search"), GLOBAL, { 'F', CTRL }),
|
|
SHORTCUT(SwitchView, "switch_view", L("Switch between Prepare/Preview"), CANVAS, { WXK_TAB }),
|
|
SHORTCUT(CollapseSidebar, "collapse_sidebar", L("Collapse/Expand the sidebar"), CANVAS, { WXK_TAB, SHIFT }),
|
|
SHORTCUT(SpeedDial, "speed_dial", L("Open the speed dial"), GLOBAL, { WXK_SPACE }),
|
|
SHORTCUT(ReloadDevicePage, "reload_device_page", L("Reload the device page"), CANVAS, { WXK_F5 }),
|
|
SHORTCUT(KeyboardShortcuts, "keyboard_shortcuts", L("Show keyboard shortcuts list"), CANVAS, { '?' }),
|
|
}};
|
|
|
|
#undef SHORTCUT
|
|
#undef REPEATING
|
|
#undef STEPPING
|
|
|
|
// The first shortcut of each section's run in shortcut_table and the section's heading, indexed
|
|
// by ShortcutSection.
|
|
struct SectionInfo
|
|
{
|
|
Shortcut first;
|
|
const char* name;
|
|
};
|
|
|
|
constexpr std::array<SectionInfo, size_t(ShortcutSection::Count)> section_table = {{
|
|
{ Shortcut::NewProject, L("Project") },
|
|
{ Shortcut::SlicePlate, L("Slicing and printing") },
|
|
{ Shortcut::SelectAll, L("Selection") },
|
|
{ Shortcut::Undo, L("Editing") },
|
|
{ Shortcut::AddInstance, L("Objects") },
|
|
{ Shortcut::Arrange, L("Placement") },
|
|
{ Shortcut::GizmoMove, L("Gizmos") },
|
|
{ Shortcut::GoToLayer, L("Sliders") },
|
|
{ Shortcut::PaintToolCircle, L("Painting tools") },
|
|
{ Shortcut::ViewDefault, L("Camera") },
|
|
{ Shortcut::ShowLabels, L("Display") },
|
|
{ Shortcut::Preferences, L("Application") },
|
|
}};
|
|
|
|
constexpr bool sections_follow_table_order()
|
|
{
|
|
if (section_table.front().first != Shortcut(0))
|
|
return false;
|
|
for (size_t i = 1; i < section_table.size(); ++i)
|
|
if (section_table[i].first <= section_table[i - 1].first)
|
|
return false;
|
|
return true;
|
|
}
|
|
static_assert(sections_follow_table_order(), "section_table must begin at the first shortcut and ascend");
|
|
|
|
constexpr bool table_is_in_enum_order()
|
|
{
|
|
for (size_t i = 0; i < shortcut_table.size(); ++i)
|
|
if (shortcut_table[i].id != Shortcut(i))
|
|
return false;
|
|
return true;
|
|
}
|
|
static_assert(table_is_in_enum_order(), "shortcut_table must list every Shortcut in declaration order");
|
|
|
|
const char* CONFIG_SECTION = "shortcuts";
|
|
const char* UNBOUND = "none";
|
|
|
|
bool share_context(uint8_t a, uint8_t b) { return (a & b) != 0 || (a & GLOBAL) != 0 || (b & GLOBAL) != 0; }
|
|
|
|
// The modifiers a modifier_variants shortcut accepts on top of its binding.
|
|
constexpr int STEP_MODIFIERS = wxMOD_SHIFT | wxMOD_CONTROL;
|
|
|
|
// The step modifiers chord adds to binding, 0 when chord is not a step of it; a binding with
|
|
// Shift or Ctrl of its own has no steps, so no two bindings share one.
|
|
int step_modifiers(const KeyChord& chord, const KeyChord& binding)
|
|
{
|
|
if (!binding.valid() || (binding.modifiers & STEP_MODIFIERS) != 0 || chord.key != binding.key ||
|
|
(chord.modifiers & ~STEP_MODIFIERS) != binding.modifiers)
|
|
return 0;
|
|
return chord.modifiers & STEP_MODIFIERS;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
const ShortcutInfo& shortcut_info(Shortcut shortcut) { return shortcut_table[size_t(shortcut)]; }
|
|
|
|
ShortcutSection shortcut_section(Shortcut shortcut)
|
|
{
|
|
size_t section = 0;
|
|
for (size_t i = 1; i < section_table.size(); ++i)
|
|
if (section_table[i].first <= shortcut)
|
|
section = i;
|
|
return ShortcutSection(section);
|
|
}
|
|
|
|
const char* section_name(ShortcutSection section) { return section_table[size_t(section)].name; }
|
|
|
|
std::vector<Shortcut> shortcuts_in(ShortcutContext context)
|
|
{
|
|
std::vector<Shortcut> out;
|
|
for (const ShortcutInfo& info : shortcut_table)
|
|
if (info.contexts & context_bit(context))
|
|
out.push_back(info.id);
|
|
return out;
|
|
}
|
|
|
|
ShortcutRegistry::ShortcutRegistry() { rebuild_index(); }
|
|
|
|
KeyChord ShortcutRegistry::binding(Shortcut shortcut) const
|
|
{
|
|
const std::optional<KeyChord>& override = m_overrides[size_t(shortcut)];
|
|
return override.has_value() ? *override : shortcut_info(shortcut).default_chord;
|
|
}
|
|
|
|
bool ShortcutRegistry::is_customized(Shortcut shortcut) const { return m_overrides[size_t(shortcut)].has_value(); }
|
|
|
|
std::string ShortcutRegistry::display(Shortcut shortcut) const { return binding(shortcut).display(); }
|
|
|
|
std::string ShortcutRegistry::with_key(const std::string& text, Shortcut shortcut) const
|
|
{
|
|
const std::string key = display(shortcut);
|
|
return key.empty() ? text : text + " [" + key + "]";
|
|
}
|
|
|
|
std::string ShortcutRegistry::accelerator(Shortcut shortcut) const
|
|
{
|
|
const KeyChord chord = binding(shortcut);
|
|
return chord.is_menu_accelerator() ? chord.to_string() : std::string();
|
|
}
|
|
|
|
std::optional<Shortcut> ShortcutRegistry::lookup(ShortcutContext context, const KeyChord& chord) const
|
|
{
|
|
if (!chord.valid())
|
|
return std::nullopt;
|
|
auto it = m_index.find(chord);
|
|
if (it == m_index.end())
|
|
return std::nullopt;
|
|
for (Shortcut shortcut : it->second)
|
|
if (shortcut_info(shortcut).contexts & context_bit(context))
|
|
return shortcut;
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<ShortcutRegistry::Match> ShortcutRegistry::match(ShortcutContext context, const KeyChord& chord) const
|
|
{
|
|
if (const std::optional<Shortcut> exact = lookup(context, chord); exact.has_value())
|
|
return Match{ *exact, 0 };
|
|
if ((chord.modifiers & STEP_MODIFIERS) == 0)
|
|
return std::nullopt;
|
|
for (const ShortcutInfo& info : shortcut_table)
|
|
if (info.modifier_variants && (info.contexts & context_bit(context)))
|
|
if (const int step = step_modifiers(chord, binding(info.id)); step != 0)
|
|
return Match{ info.id, step };
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::vector<Shortcut> ShortcutRegistry::conflicts(Shortcut shortcut, const KeyChord& chord) const
|
|
{
|
|
std::vector<Shortcut> out;
|
|
if (!chord.valid())
|
|
return out;
|
|
const uint8_t contexts = shortcut_info(shortcut).contexts;
|
|
for (const ShortcutInfo& other : shortcut_table)
|
|
if (other.id != shortcut && share_context(contexts, other.contexts) && binding(other.id) == chord)
|
|
out.push_back(other.id);
|
|
return out;
|
|
}
|
|
|
|
std::optional<Shortcut> ShortcutRegistry::step_owner(Shortcut shortcut, const KeyChord& chord) const
|
|
{
|
|
const uint8_t contexts = shortcut_info(shortcut).contexts;
|
|
for (const ShortcutInfo& other : shortcut_table)
|
|
if (other.modifier_variants && other.id != shortcut && share_context(contexts, other.contexts))
|
|
if (const int step = step_modifiers(chord, binding(other.id)); step == wxMOD_SHIFT || step == wxMOD_CONTROL) // the combined step stays assignable
|
|
return other.id;
|
|
return std::nullopt;
|
|
}
|
|
|
|
void ShortcutRegistry::bind(Shortcut shortcut, const KeyChord& chord)
|
|
{
|
|
const ShortcutInfo& info = shortcut_info(shortcut);
|
|
if (chord == info.default_chord)
|
|
m_overrides[size_t(shortcut)].reset();
|
|
else
|
|
m_overrides[size_t(shortcut)] = chord;
|
|
rebuild_index();
|
|
}
|
|
|
|
void ShortcutRegistry::reset(Shortcut shortcut)
|
|
{
|
|
m_overrides[size_t(shortcut)].reset();
|
|
rebuild_index();
|
|
}
|
|
|
|
void ShortcutRegistry::reset_all()
|
|
{
|
|
m_overrides.fill(std::nullopt);
|
|
rebuild_index();
|
|
}
|
|
|
|
void ShortcutRegistry::load(const AppConfig& config)
|
|
{
|
|
m_overrides.fill(std::nullopt);
|
|
if (config.has_section(CONFIG_SECTION)) {
|
|
const std::map<std::string, std::string>& section = config.get_section(CONFIG_SECTION);
|
|
for (const ShortcutInfo& info : shortcut_table) {
|
|
auto it = section.find(info.key);
|
|
if (it == section.end())
|
|
continue;
|
|
if (it->second == UNBOUND)
|
|
m_overrides[size_t(info.id)] = KeyChord{};
|
|
else if (std::optional<KeyChord> chord = KeyChord::parse(it->second); chord.has_value())
|
|
// A Global chord is seen before any text field, so it needs a modifier or a non-typing key.
|
|
if ((info.contexts & GLOBAL) == 0 || chord->is_menu_accelerator())
|
|
m_overrides[size_t(info.id)] = *chord;
|
|
}
|
|
}
|
|
rebuild_index();
|
|
}
|
|
|
|
void ShortcutRegistry::save(AppConfig& config) const
|
|
{
|
|
for (const ShortcutInfo& info : shortcut_table) {
|
|
const std::optional<KeyChord>& override = m_overrides[size_t(info.id)];
|
|
if (override.has_value())
|
|
config.set(CONFIG_SECTION, info.key, override->valid() ? override->to_string() : UNBOUND);
|
|
else
|
|
config.erase(CONFIG_SECTION, info.key);
|
|
}
|
|
}
|
|
|
|
void ShortcutRegistry::rebuild_index()
|
|
{
|
|
m_index.clear();
|
|
for (const ShortcutInfo& info : shortcut_table)
|
|
if (const KeyChord chord = binding(info.id); chord.valid())
|
|
m_index[chord].push_back(info.id);
|
|
}
|
|
|
|
}} // namespace Slic3r::GUI
|