diff --git a/Designer/Data/CommandsConfig.xml b/Designer/Data/CommandsConfig.xml index 188da069..f57ebca1 100644 --- a/Designer/Data/CommandsConfig.xml +++ b/Designer/Data/CommandsConfig.xml @@ -229,7 +229,6 @@ diff --git a/Designer/Data/Scripts/generate_solid_designer_commands.py b/Designer/Data/Scripts/generate_solid_designer_commands.py new file mode 100644 index 00000000..a5955d96 --- /dev/null +++ b/Designer/Data/Scripts/generate_solid_designer_commands.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +#Author: Yoichiro Nambu +#Date: 2025.12.12 +""" +Generate SolidDesignerCommands.h from CommandsConfig.xml + +Usage: + python generate_solid_designer_commands.py \ + --xml path/to/CommandsConfig.xml \ + --out path/to/SolidDesignerCommands.h +""" + +import argparse +import sys +import textwrap +import xml.etree.ElementTree as ET +from dataclasses import dataclass +from pathlib import Path +from typing import List, Dict, Set + +print("[DEBUG] argv:", sys.argv) + +@dataclass +class CommandSpec: + id: str + label: str + category: str + + +def parse_commands(xml_path: Path) -> List[CommandSpec]: + """ + Parse CommandsConfig.xml and return a flat list of CommandSpec. + """ + try: + tree = ET.parse(str(xml_path)) + except ET.ParseError as e: + print(f"[ERROR] Failed to parse XML '{xml_path}': {e}", file=sys.stderr) + sys.exit(1) + + root = tree.getroot() + + # 兼容两种写法: + commands_nodes = [] + if root.tag.lower().endswith("commandsconfig"): + commands_nodes = root.findall(".//commands/command") + else: + commands_nodes = root.findall(".//command") + + specs: List[CommandSpec] = [] + + for elem in commands_nodes: + cid = elem.get("id") + if not cid: + continue + label = elem.get("label", "") + category = elem.get("category", "") + + specs.append(CommandSpec(id=cid.strip(), + label=label.strip(), + category=category.strip())) + + if not specs: + print(f"[WARN] No elements found in '{xml_path}'", file=sys.stderr) + + # 检查重复 id + seen_ids: Set[str] = set() + duplicates: Set[str] = set() + for c in specs: + if c.id in seen_ids: + duplicates.add(c.id) + seen_ids.add(c.id) + if duplicates: + print("[ERROR] Duplicate command id(s) detected:", file=sys.stderr) + for d in sorted(duplicates): + print(f" - {d}", file=sys.stderr) + sys.exit(1) + + # 排序:按 id 排一下,保证生成文件稳定 + specs.sort(key=lambda c: c.id) + return specs + + +def id_to_const_name(cmd_id: str) -> str: + """ + Convert 'file.save' -> 'FILE_SAVE' + """ + result_chars = [] + for ch in cmd_id: + if ch.isalnum(): + result_chars.append(ch.upper()) + else: + # '.' '-' ' ' 等全部归一成 '_' + result_chars.append('_') + name = "".join(result_chars) + + # 去掉重复的 '_'(比如 "..") + # 連続する '_' を除去(例:"..") + while "__" in name: + name = name.replace("__", "_") + + # 去掉头尾 '_' + # 先頭・末尾の '_' を削除 + name = name.strip("_") + + # 如果第一个字符是数字,前面加前缀 + # 先頭が数字の場合は接頭辞を付与 + if name and name[0].isdigit(): + name = "CMD_" + name + + if not name: + raise ValueError(f"Cannot convert command id '{cmd_id}' to a valid C++ name") + + return name + + +def generate_header(specs: List[CommandSpec], + out_path: Path, + namespace_root: str = "sdr", + namespace_leaf: str = "Cmd") -> None: + """ + Generate SolidDesignerCommands.h into out_path. + """ + # 检查常量名是否冲突 + # 定数名の衝突をチェック + const_map: Dict[str, CommandSpec] = {} + collisions: Dict[str, List[str]] = {} + + for c in specs: + const_name = id_to_const_name(c.id) + if const_name in const_map: + collisions.setdefault(const_name, []).append(c.id) + else: + const_map[const_name] = c + + if collisions: + print("[ERROR] Different command ids mapped to the same C++ name:", file=sys.stderr) + for cname, ids in collisions.items(): + all_ids = [const_map[cname].id] + ids + print(f" {cname}: {', '.join(all_ids)}", file=sys.stderr) + sys.exit(1) + + lines: List[str] = [] + + lines.extend( + [ + "// -----------------------------------------------------------------------------", + "// SolidDesignerCommands.h", + "//", + "// AUTO-GENERATED FILE. DO NOT EDIT BY HAND.", + "//", + "// Generated from: CommandsConfig.xml", + "// -----------------------------------------------------------------------------", + "", + "#pragma once", + "#include ", + "", + f"namespace {namespace_root}", + "{", + f" namespace {namespace_leaf}", + " {", + " // NOTE:", + " // - Each constant is a std::string_view to the command id as defined", + " // in CommandsConfig.xml.", + " // - Use these constants in ICommand::Id() implementations, Ribbon/Menu", + " // bindings, etc. Never hard-code string literals like \"file.save\".", + "", + ] + ) + + # 按 category 分组输出,便于阅读 + # category ごとにグループ化して出力(可読性向上) + by_category: Dict[str, List[CommandSpec]] = {} + for c in specs: + cat = c.category or "uncategorized" + by_category.setdefault(cat, []).append(c) + + for cat in sorted(by_category.keys()): + lines.append(f" // === Category: {cat} ===") + for c in by_category[cat]: + const_name = id_to_const_name(c.id) + + # label 作为注释 + # label をコメントとして付与 + label_comment = f" // {c.label}" if c.label else "" + lines.append( + f' inline constexpr std::string_view {const_name:<30} = "{c.id}";{label_comment}' + ) + lines.append("") + + lines.extend( + [ + " }} // namespace {0}".format(namespace_leaf), + "}} // namespace {0}".format(namespace_root), + "", + ] + ) + + out_path.write_text("\n".join(lines), encoding="utf-8") + print(f"[INFO] Generated {out_path} ({len(specs)} commands).") + + +def main(argv: List[str]) -> int: + parser = argparse.ArgumentParser( + description="Generate SolidDesignerCommands.h from CommandsConfig.xml" + ) + parser.add_argument("--xml", required=True, help="Path to CommandsConfig.xml") + parser.add_argument("--out", required=True, help="Path to SolidDesignerCommands.h") + parser.add_argument( + "--namespace-root", default="sdr", help="Root namespace (default: sdr)" + ) + parser.add_argument( + "--namespace-leaf", default="Cmd", help="Leaf namespace (default: Cmd)" + ) + + args = parser.parse_args(argv) + + xml_path = Path(args.xml).resolve() + out_path = Path(args.out).resolve() + + if not xml_path.is_file(): + print(f"[ERROR] XML file not found: {xml_path}", file=sys.stderr) + return 1 + + specs = parse_commands(xml_path) + generate_header( + specs, + out_path, + namespace_root=args.namespace_root, + namespace_leaf=args.namespace_leaf, + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/Designer/UI/SolidDesignerCommand/CMakeLists.txt b/Designer/UI/SolidDesignerCommand/CMakeLists.txt index 5f0160dd..24c6f1a7 100644 --- a/Designer/UI/SolidDesignerCommand/CMakeLists.txt +++ b/Designer/UI/SolidDesignerCommand/CMakeLists.txt @@ -8,6 +8,16 @@ message("*--*--*--*--*--*--*--*--*--*--*--*--*--Start SolidDesignerCommand--*--* add_definitions(-DSOLID_DESIGNER_UI_HOME) add_definitions(-DSOLID_DESIGNER_COMMAND_HOME) +message("CMAKE_SOURCE_DIR = ${CMAKE_SOURCE_DIR}") +# 先指定你项目内置的 Python 解释器 +set(_SD_PY_ROOT "${CMAKE_SOURCE_DIR}/ToolChain/Python310") +set(Python3_EXECUTABLE "${_SD_PY_ROOT}/python.exe" CACHE FILEPATH "SolidDesigner bundled Python" FORCE) + +# 然后再找 Python3 +find_package(Python3 REQUIRED COMPONENTS Interpreter) + +message(STATUS "Using Python3_EXECUTABLE = ${Python3_EXECUTABLE}") + message("${PROJECT_SOURCE_DIR}") set(ABS_SDC "${PROJECT_SOURCE_DIR}/Designer/UI/SolidDesignerCommand") @@ -99,14 +109,15 @@ endif() #----------------------------------------------------------------------------- # Automatically generate SolidDesignerCommands.h #----------------------------------------------------------------------------- -set(COMMANDS_XML ${CMAKE_SOURCE_DIR}/config/CommandsConfig.xml) -set(COMMANDS_GEN_DIR ${CMAKE_BINARY_DIR}/generated/$) -set(COMMANDS_HDR ${COMMANDS_GEN_DIR}/SolidDesignerCommands.h) -set(COMMANDS_PY ${CMAKE_SOURCE_DIR}/tools/generate_solid_designer_commands.py) +set(COMMANDS_XML ${PROJECT_SOURCE_DIR}/Designer/Data/CommandsConfig.xml) +set(COMMANDS_HDR ${PROJECT_SOURCE_DIR}/Designer/UI/SolidDesignerCommand/SolidDesignerCommands.h) +set(COMMANDS_PY ${CMAKE_SOURCE_DIR}/Designer/Data/Scripts/generate_solid_designer_commands.py) + +message(STATUS "COMMANDS_XML = ${COMMANDS_XML}") +message(STATUS "COMMANDS_PY = ${COMMANDS_PY}") add_custom_command( OUTPUT ${COMMANDS_HDR} - COMMAND ${CMAKE_COMMAND} -E make_directory ${COMMANDS_GEN_DIR} COMMAND ${Python3_EXECUTABLE} ${COMMANDS_PY} --xml ${COMMANDS_XML} --out ${COMMANDS_HDR} @@ -116,8 +127,6 @@ add_custom_command( ) add_custom_target(GenerateSolidDesignerCommands DEPENDS ${COMMANDS_HDR}) - -target_include_directories(SolidDesignerCommand PRIVATE ${COMMANDS_GEN_DIR}) add_dependencies(SolidDesignerCommand GenerateSolidDesignerCommands) # 可选:让 IDE 看到生成文件 diff --git a/Designer/UI/SolidDesignerCommand/SolidDesignerCommands.h b/Designer/UI/SolidDesignerCommand/SolidDesignerCommands.h new file mode 100644 index 00000000..c3074c86 --- /dev/null +++ b/Designer/UI/SolidDesignerCommand/SolidDesignerCommands.h @@ -0,0 +1,221 @@ +// ----------------------------------------------------------------------------- +// SolidDesignerCommands.h +// +// AUTO-GENERATED FILE. DO NOT EDIT BY HAND. +// +// Generated from: CommandsConfig.xml +// ----------------------------------------------------------------------------- + +#pragma once +#include + +namespace sdr +{ + namespace Cmd + { + // NOTE: + // - Each constant is a std::string_view to the command id as defined + // in CommandsConfig.xml. + // - Use these constants in ICommand::Id() implementations, Ribbon/Menu + // bindings, etc. Never hard-code string literals like "file.save". + + // === Category: analysis === + inline constexpr std::string_view ANALYSIS_BODYCHECK = "analysis.bodyCheck"; // Body Check + inline constexpr std::string_view ANALYSIS_INTERFERENCE = "analysis.interference"; // Interference Check + inline constexpr std::string_view ANALYSIS_MASS = "analysis.mass"; // Mass Analysis + + // === Category: application === + inline constexpr std::string_view APP_SWITCH_ASSEMBLY = "app.switch.assembly"; // Assembly Design + inline constexpr std::string_view APP_SWITCH_BIM = "app.switch.bim"; // BIM + inline constexpr std::string_view APP_SWITCH_PARTMODELING = "app.switch.partModeling"; // Part Modeling + inline constexpr std::string_view APP_SWITCH_SHEETMETAL = "app.switch.sheetMetal"; // Sheet Metal Design + inline constexpr std::string_view APP_SWITCH_SHIP = "app.switch.ship"; // Ship Design + inline constexpr std::string_view APP_SWITCH_SKETCH = "app.switch.sketch"; // Sketch Environment + + // === Category: assembly === + inline constexpr std::string_view ASM_ADDCOMPONENT = "asm.addComponent"; // Add Component + inline constexpr std::string_view ASM_ALIGN = "asm.align"; // Align + inline constexpr std::string_view ASM_ARRANGE = "asm.arrange"; // Arrange Components + inline constexpr std::string_view ASM_CONSTRAINT = "asm.constraint"; // Assembly Constraints + inline constexpr std::string_view ASM_DISTANCE = "asm.distance"; // Distance + inline constexpr std::string_view ASM_EXPLODE = "asm.explode"; // Exploded View + inline constexpr std::string_view ASM_MATE = "asm.mate"; // Mate + inline constexpr std::string_view ASM_NEWCOMPONENT = "asm.newComponent"; // New Component + inline constexpr std::string_view ASM_REPLACECOMPONENT = "asm.replaceComponent"; // Replace Component + inline constexpr std::string_view ASM_UNEXPLODE = "asm.unexplode"; // Collapse + + // === Category: boolean === + inline constexpr std::string_view MODEL_BOOLEAN_INTERSECT = "model.boolean.intersect"; // Intersect + inline constexpr std::string_view MODEL_BOOLEAN_SUBTRACT = "model.boolean.subtract"; // Subtract + inline constexpr std::string_view MODEL_BOOLEAN_UNION = "model.boolean.union"; // Union + + // === Category: curve === + inline constexpr std::string_view CURVE_ARC = "curve.arc"; // Arc + inline constexpr std::string_view CURVE_CIRCLE = "curve.circle"; // Circle + inline constexpr std::string_view CURVE_EXTEND = "curve.extend"; // Extend Curve + inline constexpr std::string_view CURVE_FILLET = "curve.fillet"; // Curve Fillet + inline constexpr std::string_view CURVE_FITSPLINE = "curve.fitSpline"; // Fit Spline + inline constexpr std::string_view CURVE_JOIN = "curve.join"; // Join Curves + inline constexpr std::string_view CURVE_LINE = "curve.line"; // Line + inline constexpr std::string_view CURVE_OFFSET = "curve.offset"; // Offset Curve + inline constexpr std::string_view CURVE_POINT = "curve.point"; // Point + inline constexpr std::string_view CURVE_RECTANGLE = "curve.rectangle"; // Rectangle + inline constexpr std::string_view CURVE_SPLINETHROUGH = "curve.splineThrough"; // Spline Through Points + inline constexpr std::string_view CURVE_STUDIOSPLINE = "curve.studioSpline"; // Studio Spline + inline constexpr std::string_view CURVE_TRIM = "curve.trim"; // Trim Curve + + // === Category: datum === + inline constexpr std::string_view DATUM_AXIS = "datum.axis"; // Datum Axis + inline constexpr std::string_view DATUM_CS = "datum.cs"; // Coordinate System + inline constexpr std::string_view DATUM_PLANE = "datum.plane"; // Datum Plane + inline constexpr std::string_view DATUM_POINT = "datum.point"; // Point + + // === Category: display === + inline constexpr std::string_view DISPLAY_HIDDENLINE = "display.hiddenLine"; // Hidden Line + inline constexpr std::string_view DISPLAY_HIDE = "display.hide"; // Hide + inline constexpr std::string_view DISPLAY_ISOLATE = "display.isolate"; // Isolate + inline constexpr std::string_view DISPLAY_SHADED = "display.shaded"; // Shaded + inline constexpr std::string_view DISPLAY_SHADEDWITHEDGES = "display.shadedWithEdges"; // Shaded with Edges + inline constexpr std::string_view DISPLAY_SHOW = "display.show"; // Show + inline constexpr std::string_view DISPLAY_WIREFRAME = "display.wireframe"; // Wireframe + + // === Category: edit === + inline constexpr std::string_view EDIT_COPY = "edit.copy"; // Copy + inline constexpr std::string_view EDIT_CUT = "edit.cut"; // Cut + inline constexpr std::string_view EDIT_DELETE = "edit.delete"; // Delete + inline constexpr std::string_view EDIT_PASTE = "edit.paste"; // Paste + inline constexpr std::string_view EDIT_REDO = "edit.redo"; // Redo + inline constexpr std::string_view EDIT_SELECTALL = "edit.selectAll"; // Select All + inline constexpr std::string_view EDIT_UNDO = "edit.undo"; // Undo + + // === Category: feature === + inline constexpr std::string_view MODEL_FEATURE_DELETE = "model.feature.delete"; // Delete Feature + inline constexpr std::string_view MODEL_FEATURE_SUPPRESS = "model.feature.suppress"; // Suppress Feature + inline constexpr std::string_view MODEL_FEATURE_UNSUPPRESS = "model.feature.unsuppress"; // Unsuppress Feature + + // === Category: file === + inline constexpr std::string_view FILE_CLOSE = "file.close"; // Close + inline constexpr std::string_view FILE_EXIT = "file.exit"; // Exit + inline constexpr std::string_view FILE_NEW = "file.new"; // New + inline constexpr std::string_view FILE_OPEN = "file.open"; // Open + inline constexpr std::string_view FILE_SAVE = "file.save"; // Save + inline constexpr std::string_view FILE_SAVEAS = "file.saveAs"; // Save As + + // === Category: gc === + inline constexpr std::string_view GC_CHECKUPDATE = "gc.checkUpdate"; // Check for Updates + inline constexpr std::string_view GC_SETTINGS = "gc.settings"; // GC Settings + inline constexpr std::string_view GC_STDPARTLIBRARY = "gc.stdPartLibrary"; // Standard Parts Library + + // === Category: help === + inline constexpr std::string_view HELP_ABOUT = "help.about"; // About + inline constexpr std::string_view HELP_CONTENTS = "help.contents"; // Help Contents + + // === Category: measure === + inline constexpr std::string_view MEASURE_ANGLE = "measure.angle"; // Angle + inline constexpr std::string_view MEASURE_DIALOG = "measure.dialog"; // Measure Dialog + inline constexpr std::string_view MEASURE_DISTANCE = "measure.distance"; // Distance + inline constexpr std::string_view MEASURE_DRAFT = "measure.draft"; // Draft Analysis + inline constexpr std::string_view MEASURE_MASSPROPS = "measure.massProps"; // Mass Properties + inline constexpr std::string_view MEASURE_RADIUS = "measure.radius"; // Radius + inline constexpr std::string_view MEASURE_SECTION = "measure.section"; // Section Measurement + + // === Category: modeling === + inline constexpr std::string_view MODEL_CHAMFER = "model.chamfer"; // Chamfer + inline constexpr std::string_view MODEL_DRAFT = "model.draft"; // Draft + inline constexpr std::string_view MODEL_EXTRUDE = "model.extrude"; // Extrude + inline constexpr std::string_view MODEL_FILLET = "model.fillet"; // Fillet + inline constexpr std::string_view MODEL_HOLE = "model.hole"; // Hole + inline constexpr std::string_view MODEL_REVOLVE = "model.revolve"; // Revolve + inline constexpr std::string_view MODEL_SHELL = "model.shell"; // Shell + inline constexpr std::string_view MODEL_SLOT = "model.slot"; // Slot + + // === Category: selection === + inline constexpr std::string_view SELECT_FILTER = "select.filter"; // Selection Filter + inline constexpr std::string_view SELECT_LASSO = "select.lasso"; // Lasso Select + inline constexpr std::string_view SELECT_RECTANGLE = "select.rectangle"; // Rectangle Select + inline constexpr std::string_view SELECT_SINGLE = "select.single"; // Single Select + + // === Category: sheet === + inline constexpr std::string_view SHEET_BASEFLANGE = "sheet.baseFlange"; // Base Flange + inline constexpr std::string_view SHEET_BEND = "sheet.bend"; // Bend + inline constexpr std::string_view SHEET_BREAKCORNER = "sheet.breakCorner"; // Break Corner + inline constexpr std::string_view SHEET_CONTOURFLANGE = "sheet.contourFlange"; // Contour Flange + inline constexpr std::string_view SHEET_CORNERRELIEF = "sheet.cornerRelief"; // Corner Relief + inline constexpr std::string_view SHEET_CUTOUT = "sheet.cutout"; // Cutout + inline constexpr std::string_view SHEET_EDGEFLANGE = "sheet.edgeFlange"; // Edge Flange + inline constexpr std::string_view SHEET_EXPORTFLAT = "sheet.exportFlat"; // Export Flat + inline constexpr std::string_view SHEET_FLATTEN = "sheet.flatten"; // Flat Pattern + inline constexpr std::string_view SHEET_HOLE = "sheet.hole"; // Hole + inline constexpr std::string_view SHEET_JOG = "sheet.jog"; // Jog + inline constexpr std::string_view SHEET_LOFTEDFLANGE = "sheet.loftedFlange"; // Lofted Flange + inline constexpr std::string_view SHEET_REBEND = "sheet.rebend"; // Rebend + inline constexpr std::string_view SHEET_TAB = "sheet.tab"; // Tab + inline constexpr std::string_view SHEET_UNBEND = "sheet.unbend"; // Unbend + + // === Category: sketch === + inline constexpr std::string_view SKETCH_EDIT = "sketch.edit"; // Edit Sketch + inline constexpr std::string_view SKETCH_MIRROR = "sketch.mirror"; // Mirror Sketch + inline constexpr std::string_view SKETCH_NEW = "sketch.new"; // Sketch + inline constexpr std::string_view SKETCH_REUSE = "sketch.reuse"; // Reuse Sketch + + // === Category: sketch.constraint === + inline constexpr std::string_view SKETCH_CONSTRAINT_COINCIDENT = "sketch.constraint.coincident"; // Coincident + inline constexpr std::string_view SKETCH_CONSTRAINT_EQUAL = "sketch.constraint.equal"; // Equal + inline constexpr std::string_view SKETCH_CONSTRAINT_FIX = "sketch.constraint.fix"; // Fix + inline constexpr std::string_view SKETCH_CONSTRAINT_HORIZONTAL = "sketch.constraint.horizontal"; // Horizontal + inline constexpr std::string_view SKETCH_CONSTRAINT_PARALLEL = "sketch.constraint.parallel"; // Parallel + inline constexpr std::string_view SKETCH_CONSTRAINT_PERPENDICULAR = "sketch.constraint.perpendicular"; // Perpendicular + inline constexpr std::string_view SKETCH_CONSTRAINT_SYMMETRIC = "sketch.constraint.symmetric"; // Symmetric + inline constexpr std::string_view SKETCH_CONSTRAINT_TANGENT = "sketch.constraint.tangent"; // Tangent + inline constexpr std::string_view SKETCH_CONSTRAINT_VERTICAL = "sketch.constraint.vertical"; // Vertical + + // === Category: sketch.dim === + inline constexpr std::string_view SKETCH_DIM_ANGLE = "sketch.dim.angle"; // Angular Dimension + inline constexpr std::string_view SKETCH_DIM_DIAMETER = "sketch.dim.diameter"; // Diameter Dimension + inline constexpr std::string_view SKETCH_DIM_GENERAL = "sketch.dim.general"; // Smart Dimension + inline constexpr std::string_view SKETCH_DIM_HORIZONTAL = "sketch.dim.horizontal"; // Horizontal Dimension + inline constexpr std::string_view SKETCH_DIM_RADIUS = "sketch.dim.radius"; // Radius Dimension + inline constexpr std::string_view SKETCH_DIM_VERTICAL = "sketch.dim.vertical"; // Vertical Dimension + + // === Category: sketch.modify === + inline constexpr std::string_view SKETCH_MODIFY_EXTEND = "sketch.modify.extend"; // Extend + inline constexpr std::string_view SKETCH_MODIFY_FILLET = "sketch.modify.fillet"; // Fillet + inline constexpr std::string_view SKETCH_MODIFY_MIRROR = "sketch.modify.mirror"; // Mirror + inline constexpr std::string_view SKETCH_MODIFY_OFFSET = "sketch.modify.offset"; // Offset + inline constexpr std::string_view SKETCH_MODIFY_PATTERN = "sketch.modify.pattern"; // Pattern + inline constexpr std::string_view SKETCH_MODIFY_TRIM = "sketch.modify.trim"; // Trim + + // === Category: synchronous === + inline constexpr std::string_view SMOVE_FACE = "smove.face"; // Move Face + inline constexpr std::string_view SYNCHRONOUS_DELETEFACE = "synchronous.deleteFace"; // Delete Face + inline constexpr std::string_view SYNCHRONOUS_OFFSETREGION = "synchronous.offsetRegion"; // Offset Region + inline constexpr std::string_view SYNCHRONOUS_PULL = "synchronous.pull"; // Pull Face + + // === Category: tools === + inline constexpr std::string_view TOOLS_COMMANDLINE = "tools.commandLine"; // Command Line + inline constexpr std::string_view TOOLS_EXPRESSIONS = "tools.expressions"; // Expressions + inline constexpr std::string_view TOOLS_JOURNAL_PAUSE = "tools.journal.pause"; // Pause Journal + inline constexpr std::string_view TOOLS_JOURNAL_PLAY = "tools.journal.play"; // Play Journal + inline constexpr std::string_view TOOLS_JOURNAL_RECORD = "tools.journal.record"; // Record Journal + inline constexpr std::string_view TOOLS_PYTHONCONSOLE = "tools.pythonConsole"; // Python Console + + // === Category: ui === + inline constexpr std::string_view UI_RESTORELAYOUT = "ui.restoreLayout"; // Restore Layout + inline constexpr std::string_view UI_SAVELAYOUT = "ui.saveLayout"; // Save Layout + inline constexpr std::string_view UI_TOGGLEMODE = "ui.toggleMode"; // Toggle Ribbon / Classic + + // === Category: view === + inline constexpr std::string_view VIEW_CLIPPINGPLANE = "view.clippingPlane"; // Clipping Plane + inline constexpr std::string_view VIEW_DYNAMICSECTION = "view.dynamicSection"; // Dynamic Section + inline constexpr std::string_view VIEW_FRONT = "view.front"; // Front + inline constexpr std::string_view VIEW_RIGHT = "view.right"; // Right + inline constexpr std::string_view VIEW_TOP = "view.top"; // Top + inline constexpr std::string_view VIEW_TRIMETRIC = "view.trimetric"; // Trimetric + + // === Category: window === + inline constexpr std::string_view WINDOW_ARRANGE = "window.arrange"; // Arrange Windows + inline constexpr std::string_view WINDOW_NEW = "window.new"; // New Window + inline constexpr std::string_view WINDOW_TILEHORIZONTAL = "window.tileHorizontal"; // Tile Horizontally + + } // namespace Cmd +} // namespace sdr