Design tab: follow Orca's theme, scale, mouse settings, dialogs and undo
Felix14-v2's review of OrcaSlicer#16019 found the tab behaving as a world of its own. - Icons: the design_* glyphs were drawn in a fixed light grey, made for the dark ribbon, and the toolbar re-tinted some of them by rebuilding the bitmap from a wxImage, which drops the HiDPI scale factor Orca sets on Windows: at 150 % the icons came out half again too large for buttons that were sized in raw pixels, overlapping and clipped. The glyphs now use Orca's sidebar icon grey (#949494), which the icon cache maps per theme, nothing is re-tinted, toolbar glyphs drawn for Prepare's light toolbar use their "_dark" twin, and every size is in DIP. - Theme: the chrome colours were read once, at construction, and nothing in the tab answered a theme switch, so switching left light surfaces and unreadable text in a dark tab and the other way round. The colours are now {light, dark} token pairs; MainFrame::on_sys_color_changed reaches DesignPanel::on_sys_color_changed, which moves every token colour onto the other theme's, runs the app's dark pass and re-rasterises the icons. Card borders are StateColors, resolved at paint time. - Scale: MainFrame::on_dpi_changed reaches DesignPanel::msw_rescale, which re-rasterises every icon (buttons, flyout rows, card headers, the tree's image list, now sized from its bitmaps) and re-measures the Orca widgets. - Mouse: the canvas no longer forces middle-drag to orbit and right-drag to pan; it reads the drag actions in Preferences > Control like Prepare. Left-drag is shared with picking, so the whole-body rubber band takes Shift+left-drag while left-drag is given to the camera. - FPS counter: ImGui's display size is shared and only refreshed when a canvas sees its own size change; the Design canvas now re-announces its size when the tab is entered (and the editor canvas when it is left), as Plater does between Prepare and Preview. - Viewport text: the status line and the tool readout were top-level popups over GL. A popup does not follow its frame, so the empty-canvas hint floated over other applications, and the readout was never taken down with the tab. Both are drawn by the canvas in the tool's ImGui pass now, with the theme's overlay style. - Dialogs: messages use MessageDialog/RichMessageDialog; Add/Edit Variable is one Orca dialog with Name and Expression fields instead of two native text prompts; the Text dialog uses TextInput, ComboBox and CheckBox (its height is a TextInput: SpinInput is integer-only), and enumerates the installed fonts once per session. The ribbon's Confirm/Cancel, the reference pick buttons and the expression buttons are Orca Buttons; the variable actions are icon buttons like the other cards'. - Undo: the tab's own Undo/Redo buttons are gone. The top bar's Undo/Redo drive the Design history while the tab is shown, greyed to what an undo would do, as Ctrl+Z and Edit already did. - The first build of the tab logs how long each phase took ("Design tab build: ..."): it is under a second here but was reported at about fifteen on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
@@ -157,6 +157,11 @@ button (to terminate a chain, say) declines to also open a menu, through a read-
|
||||
Past either budget the event is navigation, and navigation does not transition the state
|
||||
machine.
|
||||
|
||||
Navigation itself is Prepare's: the camera reads the drag actions set in Preferences > Control
|
||||
for each button. The left button is shared with picking, so a whole body is swept with a
|
||||
rectangle on plain left-drag only while no camera action is assigned to it, and with
|
||||
Shift+left-drag otherwise — Prepare's own rectangle selection.
|
||||
|
||||
Entering a sketch changes three things at once so the mode is legible: a banner above the
|
||||
canvas (a sibling of the canvas, not a child over it — on GTK a child window over a
|
||||
`wxGLCanvas` is a native window and does not reliably stack over GL), the printer bed muted so
|
||||
@@ -182,6 +187,24 @@ degenerate edges are left out (`GeometryEngine::display_edges`), and the polylin
|
||||
once per shape, keyed by its `TShape`, because a recompute that leaves a body unchanged is the
|
||||
common case.
|
||||
|
||||
## Following the app
|
||||
|
||||
The tab is a page of Orca's main window and answers to the same settings as Prepare.
|
||||
|
||||
- **Theme.** Its chrome is coloured from a table of light/dark token pairs. A theme switch
|
||||
reaches `DesignPanel::on_sys_color_changed` from `MainFrame`, which moves every colour that is
|
||||
one theme's token onto the other theme's and then runs the app's own dark pass; the icons are
|
||||
Orca's sidebar grey, which the icon cache maps per theme, so they are re-rasterised rather
|
||||
than re-tinted.
|
||||
- **Scale.** Sizes are in DIP, and a DPI change reaches `DesignPanel::msw_rescale`, which
|
||||
re-rasterises every icon (button faces, flyout rows, card headers, the tree's image list).
|
||||
- **Viewport text.** The status line and the active tool's values are drawn by the canvas in
|
||||
its ImGui pass, so they go with the canvas: a top-level window over GL does not follow its
|
||||
frame and was left floating over other applications.
|
||||
- **Undo.** The tab keeps its own history (the recipe is not part of Prepare's snapshots), but
|
||||
it has no Undo/Redo of its own: the top bar, `Ctrl+Z` and Edit drive it while the tab is
|
||||
shown, greyed to what an undo would actually do.
|
||||
|
||||
## The offer is generated, not hand-written
|
||||
|
||||
Right-clicking geometry opens the *offer*: eight families in a fixed order, each verb at a
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 17 A11 11 0 0 1 20 17"/><circle cx="4" cy="17" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="12" cy="7" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="20" cy="17" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 17 A11 11 0 0 1 20 17"/><circle cx="4" cy="17" r="1.5" fill="#949494" stroke="none"/><circle cx="12" cy="7" r="1.5" fill="#949494" stroke="none"/><circle cx="20" cy="17" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 406 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 18 A13 13 0 0 1 18 5"/><path d="M5 5 5 18M5 5 18 5" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="5" cy="5" r="2" fill="#b6b6b6" stroke="none"/><circle cx="5" cy="18" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="18" cy="5" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 18 A13 13 0 0 1 18 5"/><path d="M5 5 5 18M5 5 18 5" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="5" cy="5" r="2" fill="#949494" stroke="none"/><circle cx="5" cy="18" r="1.5" fill="#949494" stroke="none"/><circle cx="18" cy="5" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 472 B After Width: | Height: | Size: 472 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="6" height="6"/><rect x="15" y="3" width="6" height="6"/><rect x="3" y="15" width="6" height="6"/><rect x="15" y="15" width="6" height="6"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="6" height="6"/><rect x="15" y="3" width="6" height="6"/><rect x="3" y="15" width="6" height="6"/><rect x="15" y="15" width="6" height="6"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
@@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="9" cy="12" r="6"/>
|
||||
<circle cx="15" cy="12" r="6"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 253 B After Width: | Height: | Size: 253 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 19 C 6 5, 12 5, 12 12 S 18 19, 21 5"/><circle cx="3" cy="19" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="21" cy="5" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 19 C 6 5, 12 5, 12 12 S 18 19, 21 5"/><circle cx="3" cy="19" r="1.5" fill="#949494" stroke="none"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/><circle cx="21" cy="5" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 420 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h16M4 20 16 6"/><path d="M11 20 A7 7 0 0 0 9.1 15.2"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h16M4 20 16 6"/><path d="M11 20 A7 7 0 0 0 9.1 15.2"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 254 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2.2" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2.2" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 279 B After Width: | Height: | Size: 279 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h20" stroke-dasharray="1.5 1.5"/><path d="M5 12h5M14 12h5"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h20" stroke-dasharray="1.5 1.5"/><path d="M5 12h5M14 12h5"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 260 B After Width: | Height: | Size: 260 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="3.5"/><circle cx="12" cy="12" r="1.3" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="3.5"/><circle cx="12" cy="12" r="1.3" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 312 B After Width: | Height: | Size: 312 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="7"/><path d="M6.3 17.7 17.7 6.3"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="7"/><path d="M6.3 17.7 17.7 6.3"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 247 B After Width: | Height: | Size: 247 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6v12M18 6v12M6 12h12M6 12l2.5-2M6 12l2.5 2M18 12l-2.5-2M18 12l-2.5 2"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6v12M18 6v12M6 12h12M6 12l2.5-2M6 12l2.5 2M18 12l-2.5-2M18 12l-2.5 2"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 269 B After Width: | Height: | Size: 269 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6h12M6 18h12M12 6v12M12 6l-2 2.5M12 6l2 2.5M12 18l-2-2.5M12 18l2-2.5"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6h12M6 18h12M12 6v12M12 6l-2 2.5M12 6l2 2.5M12 18l-2-2.5M12 18l2-2.5"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 269 B After Width: | Height: | Size: 269 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 9h14M5 15h14"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 9h14M5 15h14"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 213 B After Width: | Height: | Size: 213 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="4" cy="12" r="2.5"/><circle cx="15" cy="12" r="4.5"/><path d="M8 10h2M8 14h2"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="4" cy="12" r="2.5"/><circle cx="15" cy="12" r="4.5"/><path d="M8 10h2M8 14h2"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 277 B After Width: | Height: | Size: 277 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5.5" r="2"/><path d="M12 7.5V20M8.5 10h7M6 14a6 6 0 0 0 12 0"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5.5" r="2"/><path d="M12 7.5V20M8.5 10h7M6 14a6 6 0 0 0 12 0"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 269 B After Width: | Height: | Size: 269 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h18M3 9v6M21 9v6"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h18M3 9v6M21 9v6"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 219 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16M4 9v6M20 9v6"/><circle cx="12" cy="12" r="2" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16M4 9v6M20 9v6"/><circle cx="12" cy="12" r="2" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 279 B After Width: | Height: | Size: 279 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M7 20 13 4M13 20 19 4"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M7 20 13 4M13 20 19 4"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 219 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3v15h15M6 13h5v5"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3v15h15M6 13h5v5"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 217 B After Width: | Height: | Size: 217 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><path d="M12 12h8M17.5 9.5 20 12 17.5 14.5"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><path d="M12 12h8M17.5 9.5 20 12 17.5 14.5"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 324 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 6h8M8 18h8"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 6h8M8 18h8"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 272 B After Width: | Height: | Size: 272 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M6 8v8M18 8v8"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M6 8v8M18 8v8"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 272 B After Width: | Height: | Size: 272 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 8 4 12 8 16M16 8l4 4-4 4"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 8 4 12 8 16M16 8l4 4-4 4"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 286 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="14.5" r="5"/><path d="M3 9.5h18"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="14.5" r="5"/><path d="M3 9.5h18"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 240 B After Width: | Height: | Size: 240 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18M9 3h6M9 21h6"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18M9 3h6M9 21h6"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 219 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5v8l6 6h8"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5v8l6 6h8"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 210 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12l4 4L19 6"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12l4 4L19 6"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 212 B After Width: | Height: | Size: 212 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 279 B After Width: | Height: | Size: 279 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="7.5"/><path d="M6.7 6.7 17.3 17.3" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="6.7" cy="6.7" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="17.3" cy="17.3" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="7.5"/><path d="M6.7 6.7 17.3 17.3" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="6.7" cy="6.7" r="1.5" fill="#949494" stroke="none"/><circle cx="17.3" cy="17.3" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 420 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="4" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="5.07" cy="16" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="18.93" cy="16" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="4" r="1.5" fill="#949494" stroke="none"/><circle cx="5.07" cy="16" r="1.5" fill="#949494" stroke="none"/><circle cx="18.93" cy="16" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 407 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 270 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" stroke-dasharray="3 3"/><circle cx="4" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="20" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" stroke-dasharray="3 3"/><circle cx="4" cy="12" r="1.5" fill="#949494" stroke="none"/><circle cx="20" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 352 B After Width: | Height: | Size: 352 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="16" height="12" rx="1"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="16" height="12" rx="1"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 297 B |
@@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M6 8.5l6-3 6 3v7l-6 3-6-3z"/>
|
||||
<line x1="3" y1="14" x2="21" y2="10"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 268 B After Width: | Height: | Size: 268 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13M10 11v6M14 11v6"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13M10 11v6M14 11v6"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 248 B After Width: | Height: | Size: 248 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7v10M20 7v10M4 12h16M7 9l-3 3 3 3M17 9l3 3-3 3"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7v10M20 7v10M4 12h16M7 9l-3 3 3 3M17 9l3 3-3 3"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 247 B After Width: | Height: | Size: 247 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 4H18L15 20H9Z"/><path d="M9 20V4" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M9 14a4 4 0 0 0 1.3 3"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 4H18L15 20H9Z"/><path d="M9 20V4" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M9 14a4 4 0 0 0 1.3 3"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 307 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20V10a6 6 0 0 1 6-6h10"/><path d="M4 20h4M20 4v4" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20V10a6 6 0 0 1 6-6h10"/><path d="M4 20h4M20 4v4" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 290 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h4L19 9l-4-4L4 16z"/><path d="M14 6l4 4"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h4L19 9l-4-4L4 16z"/><path d="M14 6l4 4"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 242 B After Width: | Height: | Size: 242 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="12" rx="9.5" ry="5.5"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="12" rx="9.5" ry="5.5"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 292 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 13 A9.5 5.5 0 0 1 21 10"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="3" cy="13" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="21" cy="10" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 13 A9.5 5.5 0 0 1 21 10"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/><circle cx="3" cy="13" r="1.5" fill="#949494" stroke="none"/><circle cx="21" cy="10" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 409 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h11"/><path d="M14 12h7m-3-3l3 3-3 3"/><path d="M14 12h7" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h11"/><path d="M14 12h7m-3-3l3 3-3 3"/><path d="M14 12h7" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 300 B After Width: | Height: | Size: 300 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="11" width="16" height="9" rx="1"/><path d="M12 8V2M9 5l3-3 3 3"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="11" width="16" height="9" rx="1"/><path d="M12 8V2M9 5l3-3 3 3"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 266 B After Width: | Height: | Size: 266 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12C5 6 19 6 22 12 19 18 5 18 2 12Z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12C5 6 19 6 22 12 19 18 5 18 2 12Z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 266 B After Width: | Height: | Size: 266 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5v8a6 6 0 0 0 6 6h8"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5v8a6 6 0 0 0 6 6h8"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 220 B After Width: | Height: | Size: 220 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="12" cy="12" r="4"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="12" cy="12" r="4"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 328 B After Width: | Height: | Size: 328 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 19 19 5"/><circle cx="5" cy="19" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="19" cy="5" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 19 19 5"/><circle cx="5" cy="19" r="1.5" fill="#949494" stroke="none"/><circle cx="19" cy="5" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 330 B After Width: | Height: | Size: 330 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="15.5" width="8.5" height="4.5" rx="0.5"/><ellipse cx="16.5" cy="6" rx="4" ry="1.7"/><path d="M3.2 15.5 12.6 4.8M11.4 20 20.4 7.2"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="15.5" width="8.5" height="4.5" rx="0.5"/><ellipse cx="16.5" cy="6" rx="4" ry="1.7"/><path d="M3.2 15.5 12.6 4.8M11.4 20 20.4 7.2"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 332 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 7 4 12 8 17Z"/><path d="M16 7l4 5-4 5Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 7 4 12 8 17Z"/><path d="M16 7l4 5-4 5Z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 300 B After Width: | Height: | Size: 300 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="6" height="6" rx="1"/><path d="M12 8V2M9.6 4.4 12 2 14.4 4.4M12 16v6M9.6 19.6 12 22 14.4 19.6M8 12H2M4.4 9.6 2 12 4.4 14.4M16 12h6M19.6 9.6 22 12 19.6 14.4"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="6" height="6" rx="1"/><path d="M12 8V2M9.6 4.4 12 2 14.4 4.4M12 16v6M9.6 19.6 12 22 14.4 19.6M8 12H2M4.4 9.6 2 12 4.4 14.4M16 12h6M19.6 9.6 22 12 19.6 14.4"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 369 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M6 13l6 6 6-6"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M6 13l6 6 6-6"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 219 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M6 11l6-6 6 6"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M6 11l6-6 6 6"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 219 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 20V10A7 7 0 0 1 10 3H20"/><path d="M7 20V10A3 3 0 0 1 10 7H20"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 20V10A7 7 0 0 1 10 3H20"/><path d="M7 20V10A3 3 0 0 1 10 7H20"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 262 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="9" width="6" height="6" rx="1"/><rect x="10.5" y="9" width="6" height="6" rx="1"/><rect x="18" y="9" width="3.6" height="6" rx="1" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="9" width="6" height="6" rx="1"/><rect x="10.5" y="9" width="6" height="6" rx="1"/><rect x="18" y="9" width="3.6" height="6" rx="1" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 374 B After Width: | Height: | Size: 374 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8 13 8 21 15 11 15Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8 13 8 21 15 11 15Z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 220 B After Width: | Height: | Size: 220 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v3.4M12 16.6v3.4M4 12h3.4M16.6 12h3.4"/><circle cx="12" cy="12" r="2.2" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v3.4M12 16.6v3.4M4 12h3.4M16.6 12h3.4"/><circle cx="12" cy="12" r="2.2" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 302 B After Width: | Height: | Size: 302 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="12" cy="12" r="1.4" fill="#b6b6b6" stroke="none"/><circle cx="12" cy="4" r="1.8" fill="#b6b6b6" stroke="none"/><circle cx="18.9" cy="8" r="1.8" fill="#b6b6b6" stroke="none"/><circle cx="18.9" cy="16" r="1.8" fill="#b6b6b6" stroke="none"/><circle cx="12" cy="20" r="1.8" fill="#b6b6b6" stroke="none"/><circle cx="5.1" cy="16" r="1.8" fill="#b6b6b6" stroke="none"/><circle cx="5.1" cy="8" r="1.8" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="12" cy="12" r="1.4" fill="#949494" stroke="none"/><circle cx="12" cy="4" r="1.8" fill="#949494" stroke="none"/><circle cx="18.9" cy="8" r="1.8" fill="#949494" stroke="none"/><circle cx="18.9" cy="16" r="1.8" fill="#949494" stroke="none"/><circle cx="12" cy="20" r="1.8" fill="#949494" stroke="none"/><circle cx="5.1" cy="16" r="1.8" fill="#949494" stroke="none"/><circle cx="5.1" cy="8" r="1.8" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 695 B After Width: | Height: | Size: 695 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9Z"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9Z"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 290 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18 9 9 14 15 21 5"/><circle cx="3" cy="18" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="9" cy="9" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="14" cy="15" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="21" cy="5" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18 9 9 14 15 21 5"/><circle cx="3" cy="18" r="1.5" fill="#949494" stroke="none"/><circle cx="9" cy="9" r="1.5" fill="#949494" stroke="none"/><circle cx="14" cy="15" r="1.5" fill="#949494" stroke="none"/><circle cx="21" cy="5" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 462 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="16" height="12" rx="1"/><circle cx="4" cy="6" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="16" height="12" rx="1"/><circle cx="4" cy="6" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 295 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 16 10 6 20 8 14 18Z"/><circle cx="4" cy="16" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="10" cy="6" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 16 10 6 20 8 14 18Z"/><circle cx="4" cy="16" r="1.5" fill="#949494" stroke="none"/><circle cx="10" cy="6" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 342 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3.5" y="6" width="17" height="12" rx="4.5"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3.5" y="6" width="17" height="12" rx="4.5"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 239 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 16.5 A9 9 0 0 1 21 16.5 L16 16.5 A4 4 0 0 0 8 16.5 Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 16.5 A9 9 0 0 1 21 16.5 L16 16.5 A4 4 0 0 0 8 16.5 Z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 253 B After Width: | Height: | Size: 253 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="18" width="18" height="3" rx="0.5"/><rect x="6" y="6" width="2.5" height="12" rx="0.5"/><path d="M8.5 9L17 18"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="18" width="18" height="3" rx="0.5"/><rect x="6" y="6" width="2.5" height="12" rx="0.5"/><path d="M8.5 9L17 18"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 313 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3.5-7.1"/><path d="M21 3 21 8 16 8"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3.5-7.1"/><path d="M21 3 21 8 16 8"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 249 B After Width: | Height: | Size: 249 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="6" height="6" rx="1"/><path d="M8 8 3 3M3 7 3 3 7 3M16 8 21 3M17 3 21 3 21 7M8 16 3 21M3 17 3 21 7 21M16 16 21 21M17 21 21 21 21 17"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="6" height="6" rx="1"/><path d="M8 8 3 3M3 7 3 3 7 3M16 8 21 3M17 3 21 3 21 7M8 16 3 21M3 17 3 21 7 21M16 16 21 21M17 21 21 21 21 17"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 345 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3 5 17.5 8.8 14.2 11.3 20.3 13.4 19.4 11 13.6 15.9 13.6Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3 5 17.5 8.8 14.2 11.3 20.3 13.4 19.4 11 13.6 15.9 13.6Z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="1"/><path d="M8 4v12h8V4"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="1"/><path d="M8 4v12h8V4"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 258 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M16 3.5a2.1 2.1 0 0 1 3 3L7.5 18l-4 1 1-4Z"/><path d="M14.5 5 19 9.5"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M16 3.5a2.1 2.1 0 0 1 3 3L7.5 18l-4 1 1-4Z"/><path d="M14.5 5 19 9.5"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 266 B After Width: | Height: | Size: 266 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M8 7h8a5 5 0 0 1 0 10H8a5 5 0 0 1 0-10Z"/><circle cx="8" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="16" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M8 7h8a5 5 0 0 1 0 10H8a5 5 0 0 1 0-10Z"/><circle cx="8" cy="12" r="1.5" fill="#949494" stroke="none"/><circle cx="16" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 360 B After Width: | Height: | Size: 360 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5.3 13 A8 8 0 0 1 12.9 17 A2.5 2.5 0 0 0 17.3 14.5 A13 13 0 0 0 4.9 8 A2.5 2.5 0 0 0 5.3 13Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5.3 13 A8 8 0 0 1 12.9 17 A2.5 2.5 0 0 0 17.3 14.5 A13 13 0 0 0 4.9 8 A2.5 2.5 0 0 0 5.3 13Z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 291 B After Width: | Height: | Size: 291 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3.0 9.0 L16.0 9.0 L16.0 21.0 L3.0 21.0 ZM3.0 9.0 L7.0 5.0 L20.0 5.0 L16.0 9.0M20.0 5.0 L20.0 17.0 L16.0 21.0" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.168410256410256 13.423897435897436V13.868410256410256Q5.908923076923077 13.744307692307691 5.678769230769231 13.683384615384615Q5.448615384615385 13.622461538461538 5.23425641025641 13.622461538461538Q4.861948717948718 13.622461538461538 4.66 13.766871794871793Q4.458051282051283 13.91128205128205 4.458051282051283 14.177538461538461Q4.458051282051283 14.400923076923076 4.592307692307693 14.514871794871794Q4.726564102564103 14.628820512820512 5.101128205128205 14.69876923076923L5.3764102564102565 14.755179487179486Q5.886358974358975 14.852205128205128 6.128923076923078 15.09702564102564Q6.37148717948718 15.341846153846152 6.37148717948718 15.75251282051282Q6.37148717948718 16.242153846153844 6.043179487179488 16.49487179487179Q5.7148717948717955 16.747589743589742 5.080820512820513 16.747589743589742Q4.841641025641026 16.747589743589742 4.572000000000001 16.693435897435897Q4.302358974358975 16.639282051282052 4.013538461538462 16.53323076923077V16.063897435897434Q4.291076923076924 16.219589743589744 4.557333333333334 16.2985641025641Q4.823589743589745 16.37753846153846 5.080820512820513 16.37753846153846Q5.471179487179487 16.37753846153846 5.683282051282052 16.224102564102562Q5.895384615384616 16.070666666666664 5.895384615384616 15.786358974358974Q5.895384615384616 15.538153846153845 5.743076923076924 15.398256410256408Q5.590769230769231 15.258358974358973 5.243282051282051 15.188410256410256L4.965743589743591 15.134256410256409Q4.455794871794872 15.032717948717949 4.227897435897436 14.816102564102565Q4.000000000000001 14.599487179487179 4.000000000000001 14.213641025641024Q4.000000000000001 13.766871794871793 4.314769230769231 13.509641025641024Q4.629538461538462 13.252410256410256 5.182358974358975 13.252410256410256Q5.419282051282051 13.252410256410256 5.665230769230769 13.29528205128205Q5.911179487179488 13.338153846153846 6.168410256410256 13.423897435897436ZM6.615179487179487 13.313333333333333H9.46502564102564V13.696923076923076H8.269128205128204V16.682153846153845H7.811076923076923V13.696923076923076H6.615179487179487ZM9.905025641025642 13.313333333333333H12.035076923076925V13.696923076923076H10.360820512820514V14.69425641025641H11.965128205128206V15.077846153846153H10.360820512820514V16.2985641025641H12.075692307692309V16.682153846153845H9.905025641025642ZM13.280615384615384 13.687897435897435V14.953743589743588H13.85374358974359Q14.171897435897435 14.953743589743588 14.345641025641026 14.78902564102564Q14.519384615384615 14.624307692307692 14.519384615384615 14.319692307692307Q14.519384615384615 14.017333333333333 14.345641025641026 13.852615384615383Q14.171897435897435 13.687897435897435 13.85374358974359 13.687897435897435ZM12.824820512820512 13.313333333333333H13.85374358974359Q14.420102564102564 13.313333333333333 14.710051282051282 13.569435897435897Q15.0 13.82553846153846 15.0 14.319692307692307Q15.0 14.818358974358974 14.710051282051282 15.073333333333332Q14.420102564102564 15.32830769230769 13.85374358974359 15.32830769230769H13.280615384615384V16.682153846153845H12.824820512820512Z" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3.0 9.0 L16.0 9.0 L16.0 21.0 L3.0 21.0 ZM3.0 9.0 L7.0 5.0 L20.0 5.0 L16.0 9.0M20.0 5.0 L20.0 17.0 L16.0 21.0" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.168410256410256 13.423897435897436V13.868410256410256Q5.908923076923077 13.744307692307691 5.678769230769231 13.683384615384615Q5.448615384615385 13.622461538461538 5.23425641025641 13.622461538461538Q4.861948717948718 13.622461538461538 4.66 13.766871794871793Q4.458051282051283 13.91128205128205 4.458051282051283 14.177538461538461Q4.458051282051283 14.400923076923076 4.592307692307693 14.514871794871794Q4.726564102564103 14.628820512820512 5.101128205128205 14.69876923076923L5.3764102564102565 14.755179487179486Q5.886358974358975 14.852205128205128 6.128923076923078 15.09702564102564Q6.37148717948718 15.341846153846152 6.37148717948718 15.75251282051282Q6.37148717948718 16.242153846153844 6.043179487179488 16.49487179487179Q5.7148717948717955 16.747589743589742 5.080820512820513 16.747589743589742Q4.841641025641026 16.747589743589742 4.572000000000001 16.693435897435897Q4.302358974358975 16.639282051282052 4.013538461538462 16.53323076923077V16.063897435897434Q4.291076923076924 16.219589743589744 4.557333333333334 16.2985641025641Q4.823589743589745 16.37753846153846 5.080820512820513 16.37753846153846Q5.471179487179487 16.37753846153846 5.683282051282052 16.224102564102562Q5.895384615384616 16.070666666666664 5.895384615384616 15.786358974358974Q5.895384615384616 15.538153846153845 5.743076923076924 15.398256410256408Q5.590769230769231 15.258358974358973 5.243282051282051 15.188410256410256L4.965743589743591 15.134256410256409Q4.455794871794872 15.032717948717949 4.227897435897436 14.816102564102565Q4.000000000000001 14.599487179487179 4.000000000000001 14.213641025641024Q4.000000000000001 13.766871794871793 4.314769230769231 13.509641025641024Q4.629538461538462 13.252410256410256 5.182358974358975 13.252410256410256Q5.419282051282051 13.252410256410256 5.665230769230769 13.29528205128205Q5.911179487179488 13.338153846153846 6.168410256410256 13.423897435897436ZM6.615179487179487 13.313333333333333H9.46502564102564V13.696923076923076H8.269128205128204V16.682153846153845H7.811076923076923V13.696923076923076H6.615179487179487ZM9.905025641025642 13.313333333333333H12.035076923076925V13.696923076923076H10.360820512820514V14.69425641025641H11.965128205128206V15.077846153846153H10.360820512820514V16.2985641025641H12.075692307692309V16.682153846153845H9.905025641025642ZM13.280615384615384 13.687897435897435V14.953743589743588H13.85374358974359Q14.171897435897435 14.953743589743588 14.345641025641026 14.78902564102564Q14.519384615384615 14.624307692307692 14.519384615384615 14.319692307692307Q14.519384615384615 14.017333333333333 14.345641025641026 13.852615384615383Q14.171897435897435 13.687897435897435 13.85374358974359 13.687897435897435ZM12.824820512820512 13.313333333333333H13.85374358974359Q14.420102564102564 13.313333333333333 14.710051282051282 13.569435897435897Q15.0 13.82553846153846 15.0 14.319692307692307Q15.0 14.818358974358974 14.710051282051282 15.073333333333332Q14.420102564102564 15.32830769230769 13.85374358974359 15.32830769230769H13.280615384615384V16.682153846153845H12.824820512820512Z" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 14C7 10 11 18 15 14L21 9C17 13 13 5 9 9Z"/><path d="M6 11.5 12 6.5" stroke-opacity="0.55"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 14C7 10 11 18 15 14L21 9C17 13 13 5 9 9Z"/><path d="M6 11.5 12 6.5" stroke-opacity="0.55"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M7.081932245922209 8.669636135508153V9.608908406524465Q6.5336260978670015 9.346675031367628 6.047302383939774 9.217942283563362Q5.560978670012547 9.089209535759096 5.108030112923463 9.089209535759096Q4.321329987452948 9.089209535759096 3.894604767879548 9.39435382685069Q3.467879548306148 9.699498117942282 3.467879548306148 10.262107904642408Q3.467879548306148 10.734127979924716 3.751568381430364 10.974905897114176Q4.03525721455458 11.215683814303638 4.8267252195734 11.363488080301128L5.40840652446675 11.482685069008781Q6.48594730238394 11.687703889585945 6.9984943538268505 12.205018820577163Q7.511041405269761 12.72233375156838 7.511041405269761 13.5900878293601Q7.511041405269761 14.624717691342532 6.817314930991217 15.158720200752821Q6.123588456712673 15.69272271016311 4.7838143036386445 15.69272271016311Q4.278419071518194 15.69272271016311 3.708657465495609 15.578293601003763Q3.138895859473024 15.463864491844415 2.528607277289837 15.239774153074025V14.248055207026347Q3.115056461731493 14.577038895859472 3.6776662484316187 14.743914680050187Q4.240276035131744 14.910790464240902 4.7838143036386445 14.910790464240902Q5.608657465495608 14.910790464240902 6.056838143036387 14.586574654956085Q6.505018820577165 14.262358845671265 6.505018820577165 13.66160602258469Q6.505018820577165 13.137139272271014 6.183186951066499 12.841530740276033Q5.861355081555834 12.545922208281052 5.1271016311166875 12.398117942283562L4.540652446675031 12.283688833124215Q3.4631116687578416 12.069134253450438 2.981555834378921 11.611417816813049Q2.5 11.153701380175658 2.5 10.338393977415306Q2.5 9.39435382685069 3.165119196988708 8.850815558343788Q3.830238393977415 8.307277289836886 4.998368883312422 8.307277289836886Q5.498996235884567 8.307277289836886 6.018695106649937 8.397867001254703Q6.538393977415307 8.48845671267252 7.081932245922209 8.669636135508153ZM10.848557089084066 15.554454203262232 8.13086574654956 8.436010037641154H9.136888331242158L11.392095357590966 14.42923462986198L13.652070263488081 8.436010037641154H14.653324968632372L11.940401505646173 15.554454203262232ZM20.546424090338768 14.538895859473023V12.626976160602258H18.97302383939774V11.835508155583437H21.499999999999996V14.891718946047677Q20.942158092848178 15.287452948557087 20.26988707653701 15.4900878293601Q19.597616060225846 15.69272271016311 18.83475533249686 15.69272271016311Q17.16599749058971 15.69272271016311 16.224341279799248 14.717691342534504Q15.282685069008783 13.742659974905896 15.282685069008783 12.002383939774152Q15.282685069008783 10.257340025094102 16.224341279799248 9.282308657465494Q17.16599749058971 8.307277289836886 18.83475533249686 8.307277289836886Q19.53086574654956 8.307277289836886 20.15784190715182 8.478920953575908Q20.784818067754077 8.650564617314929 21.31405269761606 8.984316185696361V10.009410288582181Q20.78005018820577 9.556461731493098 20.179297365119197 9.327603513174402Q19.57854454203262 9.098745294855707 18.915809284818067 9.098745294855707Q17.609410288582183 9.098745294855707 16.953826850690085 9.828230865746548Q16.29824341279799 10.557716436637389 16.29824341279799 12.002383939774152Q16.29824341279799 13.442283563362608 16.953826850690085 14.171769134253449Q17.609410288582183 14.90125470514429 18.915809284818067 14.90125470514429Q19.425972396486824 14.90125470514429 19.826474278544538 14.813048933500625Q20.226976160602256 14.724843161856962 20.546424090338768 14.538895859473023Z" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M7.081932245922209 8.669636135508153V9.608908406524465Q6.5336260978670015 9.346675031367628 6.047302383939774 9.217942283563362Q5.560978670012547 9.089209535759096 5.108030112923463 9.089209535759096Q4.321329987452948 9.089209535759096 3.894604767879548 9.39435382685069Q3.467879548306148 9.699498117942282 3.467879548306148 10.262107904642408Q3.467879548306148 10.734127979924716 3.751568381430364 10.974905897114176Q4.03525721455458 11.215683814303638 4.8267252195734 11.363488080301128L5.40840652446675 11.482685069008781Q6.48594730238394 11.687703889585945 6.9984943538268505 12.205018820577163Q7.511041405269761 12.72233375156838 7.511041405269761 13.5900878293601Q7.511041405269761 14.624717691342532 6.817314930991217 15.158720200752821Q6.123588456712673 15.69272271016311 4.7838143036386445 15.69272271016311Q4.278419071518194 15.69272271016311 3.708657465495609 15.578293601003763Q3.138895859473024 15.463864491844415 2.528607277289837 15.239774153074025V14.248055207026347Q3.115056461731493 14.577038895859472 3.6776662484316187 14.743914680050187Q4.240276035131744 14.910790464240902 4.7838143036386445 14.910790464240902Q5.608657465495608 14.910790464240902 6.056838143036387 14.586574654956085Q6.505018820577165 14.262358845671265 6.505018820577165 13.66160602258469Q6.505018820577165 13.137139272271014 6.183186951066499 12.841530740276033Q5.861355081555834 12.545922208281052 5.1271016311166875 12.398117942283562L4.540652446675031 12.283688833124215Q3.4631116687578416 12.069134253450438 2.981555834378921 11.611417816813049Q2.5 11.153701380175658 2.5 10.338393977415306Q2.5 9.39435382685069 3.165119196988708 8.850815558343788Q3.830238393977415 8.307277289836886 4.998368883312422 8.307277289836886Q5.498996235884567 8.307277289836886 6.018695106649937 8.397867001254703Q6.538393977415307 8.48845671267252 7.081932245922209 8.669636135508153ZM10.848557089084066 15.554454203262232 8.13086574654956 8.436010037641154H9.136888331242158L11.392095357590966 14.42923462986198L13.652070263488081 8.436010037641154H14.653324968632372L11.940401505646173 15.554454203262232ZM20.546424090338768 14.538895859473023V12.626976160602258H18.97302383939774V11.835508155583437H21.499999999999996V14.891718946047677Q20.942158092848178 15.287452948557087 20.26988707653701 15.4900878293601Q19.597616060225846 15.69272271016311 18.83475533249686 15.69272271016311Q17.16599749058971 15.69272271016311 16.224341279799248 14.717691342534504Q15.282685069008783 13.742659974905896 15.282685069008783 12.002383939774152Q15.282685069008783 10.257340025094102 16.224341279799248 9.282308657465494Q17.16599749058971 8.307277289836886 18.83475533249686 8.307277289836886Q19.53086574654956 8.307277289836886 20.15784190715182 8.478920953575908Q20.784818067754077 8.650564617314929 21.31405269761606 8.984316185696361V10.009410288582181Q20.78005018820577 9.556461731493098 20.179297365119197 9.327603513174402Q19.57854454203262 9.098745294855707 18.915809284818067 9.098745294855707Q17.609410288582183 9.098745294855707 16.953826850690085 9.828230865746548Q16.29824341279799 10.557716436637389 16.29824341279799 12.002383939774152Q16.29824341279799 13.442283563362608 16.953826850690085 14.171769134253449Q17.609410288582183 14.90125470514429 18.915809284818067 14.90125470514429Q19.425972396486824 14.90125470514429 19.826474278544538 14.813048933500625Q20.226976160602256 14.724843161856962 20.546424090338768 14.538895859473023Z" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="17" r="2.4"/><path d="M6 17 C 7 9, 15 15, 18 6"/><path d="M15.2 6.6 18 6 17.7 8.9"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="17" r="2.4"/><path d="M6 17 C 7 9, 15 15, 18 6"/><path d="M15.2 6.6 18 6 17.7 8.9"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18h8"/><path d="M11 18 A7 7 0 0 1 18 11"/><circle cx="3" cy="18" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="11" cy="18" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18h8"/><path d="M11 18 A7 7 0 0 1 18 11"/><circle cx="3" cy="18" r="1.5" fill="#949494" stroke="none"/><circle cx="11" cy="18" r="1.5" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 363 B After Width: | Height: | Size: 363 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 20 12 4 18 20M8.5 14h7"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 20 12 4 18 20M8.5 14h7"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 223 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10C7 6 11 14 15 10L21 5"/><path d="M3 16C7 12 11 20 15 16L21 11"/><path d="M3 10v6M21 5v6"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10C7 6 11 14 15 10L21 5"/><path d="M3 16C7 12 11 20 15 16L21 11"/><path d="M3 10v6M21 5v6"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 290 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3h8v13l-4 5-4-5Z"/><path d="M8 7l8-2M8 11l8-2M8 15l8-2"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3h8v13l-4 5-4-5Z"/><path d="M8 7l8-2M8 11l8-2M8 15l8-2"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6.5" r="2.6"/><circle cx="6" cy="17.5" r="2.6"/><path d="M8.3 8 20.5 17M8.3 16 20.5 7"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6.5" r="2.6"/><circle cx="6" cy="17.5" r="2.6"/><path d="M8.3 8 20.5 17M8.3 16 20.5 7"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 293 B |
@@ -12,6 +12,9 @@
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#ifdef SLIC3R_CAD
|
||||
#include "CAD/DesignPanel.hpp"
|
||||
#endif
|
||||
#include "WebViewDialog.hpp"
|
||||
#include "PartPlate.hpp"
|
||||
|
||||
@@ -424,6 +427,9 @@ void BBLTopbar::OnSaveProject(wxAuiToolBarEvent& event)
|
||||
void BBLTopbar::OnUndo(wxAuiToolBarEvent& event)
|
||||
{
|
||||
MainFrame* main_frame = dynamic_cast<MainFrame*>(m_frame);
|
||||
#ifdef SLIC3R_CAD
|
||||
if (DesignPanel* design = main_frame->shown_design_panel()) { design->menu_undo_redo(false); return; }
|
||||
#endif
|
||||
Plater* plater = main_frame->plater();
|
||||
plater->undo();
|
||||
}
|
||||
@@ -431,6 +437,9 @@ void BBLTopbar::OnUndo(wxAuiToolBarEvent& event)
|
||||
void BBLTopbar::OnRedo(wxAuiToolBarEvent& event)
|
||||
{
|
||||
MainFrame* main_frame = dynamic_cast<MainFrame*>(m_frame);
|
||||
#ifdef SLIC3R_CAD
|
||||
if (DesignPanel* design = main_frame->shown_design_panel()) { design->menu_undo_redo(true); return; }
|
||||
#endif
|
||||
Plater* plater = main_frame->plater();
|
||||
plater->redo();
|
||||
}
|
||||
@@ -443,6 +452,13 @@ void BBLTopbar::EnableUndoRedoItems()
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void BBLTopbar::EnableUndoRedo(bool undo, bool redo)
|
||||
{
|
||||
this->EnableTool(m_undo_item->GetId(), undo);
|
||||
this->EnableTool(m_redo_item->GetId(), redo);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void BBLTopbar::DisableUndoRedoItems()
|
||||
{
|
||||
this->EnableTool(m_undo_item->GetId(), false);
|
||||
|
||||
@@ -69,6 +69,7 @@ public:
|
||||
|
||||
void EnableUndoRedoItems();
|
||||
void DisableUndoRedoItems();
|
||||
void EnableUndoRedo(bool undo, bool redo);
|
||||
|
||||
void SaveNormalRect();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "slic3r/GUI/Camera.hpp" // N: look down the sketch plane normal
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/ImGuiWrapper.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "slic3r/GUI/3DScene.hpp"
|
||||
@@ -48,12 +49,6 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
|
||||
m_canvas->set_type(GLCanvas3D::ECanvasType::CanvasView3D);
|
||||
m_canvas->set_studio_lighting(true); // see GLCanvas3D::m_studio_lighting and phong.fs
|
||||
|
||||
// CAD navigation, this canvas only: left-drag sweeps a selection rubber band, so orbit
|
||||
// moves to middle-drag and pan to right-drag. Design is a different modality from
|
||||
// Prepare/Preview and every CAD the user already knows maps the mouse this way; the other
|
||||
// tabs are untouched.
|
||||
m_canvas->set_cad_navigation(true);
|
||||
|
||||
m_canvas->enable_picking(false); // viewport face/edge picking is custom (TODO)
|
||||
m_canvas->enable_moving(false);
|
||||
m_canvas->enable_gizmos(false);
|
||||
@@ -155,76 +150,11 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
|
||||
if (m_inline_editor) m_inline_editor->commit();
|
||||
};
|
||||
|
||||
// Bottom-right viewport HUD: a borderless, non-focusable float label showing the active
|
||||
// tool's current values. Top-level (a child widget is hidden by the GL surface, same as
|
||||
// the inline editor). Fed every frame by the tool's on_readout; empty text hides it.
|
||||
// NON-FOCUSABLE IS THE LOAD-BEARING WORD, and a wxFrame is not: see the header. The chip
|
||||
// outlives the gesture that drew it, and while it held the X input focus every sketch
|
||||
// shortcut was swallowed until the user clicked the canvas. Same window class as the status
|
||||
// chip below for the same reason. Do not "simplify" it back to a wxFrame.
|
||||
{
|
||||
wxWindow* top = wxGetTopLevelParent(m_canvas_widget);
|
||||
m_hud = new wxPopupWindow(top, wxBORDER_NONE);
|
||||
m_hud->SetBackgroundColour(wxColour(28, 30, 34));
|
||||
m_hud_label = new wxStaticText(m_hud, wxID_ANY, wxEmptyString);
|
||||
m_hud_label->SetForegroundColour(wxColour(0x46, 0xE0, 0xC8)); // teal, reads on dark bed
|
||||
wxFont f = m_hud_label->GetFont(); f.MakeBold(); m_hud_label->SetFont(f);
|
||||
auto* hs = new wxBoxSizer(wxHORIZONTAL);
|
||||
hs->Add(m_hud_label, 0, wxALL, 6);
|
||||
m_hud->SetSizerAndFit(hs);
|
||||
m_hud->Hide();
|
||||
}
|
||||
m_sketch_tool.on_readout = [this](const std::string& s) { set_readout(s); };
|
||||
|
||||
// Bottom-LEFT twin, carrying the status line. Top-level for the same reason as the readout
|
||||
// (a child widget is hidden by the GL surface) but a wxPopupWindow rather than a wxFrame,
|
||||
// because a popup cannot take keyboard focus. The readout gets away with a frame only
|
||||
// because it appears mid-gesture and the next input is the mouse; this one is up
|
||||
// permanently and is re-raised on every status change. As a frame it took the WM's focus
|
||||
// each time and the canvas stopped receiving keys at all — every sketch shortcut silently
|
||||
// dead, which reads as a broken tool. Do not "simplify" it back to a wxFrame.
|
||||
// Its colour is set per message — the panel decides whether a line is neutral or an error.
|
||||
{
|
||||
wxWindow* top = wxGetTopLevelParent(m_canvas_widget);
|
||||
m_status_hud = new wxPopupWindow(top, wxBORDER_NONE);
|
||||
m_status_hud->SetBackgroundColour(wxColour(28, 30, 34));
|
||||
m_status_hud_label = new wxStaticText(m_status_hud, wxID_ANY, wxEmptyString);
|
||||
auto* ss = new wxBoxSizer(wxHORIZONTAL);
|
||||
// The line never wraps — there is a whole window's width down here — so the chip is
|
||||
// ONE LINE tall. Spacers rather than a wxALL border because the two axes want
|
||||
// different numbers: roomy at the sides so it reads as a label, and just enough top
|
||||
// and bottom to clear the descenders. Zero vertical clips the glyphs; 6 (what the
|
||||
// readout chip uses) makes it look like a two-line box.
|
||||
ss->AddSpacer(10);
|
||||
ss->Add(m_status_hud_label, 0, wxTOP | wxBOTTOM, 3);
|
||||
ss->AddSpacer(10);
|
||||
m_status_hud->SetSizerAndFit(ss);
|
||||
m_status_hud->Hide();
|
||||
}
|
||||
// A floating frame does not follow its parent, so the anchor has to be recomputed whenever
|
||||
// the canvas changes size (that bind is below bind_event_handlers(), for the reason given
|
||||
// there). The readout HUD gets away without this because it is transient; the status line is
|
||||
// on screen almost permanently and would visibly detach.
|
||||
// ...and it does not follow the WINDOW either. A popup is override-redirect: the window
|
||||
// manager does not own it, so minimising the app leaves the chip sitting on the bare desktop
|
||||
// (seen on the rig: whole screen black, chip still there), and it stacks above other
|
||||
// applications rather than behind them. IsShownOnScreen does not catch this — an iconised
|
||||
// frame still counts as shown — so the frame has to say so itself. Deactivating the app is
|
||||
// the same case one step weaker: the chip belongs to a viewport the user is no longer
|
||||
// looking at. Showing it back is safe because a popup cannot take focus, so neither event
|
||||
// can be re-triggered by our own Show().
|
||||
// Members rather than lambdas so unbind_canvas_event_handlers() can Unbind them: these sit on
|
||||
// a frame that OUTLIVES this canvas, and a lambda cannot be unbound.
|
||||
if (wxWindow* top = wxGetTopLevelParent(m_canvas_widget)) {
|
||||
top->Bind(wxEVT_ICONIZE, &DesignCanvas::on_frame_iconize, this);
|
||||
top->Bind(wxEVT_ACTIVATE, &DesignCanvas::on_frame_activate, this);
|
||||
// The anchor is an ABSOLUTE SCREEN position (ClientToScreen below), so moving the window
|
||||
// moves the canvas out from under a chip that stays where it was. Dragging the frame by
|
||||
// its title bar left the chip stranded mid-viewport until the next size, status or tab
|
||||
// change happened to re-place it. Nothing on the canvas fires for a move that does not
|
||||
// also resize, so it has to come from the frame.
|
||||
top->Bind(wxEVT_MOVE, &DesignCanvas::on_status_hud_reanchor, this);
|
||||
}
|
||||
// The two viewport chips — the active tool's values bottom-right, the status line bottom-left
|
||||
// — are drawn by the canvas itself, in the tool's ImGui pass: they go away with the canvas,
|
||||
// the tab and the window, and never take the keyboard.
|
||||
m_sketch_tool.on_readout = [this](const std::string& s) { set_readout(s); };
|
||||
m_sketch_tool.render_overlays = [this] { render_hud(); };
|
||||
|
||||
refresh_bed();
|
||||
|
||||
@@ -236,16 +166,10 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
|
||||
// reverse order of binding, and GLCanvas3D swallows several events without skipping them —
|
||||
// wxEVT_RIGHT_UP and wxEVT_ENTER_WINDOW in on_mouse, and wxEVT_SIZE in on_size, which is
|
||||
// just `m_dirty = true;`. For those, whatever is bound LAST is the only handler that runs.
|
||||
// The context menu, the focus-follows-mouse and the status-chip re-anchor all depend on
|
||||
// running first, which is only true while this call stays ahead of them.
|
||||
// The context menu and the focus-follows-mouse depend on running first, which is only true
|
||||
// while this call stays ahead of them.
|
||||
m_canvas->bind_event_handlers();
|
||||
|
||||
// The status-chip re-anchor promised above, bound AFTER the call so it runs first — ahead of
|
||||
// it the handler never ran at all, leaving a stale anchor and wrap width after any resize
|
||||
// that did not also move the frame or change the text. Its e.Skip() is load-bearing the
|
||||
// other way: it falls through to on_size, which is what still marks the canvas dirty.
|
||||
m_canvas_widget->Bind(wxEVT_SIZE, &DesignCanvas::on_status_hud_reanchor, this);
|
||||
|
||||
// The Design GL canvas only receives key events (Esc to exit/enter Select, Ctrl+Z undo)
|
||||
// while it holds keyboard focus. Clicking a side-panel button steals focus, after which
|
||||
// Esc/Ctrl+Z silently do nothing until the viewport is clicked again. Restore focus
|
||||
@@ -268,18 +192,6 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
|
||||
// Both are idempotent, and neither destroys anything: the destructor still owns that.
|
||||
void DesignCanvas::unbind_canvas_event_handlers()
|
||||
{
|
||||
if (wxWindow* top = wxGetTopLevelParent(m_canvas_widget)) {
|
||||
top->Unbind(wxEVT_ICONIZE, &DesignCanvas::on_frame_iconize, this);
|
||||
top->Unbind(wxEVT_ACTIVATE, &DesignCanvas::on_frame_activate, this);
|
||||
top->Unbind(wxEVT_MOVE, &DesignCanvas::on_status_hud_reanchor, this);
|
||||
}
|
||||
// Before the popups go down, or a resize still in flight re-places and re-shows the chip.
|
||||
if (m_canvas_widget)
|
||||
m_canvas_widget->Unbind(wxEVT_SIZE, &DesignCanvas::on_status_hud_reanchor, this);
|
||||
// A popup is override-redirect: it does not go down with the frame, so one left showing sits
|
||||
// on the bare desktop for however long the teardown takes.
|
||||
show_status_hud(false);
|
||||
if (m_hud) m_hud->Hide();
|
||||
if (m_canvas) m_canvas->unbind_event_handlers();
|
||||
}
|
||||
|
||||
@@ -290,7 +202,6 @@ void DesignCanvas::reset_canvas_volumes()
|
||||
|
||||
DesignCanvas::~DesignCanvas()
|
||||
{
|
||||
if (m_hud) m_hud->Destroy();
|
||||
delete m_canvas;
|
||||
delete m_canvas_widget;
|
||||
}
|
||||
@@ -346,14 +257,22 @@ void DesignCanvas::request_repaint()
|
||||
}
|
||||
}
|
||||
|
||||
// ImGui's display size is shared by every canvas and only refreshed when a canvas sees its own
|
||||
// size change, so the canvas taking over must re-announce its size (Plater does the same between
|
||||
// Prepare and Preview). Otherwise the overlays anchored to it, the FPS counter first, are laid
|
||||
// out for the other canvas.
|
||||
void DesignCanvas::enter_viewport()
|
||||
{
|
||||
if (!m_camera_swapped) swap_camera();
|
||||
if (m_canvas) { m_canvas->reset_old_size(); m_canvas->set_as_dirty(); }
|
||||
}
|
||||
|
||||
void DesignCanvas::leave_viewport()
|
||||
{
|
||||
if (m_camera_swapped) swap_camera();
|
||||
if (Plater* plater = wxGetApp().plater())
|
||||
if (GLCanvas3D* editor = plater->get_current_canvas3D())
|
||||
editor->reset_old_size();
|
||||
}
|
||||
|
||||
void DesignCanvas::swap_camera()
|
||||
@@ -1188,124 +1107,59 @@ double DesignCanvas::model_mid_z() const
|
||||
|
||||
void DesignCanvas::set_readout(const std::string& text)
|
||||
{
|
||||
if (!m_hud || !m_hud_label || !m_canvas_widget) return;
|
||||
if (text == m_hud_last) return; // only touch the WM on a real change
|
||||
if (text == m_hud_last) return;
|
||||
m_hud_last = text;
|
||||
if (text.empty()) { m_hud->Hide(); return; }
|
||||
m_hud_label->SetLabel(wxString::FromUTF8(text));
|
||||
place_readout_hud();
|
||||
}
|
||||
|
||||
void DesignCanvas::place_readout_hud()
|
||||
{
|
||||
if (!m_hud || !m_hud_label || !m_canvas_widget) return;
|
||||
if (m_hud_last.empty() || !m_canvas_widget->IsShownOnScreen()) { m_hud->Hide(); return; }
|
||||
m_hud->Fit();
|
||||
// Anchor to the canvas's bottom-right corner with a small margin (screen coords).
|
||||
const wxSize cs = m_canvas_widget->GetClientSize();
|
||||
const wxSize hs = m_hud->GetSize();
|
||||
const wxPoint br = m_canvas_widget->ClientToScreen(
|
||||
wxPoint(cs.GetWidth() - hs.GetWidth() - 12, cs.GetHeight() - hs.GetHeight() - 12));
|
||||
if (!m_hud->IsShown()) m_hud->Show(); // Show before Move (GTK ignores pre-map Move)
|
||||
m_hud->Move(br);
|
||||
m_hud->Raise();
|
||||
}
|
||||
|
||||
// A popup is override-redirect: the window manager does not own it, so an iconised or
|
||||
// deactivated app would leave the chip sitting on the bare desktop. The status chip already
|
||||
// had to answer this; now that the readout is a popup too, it answers it the same way.
|
||||
void DesignCanvas::show_readout_hud(bool on)
|
||||
{
|
||||
if (!m_hud) return;
|
||||
if (on) place_readout_hud();
|
||||
else m_hud->Hide();
|
||||
if (m_canvas) m_canvas->set_as_dirty(); // drawn by the next frame (the tool feeds this from one)
|
||||
}
|
||||
|
||||
// Clear of the view cube and the two round view buttons, which own the bottom-left corner.
|
||||
// Shared by the placement and by the wrap width, which have to agree or the chip wraps to a
|
||||
// width it is then not given.
|
||||
static constexpr int kStatusHudLeftInsetDip = 190;
|
||||
static constexpr float kStatusHudLeftInset = 190.f;
|
||||
|
||||
void DesignCanvas::set_status_text(const wxString& text, const wxColour& colour)
|
||||
{
|
||||
if (!m_status_hud || !m_status_hud_label || !m_canvas_widget) return;
|
||||
if (text == m_status_hud_last && colour == m_status_hud_colour) return;
|
||||
m_status_hud_last = text;
|
||||
m_status_hud_colour = colour;
|
||||
if (text.IsEmpty()) { m_status_hud->Hide(); return; }
|
||||
m_status_hud_label->SetForegroundColour(colour);
|
||||
apply_status_label();
|
||||
place_status_hud();
|
||||
request_repaint();
|
||||
}
|
||||
|
||||
// SetLabel + Wrap + Fit, in that order and always together. Moving the status out of the panel
|
||||
// removed the clipping of 8cc but not the underlying problem: the chip is a top-level
|
||||
// popup that Fit()s to its text, so a long sentence simply grew past the right edge of the canvas
|
||||
// and hung over the window. Wrapping to the room actually available is what makes the earlier
|
||||
// promise — "a sentence can be a sentence" — true at every window width, including the charter's
|
||||
// 1366 reach. Wrap() rewrites the label it is given, so it must follow a fresh SetLabel every
|
||||
// time; that is the whole reason this is one function instead of three call sites.
|
||||
void DesignCanvas::apply_status_label()
|
||||
void DesignCanvas::render_hud()
|
||||
{
|
||||
if (!m_status_hud || !m_status_hud_label || !m_canvas_widget) return;
|
||||
m_status_hud_label->SetLabel(m_status_hud_last);
|
||||
const int avail = m_canvas_widget->GetClientSize().GetWidth()
|
||||
- m_canvas_widget->FromDIP(kStatusHudLeftInsetDip)
|
||||
- m_canvas_widget->FromDIP(24);
|
||||
if (avail > m_canvas_widget->FromDIP(120)) // a uselessly narrow canvas: leave it unwrapped
|
||||
m_status_hud_label->Wrap(avail);
|
||||
m_status_hud->Fit();
|
||||
}
|
||||
|
||||
void DesignCanvas::place_status_hud()
|
||||
{
|
||||
if (!m_status_hud || !m_canvas_widget || m_status_hud_last.IsEmpty()) return;
|
||||
// The canvas has a client size even while its page is hidden, and it is not the size the
|
||||
// page will have when shown — anchoring against it put the chip up on the tab bar, where it
|
||||
// then stayed until the next status change moved it. Nothing to anchor to: stay down.
|
||||
if (!m_canvas_widget->IsShownOnScreen()) { m_status_hud->Hide(); return; }
|
||||
const wxSize cs = m_canvas_widget->GetClientSize();
|
||||
// Re-wrap first: this also runs on resize, and a chip wrapped for the old width either
|
||||
// overhangs a narrowed canvas or wastes a widened one.
|
||||
apply_status_label();
|
||||
const wxSize hs = m_status_hud->GetSize();
|
||||
const int kLeftInset = m_canvas_widget->FromDIP(kStatusHudLeftInsetDip);
|
||||
const wxPoint bl = m_canvas_widget->ClientToScreen(
|
||||
wxPoint(kLeftInset, cs.GetHeight() - hs.GetHeight() - 12));
|
||||
// No Raise() and no focus juggling: a popup neither takes focus nor falls behind. This was
|
||||
// caught with ORCA_CAD_KEYTRACE — shift+S logged a line, the following R logged nothing, and
|
||||
// the only thing between them was the first status update showing this window.
|
||||
if (!m_status_hud->IsShown()) m_status_hud->Show(); // Show before Move (GTK ignores pre-map Move)
|
||||
m_status_hud->Move(bl);
|
||||
}
|
||||
|
||||
void DesignCanvas::on_frame_iconize(wxIconizeEvent& e)
|
||||
{
|
||||
show_status_hud(!e.IsIconized());
|
||||
show_readout_hud(!e.IsIconized());
|
||||
e.Skip();
|
||||
}
|
||||
|
||||
void DesignCanvas::on_frame_activate(wxActivateEvent& e)
|
||||
{
|
||||
show_status_hud(e.GetActive());
|
||||
show_readout_hud(e.GetActive());
|
||||
e.Skip();
|
||||
}
|
||||
|
||||
// wxEvent& so one handler serves both events that invalidate the anchor: the frame moving out
|
||||
// from under the chip, and the canvas resizing under it.
|
||||
void DesignCanvas::on_status_hud_reanchor(wxEvent& e)
|
||||
{
|
||||
place_status_hud();
|
||||
e.Skip();
|
||||
}
|
||||
|
||||
void DesignCanvas::show_status_hud(bool on)
|
||||
{
|
||||
if (!m_status_hud) return;
|
||||
if (on) place_status_hud(); // re-anchors first: the page may have been resized while away
|
||||
else m_status_hud->Hide();
|
||||
if (m_hud_last.empty() && m_status_hud_last.IsEmpty()) return;
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
const ImVec2 ds = ImGui::GetIO().DisplaySize;
|
||||
const float em = imgui.get_style_scaling(); // follows the font, so the DPI
|
||||
const float margin = 12.f * em;
|
||||
const int flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize |
|
||||
ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
|
||||
auto chip = [&](const char* id, const std::string& text, float x, float pivot_x, float wrap,
|
||||
const ImVec4* colour) {
|
||||
ImGuiWrapper::push_common_window_style(m_canvas->get_scale());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f * em, 4.f * em));
|
||||
imgui.set_next_window_pos(x, ds.y - margin, ImGuiCond_Always, pivot_x, 1.f);
|
||||
imgui.begin(std::string(id), flags);
|
||||
if (wrap > 0.f) ImGui::PushTextWrapPos(wrap);
|
||||
if (colour) ImGui::PushStyleColor(ImGuiCol_Text, *colour);
|
||||
ImGui::TextUnformatted(text.c_str());
|
||||
if (colour) ImGui::PopStyleColor();
|
||||
if (wrap > 0.f) ImGui::PopTextWrapPos();
|
||||
imgui.end();
|
||||
ImGui::PopStyleVar();
|
||||
ImGuiWrapper::pop_common_window_style();
|
||||
};
|
||||
if (!m_status_hud_last.IsEmpty()) {
|
||||
// A sentence can be a sentence: it wraps to the room left of the readout chip.
|
||||
const float left = kStatusHudLeftInset * em;
|
||||
const ImVec4 col = m_status_hud_colour.IsOk()
|
||||
? ImVec4(m_status_hud_colour.Red() / 255.f, m_status_hud_colour.Green() / 255.f,
|
||||
m_status_hud_colour.Blue() / 255.f, 1.f)
|
||||
: ImVec4();
|
||||
chip("##design_status", m_status_hud_last.ToUTF8().data(), left, 0.f,
|
||||
std::max(ds.x * 0.6f - left, 120.f * em), m_status_hud_colour.IsOk() ? &col : nullptr);
|
||||
}
|
||||
if (!m_hud_last.empty())
|
||||
chip("##design_readout", m_hud_last, ds.x - margin, 1.f, 0.f, &ImGuiWrapper::COL_ORCA);
|
||||
}
|
||||
|
||||
void DesignCanvas::set_body_highlight(bool on)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define slic3r_DesignCanvas_hpp_
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <wx/popupwin.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -227,10 +226,6 @@ public:
|
||||
// the panel clips it at ~73 characters with no warning (8cc), the viewport's
|
||||
// bottom margin has the whole window width to spare. Empty text hides it.
|
||||
void set_status_text(const wxString& text, const wxColour& colour);
|
||||
// Take the status line down / bring it back when the Design page leaves and re-enters view.
|
||||
// A popup is a TOP-LEVEL window: hiding the page it belongs to does not hide it. Keeps the
|
||||
// text, so coming back needs no re-selection.
|
||||
void show_status_hud(bool on);
|
||||
void set_operand_bodies(int target_body, int tool_body); // -1,-1 clears
|
||||
void set_body_translucent(bool on); // render the solid see-through (fillet/chamfer preview)
|
||||
void set_xray_focus(int body); // >=0: fade+lock out every other body (CoordSys picking)
|
||||
@@ -398,36 +393,15 @@ private:
|
||||
bool m_section_on{false};
|
||||
|
||||
std::unique_ptr<SketchInlineEditor> m_inline_editor; // floating in-canvas value editor
|
||||
// Bottom-right viewport HUD: a borderless float label over the GL canvas showing the
|
||||
// active tool's current values (fed by the tool's on_readout). Empty text hides it.
|
||||
// A wxPopupWindow for the SAME reason as the status chip below, and it was a wxFrame until
|
||||
// the reason was measured rather than assumed: "it appears mid-gesture and the next input is
|
||||
// the mouse" is false. The chip keeps the last value on screen AFTER the gesture ends, and a
|
||||
// frame holds the X input focus once it has it — so the next keystroke went to a 119x31
|
||||
// window that has no use for it. Measured on :10: focus on the chip, `r` produced no
|
||||
// CHAR_HOOK line at all; one bare canvas click moved focus back and the same key armed the
|
||||
// tool. That is every sketch shortcut dead after every dimensioned entity.
|
||||
wxPopupWindow* m_hud{nullptr};
|
||||
wxStaticText* m_hud_label{nullptr};
|
||||
// The viewport chips, drawn in the tool's ImGui pass (render_hud): bottom-right the active
|
||||
// tool's current values (fed by the tool's on_readout), bottom-left the status line written by
|
||||
// DesignPanel. Empty text draws nothing. They were top-level popups once; a popup does not
|
||||
// follow its frame, so it floated over other applications and outlived the tab.
|
||||
std::string m_hud_last;
|
||||
void set_readout(const std::string& text);
|
||||
void place_readout_hud(); // anchor + show, using m_hud_last
|
||||
void show_readout_hud(bool on); // iconise/deactivate: a popup would float on the desktop
|
||||
|
||||
// Bottom-LEFT viewport HUD: the selection / tool status line, written by DesignPanel.
|
||||
// A wxPopupWindow, NOT the wxFrame the readout HUD uses: a frame accepts keyboard focus,
|
||||
// and this one is on screen permanently and re-raised on every status change, so it stole
|
||||
// the keyboard from the canvas and killed every sketch shortcut in the tab.
|
||||
wxPopupWindow* m_status_hud{nullptr};
|
||||
wxStaticText* m_status_hud_label{nullptr};
|
||||
wxString m_status_hud_last;
|
||||
wxColour m_status_hud_colour;
|
||||
void place_status_hud(); // re-anchors to the canvas corner (also on resize)
|
||||
void apply_status_label(); // SetLabel + Wrap to the canvas width + Fit, always together
|
||||
// On the top-level frame, which outlives this canvas — members so they can be unbound.
|
||||
void on_frame_iconize(wxIconizeEvent& e);
|
||||
void on_frame_activate(wxActivateEvent& e);
|
||||
void on_status_hud_reanchor(wxEvent& e); // frame wxEVT_MOVE and canvas wxEVT_SIZE
|
||||
wxColour m_status_hud_colour; // wxNullColour: the overlay's own text colour
|
||||
void set_readout(const std::string& text);
|
||||
void render_hud();
|
||||
std::function<void(const SketchProfile&, const SketchPlane&)> m_on_sketch_commit;
|
||||
std::function<void(const std::vector<SketchEntity>&,
|
||||
const std::vector<SketchEntityConstraintDef>&,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/log/trivial.hpp> // the offer/atlas join check reports on the log
|
||||
#include <wx/stopwatch.h>
|
||||
#include <Standard_Failure.hxx>
|
||||
|
||||
#include <cassert>
|
||||
@@ -37,7 +38,6 @@
|
||||
#include <wx/menu.h>
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/utils.h> // wxWindowDisabler, wxMilliSleep
|
||||
#include <wx/msgdlg.h> // wxMessageBox
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@@ -53,6 +53,9 @@
|
||||
#include "slic3r/GUI/Widgets/Button.hpp" // Orca-styled Button (ButtonStyle/ButtonType) — same look as Prepare
|
||||
#include "slic3r/GUI/Widgets/CheckBox.hpp" // Orca teal check (label lives in the row's left column)
|
||||
#include "slic3r/GUI/Widgets/ComboBox.hpp" // Orca dropdown — replaces wxChoice in every Design card
|
||||
#include "slic3r/GUI/Widgets/TextInput.hpp"
|
||||
#include "slic3r/GUI/Widgets/DialogButtons.hpp"
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "slic3r/GUI/Widgets/StaticBox.hpp" // Prepare's rounded white card frame around each tool dialog
|
||||
#include "libslic3r/CAD/SketchImport.hpp" // text_to_regions / svg_to_regions
|
||||
#include "libslic3r/CAD/ThreadStandards.hpp" // ISO metric / Unified imperial thread tables
|
||||
@@ -149,21 +152,42 @@ static bool en_parse(const wxString& text, double& out)
|
||||
return t.ToCDouble(&out);
|
||||
}
|
||||
|
||||
// Design-tab chrome tokens. The dark branch returns the EXACT legacy values so the
|
||||
// (correct) dark theme stays byte-identical; the light branch maps each onto Orca's
|
||||
// light surface so the ribbon/sidebar follow the app theme instead of staying black.
|
||||
// Design-tab chrome tokens, one {light, dark} pair each. Controls are coloured from these at
|
||||
// construction; on a theme switch on_sys_color_changed() walks the panel and moves every colour
|
||||
// that is one theme's token onto the other theme's, so the tab follows the app theme live.
|
||||
struct DpToken { wxColour light, dark; };
|
||||
enum DpTok { TokRibbonBg, TokRibbonHover, TokPanelBg, TokSecText, TokCtlText, TokItemText, TokItemDim,
|
||||
TokBorder, TokCount };
|
||||
static const DpToken kDpTokens[TokCount] = {
|
||||
{ wxColour(0xEC,0xEC,0xEE), wxColour(0x36,0x36,0x3C) }, // ribbon
|
||||
{ wxColour(0xD7,0xD7,0xDB), wxColour(0x4D,0x4D,0x54) }, // ribbon hover
|
||||
{ wxColour(0xFB,0xFB,0xFD), wxColour(0x2D,0x2D,0x30) }, // sidebar / lists
|
||||
{ wxColour(0x66,0x66,0x68), wxColour(0x81,0x81,0x83) }, // secondary text
|
||||
{ wxColour(0x35,0x35,0x37), wxColour(0xC8,0xC8,0xC8) }, // control text
|
||||
{ wxColour(0x2C,0x2C,0x2E), wxColour(0xE0,0xE0,0xE0) }, // list item text
|
||||
{ wxColour(0xA0,0xA0,0xA2), wxColour(0x80,0x80,0x80) }, // dimmed list item
|
||||
// Prepare's control-outline grey, sampled from its sidebar: #DBDBDB on light, #4A4A51 on the
|
||||
// #2D2D31 dark panel. Every framed thing in Design uses this so the tab matches.
|
||||
{ wxColour(0xDB,0xDB,0xDB), wxColour(0x4A,0x4A,0x51) },
|
||||
};
|
||||
static bool dp_dark() { return wxGetApp().dark_mode(); }
|
||||
static wxColour dp_ribbon_bg() { return dp_dark() ? wxColour(0x36,0x36,0x3C) : wxColour(0xEC,0xEC,0xEE); }
|
||||
static wxColour dp_ribbon_hover() { return dp_dark() ? wxColour(0x4D,0x4D,0x54) : wxColour(0xD7,0xD7,0xDB); }
|
||||
static wxColour dp_panel_bg() { return dp_dark() ? wxColour(0x2D,0x2D,0x30) : wxColour(0xFB,0xFB,0xFD); }
|
||||
static wxColour dp_sec_text() { return dp_dark() ? wxColour(0x81,0x81,0x83) : wxColour(0x66,0x66,0x68); }
|
||||
static wxColour dp_ctl_text() { return dp_dark() ? wxColour(0xC8,0xC8,0xC8) : wxColour(0x35,0x35,0x37); }
|
||||
static wxColour dp_item_text() { return dp_dark() ? wxColour(0xE0,0xE0,0xE0) : wxColour(0x2C,0x2C,0x2E); }
|
||||
static wxColour dp_item_dim() { return dp_dark() ? wxColour(0x80,0x80,0x80) : wxColour(0xA0,0xA0,0xA2); }
|
||||
static wxColour dp_tok(DpTok t) { return dp_dark() ? kDpTokens[t].dark : kDpTokens[t].light; }
|
||||
static wxColour dp_ribbon_bg() { return dp_tok(TokRibbonBg); }
|
||||
static wxColour dp_ribbon_hover() { return dp_tok(TokRibbonHover); }
|
||||
static wxColour dp_panel_bg() { return dp_tok(TokPanelBg); }
|
||||
static wxColour dp_sec_text() { return dp_tok(TokSecText); }
|
||||
static wxColour dp_ctl_text() { return dp_tok(TokCtlText); }
|
||||
static wxColour dp_item_text() { return dp_tok(TokItemText); }
|
||||
static wxColour dp_item_dim() { return dp_tok(TokItemDim); }
|
||||
|
||||
// Prepare's control-outline grey, sampled from its sidebar: #4A4A51 on the #2D2D31 dark
|
||||
// panel, #DBDBDB on light. Every framed thing in Design uses this so the tab matches.
|
||||
static wxColour dp_border_col() { return dp_dark() ? wxColour(0x4A,0x4A,0x51) : wxColour(0xDB,0xDB,0xDB); }
|
||||
// The other theme's value of a token colour, or `c` itself when it is no token.
|
||||
static wxColour dp_retheme(const wxColour& c, bool to_dark)
|
||||
{
|
||||
for (const DpToken& t : kDpTokens)
|
||||
if (c == (to_dark ? t.light : t.dark))
|
||||
return to_dark ? t.dark : t.light;
|
||||
return c;
|
||||
}
|
||||
|
||||
// A tool card: Prepare's rounded white-bordered panel (Plater.cpp's panel_printer_preset
|
||||
// idiom — radius 8, #EEEEEE border, green on hover). Every card's controls are parented
|
||||
@@ -172,7 +196,9 @@ static StaticBox* make_card(wxWindow* parent)
|
||||
{
|
||||
auto* c = new StaticBox(parent);
|
||||
c->SetCornerRadius(8);
|
||||
c->SetBorderColorNormal(dp_border_col()); // no hover accent: the frame is not clickable
|
||||
// The light literal in a StateColor is mapped to the dark one at paint time, so the frame
|
||||
// follows a theme switch by itself. No hover accent: the frame is not clickable.
|
||||
c->SetBorderColor(StateColor(kDpTokens[TokBorder].light));
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -186,8 +212,8 @@ static wxSpinCtrlDouble* make_spin(wxWindow* parent, double val,
|
||||
{
|
||||
auto* box = new StaticBox(parent);
|
||||
box->SetCornerRadius(4);
|
||||
box->SetBorderColorNormal(dp_border_col());
|
||||
auto* s = new wxSpinCtrlDouble(box, wxID_ANY, "", wxDefaultPosition, wxSize(90, -1),
|
||||
box->SetBorderColor(StateColor(kDpTokens[TokBorder].light));
|
||||
auto* s = new wxSpinCtrlDouble(box, wxID_ANY, "", wxDefaultPosition, parent->FromDIP(wxSize(90, -1)),
|
||||
wxSP_ARROW_KEYS | wxBORDER_NONE);
|
||||
s->SetRange(mn, mx);
|
||||
s->SetDigits(2);
|
||||
@@ -280,17 +306,23 @@ static SketchPlane face_plane_inward(const TopoDS_Face& face)
|
||||
DesignPanel::DesignPanel(wxWindow* parent)
|
||||
: wxPanel(parent, wxID_ANY)
|
||||
{
|
||||
// The tab is built on its first show; where that time goes is logged, per phase, because it
|
||||
// varies by an order of magnitude between machines.
|
||||
wxStopWatch build_clock;
|
||||
long build_mark = 0;
|
||||
auto build_phase = [&build_clock, &build_mark](const char* phase) {
|
||||
const long now = build_clock.Time();
|
||||
BOOST_LOG_TRIVIAL(info) << "Design tab build: " << phase << " " << now - build_mark << " ms";
|
||||
build_mark = now;
|
||||
};
|
||||
// Left column: a slim feature-tree + docked tool-dialog column. All form
|
||||
// controls are parented to m_form so it can scroll independently of the
|
||||
// live GL viewport. The tool buttons live in the top toolbar (built below).
|
||||
m_form = new wxScrolledWindow(this, wxID_ANY);
|
||||
// The sidebar/panel never carried an explicit background, so in light theme it
|
||||
// inherited the dark window colour and stayed black. Paint it on the light surface;
|
||||
// dark is left untouched (it already reads correctly via inheritance).
|
||||
if (!dp_dark()) {
|
||||
SetBackgroundColour(dp_panel_bg());
|
||||
m_form->SetBackgroundColour(dp_panel_bg());
|
||||
}
|
||||
// Explicit token background in both themes, so a theme switch can move it (an inherited
|
||||
// colour stays whatever the theme was when the panel was built).
|
||||
SetBackgroundColour(dp_panel_bg());
|
||||
m_form->SetBackgroundColour(dp_panel_bg());
|
||||
|
||||
auto* root = new wxBoxSizer(wxVERTICAL);
|
||||
{
|
||||
@@ -313,22 +345,20 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// #4D4D54); light maps onto the app's light chrome so the strip follows the theme.
|
||||
m_toolbar->SetBackgroundColour(dp_ribbon_bg());
|
||||
|
||||
const wxColour tb_bg = dp_ribbon_bg();
|
||||
const wxColour tb_hover = dp_ribbon_hover();
|
||||
auto icon_btn = [this, tb_bg, tb_hover](const char* icon, const wxString& tip) {
|
||||
auto icon_btn = [this](const char* icon, const wxString& tip) {
|
||||
// Prepare's main toolbar: 40 px icon cell, 4 px gap (GLToolbar::Default_Icons_Size
|
||||
// and set_gap_size(4)) -> 44 px pitch. Match it exactly.
|
||||
auto* b = new ScalableButton(m_toolbar, wxID_ANY, icon, "", wxSize(40, 40),
|
||||
auto* b = new ScalableButton(m_toolbar, wxID_ANY, icon, "", FromDIP(wxSize(40, 40)),
|
||||
wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, 34);
|
||||
b->SetToolTip(tip);
|
||||
b->SetBackgroundColour(tb_bg);
|
||||
b->SetBackgroundColour(dp_ribbon_bg());
|
||||
m_tool_btns.push_back(b);
|
||||
// Hover affordance, honouring the active-tool teal state.
|
||||
b->Bind(wxEVT_ENTER_WINDOW, [this, b, tb_hover](wxMouseEvent& e) {
|
||||
b->SetBackgroundColour(b == m_active_tool_btn ? wxColour(0x52, 0xC7, 0xB8) : tb_hover);
|
||||
b->Bind(wxEVT_ENTER_WINDOW, [this, b](wxMouseEvent& e) {
|
||||
b->SetBackgroundColour(b == m_active_tool_btn ? wxColour(0x52, 0xC7, 0xB8) : dp_ribbon_hover());
|
||||
b->Refresh(); e.Skip(); });
|
||||
b->Bind(wxEVT_LEAVE_WINDOW, [this, b, tb_bg](wxMouseEvent& e) {
|
||||
b->SetBackgroundColour(b == m_active_tool_btn ? wxColour(0x00, 0x96, 0x88) : tb_bg);
|
||||
b->Bind(wxEVT_LEAVE_WINDOW, [this, b](wxMouseEvent& e) {
|
||||
b->SetBackgroundColour(b == m_active_tool_btn ? wxColour(0x00, 0x96, 0x88) : dp_ribbon_bg());
|
||||
b->Refresh(); e.Skip(); });
|
||||
// Mark this tool active (teal) on press — a separate event from the
|
||||
// button's command handler, so it never swallows the click action.
|
||||
@@ -346,7 +376,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
};
|
||||
auto add_sep = [this](wxSizer* row) {
|
||||
row->AddSpacer(5);
|
||||
row->Add(new wxStaticLine(m_toolbar, wxID_ANY, wxDefaultPosition, wxSize(1, 22), wxLI_VERTICAL),
|
||||
row->Add(new wxStaticLine(m_toolbar, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(1, 22)), wxLI_VERTICAL),
|
||||
0, wxALIGN_CENTER_VERTICAL);
|
||||
row->AddSpacer(5);
|
||||
};
|
||||
@@ -497,24 +527,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
set_status(show ? _L("Bed shown") : _L("Bed hidden"));
|
||||
};
|
||||
|
||||
// Shared flyout glyph tint (used by BOTH the feature and sketch toolbars). Re-tint each
|
||||
// design_* glyph to the DropDown's resolved TEXT colour so it reads on the popup in either
|
||||
// theme: text_color is 0x363636, which darkModeColorFor() maps to a light tone in dark mode
|
||||
// (the popup bg is darkModeColorFor(white) = dark) and leaves dark in light mode. The alpha
|
||||
// (the glyph shape) is preserved; only RGB is replaced.
|
||||
// ponytail: wxBitmap(img) drops the HiDPI scale factor (no scale ctor before wx 3.1.6); the
|
||||
// deploy target runs at scale 1.0, so this is exact there.
|
||||
const wxColour drop_icon_col = StateColor::darkModeColorFor(wxColour(0x36, 0x36, 0x36));
|
||||
auto tint = [](wxBitmap bmp, const wxColour& c) -> wxBitmap {
|
||||
if (!bmp.IsOk()) return bmp;
|
||||
wxImage img = bmp.ConvertToImage();
|
||||
if (!img.HasAlpha()) img.InitAlpha();
|
||||
const int w = img.GetWidth(), h = img.GetHeight();
|
||||
for (int y = 0; y < h; ++y)
|
||||
for (int x = 0; x < w; ++x)
|
||||
img.SetRGB(x, y, c.Red(), c.Green(), c.Blue());
|
||||
return wxBitmap(img);
|
||||
};
|
||||
// Flyout rows show the design_* glyphs as they are: drawn in Orca's icon grey (#949494), which
|
||||
// the icon cache maps per theme like every other sidebar icon, so they need no re-tint.
|
||||
|
||||
// --- Feature group: Sketch / Extrude / Fillet-Chamfer / Hole / Thread / Constrain
|
||||
m_tb_feature = new wxBoxSizer(wxHORIZONTAL);
|
||||
@@ -572,13 +586,18 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
DropDown::Item it;
|
||||
it.text = v.tip;
|
||||
it.tip = v.hint;
|
||||
it.icon = tint(create_scaled_bitmap(v.icon, m_form, 18), drop_icon_col);
|
||||
it.icon = create_scaled_bitmap(v.icon, m_form, 18);
|
||||
fo->items.push_back(it);
|
||||
fo->actions.push_back(std::move(v.action));
|
||||
fo->icon_names.emplace_back(v.icon);
|
||||
}
|
||||
fo->btn = b;
|
||||
fo->drop.Create(b);
|
||||
m_icon_refresh.push_back([this, fp = fo.get()] {
|
||||
for (size_t i = 0; i < fp->items.size(); ++i)
|
||||
fp->items[i].icon = create_scaled_bitmap(fp->icon_names[i], m_form, 18);
|
||||
fp->drop.Invalidate(true);
|
||||
});
|
||||
fo->drop.SetUseContentWidth(true, false);
|
||||
fo->drop.Invalidate(true);
|
||||
FeatFlyout* fp = fo.get();
|
||||
@@ -597,7 +616,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
fp->drop.SetUseContentWidth(false, false);
|
||||
fp->drop.SetUseContentWidth(true, false);
|
||||
wxPoint pos = b->ClientToScreen(wxPoint(0, -6));
|
||||
fp->drop.Position(pos, wxSize(0, b->GetSize().y + 12));
|
||||
fp->drop.Position(pos, wxSize(0, b->GetSize().y + b->FromDIP(12)));
|
||||
fp->drop.Popup();
|
||||
});
|
||||
m_flyout_keepalive.push_back(fo);
|
||||
@@ -1224,7 +1243,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
DropDown::Item it;
|
||||
it.text = v.tip;
|
||||
it.tip = v.hint;
|
||||
it.icon = tint(create_scaled_bitmap(v.icon, m_form, 18), drop_icon_col);
|
||||
it.icon = create_scaled_bitmap(v.icon, m_form, 18);
|
||||
fo->items.push_back(it);
|
||||
fo->modes.push_back(v.mode);
|
||||
fo->hints.push_back(v.hint);
|
||||
@@ -1232,6 +1251,11 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
}
|
||||
fo->btn = b;
|
||||
fo->drop.Create(b);
|
||||
m_icon_refresh.push_back([this, fp = fo.get()] {
|
||||
for (size_t i = 0; i < fp->items.size(); ++i)
|
||||
fp->items[i].icon = create_scaled_bitmap(fp->icon_names[i], m_form, 18);
|
||||
fp->drop.Invalidate(true);
|
||||
});
|
||||
fo->drop.SetUseContentWidth(true, false);
|
||||
fo->drop.Invalidate(true);
|
||||
ToolFlyout* fp = fo.get();
|
||||
@@ -1253,7 +1277,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
fp->drop.SetUseContentWidth(false, false);
|
||||
fp->drop.SetUseContentWidth(true, false);
|
||||
wxPoint pos = b->ClientToScreen(wxPoint(0, -6));
|
||||
fp->drop.Position(pos, wxSize(0, b->GetSize().y + 12));
|
||||
fp->drop.Position(pos, wxSize(0, b->GetSize().y + b->FromDIP(12)));
|
||||
fp->drop.Popup();
|
||||
});
|
||||
m_flyout_keepalive.push_back(fo);
|
||||
@@ -1496,67 +1520,37 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// active (update_action_bar). Replaces the 13 per-card buttons + sketch Finish + Done.
|
||||
m_tb_action = new wxBoxSizer(wxHORIZONTAL);
|
||||
{
|
||||
auto* ok = new wxButton(m_toolbar, wxID_ANY, _L("✓ Confirm"));
|
||||
ok->SetForegroundColour(*wxWHITE);
|
||||
ok->SetBackgroundColour(wxColour(0x00, 0x96, 0x88)); // Orca teal accent
|
||||
auto* ok = new ::Button(m_toolbar, _L("Confirm"));
|
||||
ok->SetStyle(ButtonStyle::Confirm, ButtonType::Window);
|
||||
ok->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { tool_confirm(); });
|
||||
m_confirm_btns.push_back(ok); // refresh_preview greys this on an invalid candidate
|
||||
auto* no = new wxButton(m_toolbar, wxID_ANY, _L("✗ Cancel"));
|
||||
auto* no = new ::Button(m_toolbar, _L("Cancel"));
|
||||
no->SetStyle(ButtonStyle::Regular, ButtonType::Window);
|
||||
no->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { tool_cancel(); });
|
||||
m_tb_action->Add(ok, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6);
|
||||
m_tb_action->Add(no, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 10);
|
||||
}
|
||||
|
||||
// Persistent Undo/Redo group: always visible (not mode-gated like the tool groups), so
|
||||
// history is reachable from Feature, Sketch and Constrain alike. These are momentary
|
||||
// actions, so — unlike icon_btn — they are NOT registered in m_tool_btns and never take
|
||||
// the teal active-tool highlight. They route to the SAME do_undo_redo as the keyboard
|
||||
// Ctrl+Z / Ctrl+Shift+Z path, and are greyed by update_undo_redo_buttons().
|
||||
m_tb_history = new wxBoxSizer(wxHORIZONTAL);
|
||||
{
|
||||
auto hist_btn = [this, tb_bg, tb_hover](const char* icon, const wxString& tip) {
|
||||
auto* b = new ScalableButton(m_toolbar, wxID_ANY, icon, "", wxSize(40, 40),
|
||||
wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, 34);
|
||||
b->SetToolTip(tip);
|
||||
b->SetBackgroundColour(tb_bg);
|
||||
b->Bind(wxEVT_ENTER_WINDOW, [b, tb_hover](wxMouseEvent& e) {
|
||||
if (b->IsEnabled()) { b->SetBackgroundColour(tb_hover); b->Refresh(); } e.Skip(); });
|
||||
b->Bind(wxEVT_LEAVE_WINDOW, [b, tb_bg](wxMouseEvent& e) {
|
||||
b->SetBackgroundColour(tb_bg); b->Refresh(); e.Skip(); });
|
||||
return b;
|
||||
};
|
||||
m_btn_undo = hist_btn("menu_undo", _L("Undo (Ctrl+Z)"));
|
||||
m_btn_undo->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { do_undo_redo(false); });
|
||||
m_btn_redo = hist_btn("menu_redo", _L("Redo (Ctrl+Shift+Z)"));
|
||||
m_btn_redo->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { do_undo_redo(true); });
|
||||
m_btn_undo->Enable(false); // nothing to undo/redo on a fresh document
|
||||
m_btn_redo->Enable(false);
|
||||
m_tb_history->Add(m_btn_undo, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
|
||||
m_tb_history->Add(m_btn_redo, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
|
||||
}
|
||||
|
||||
// Document actions, left of Undo/Redo and always visible (not mode-gated like the tools).
|
||||
// Same styling as the history pair: momentary actions, never the teal active-tool state.
|
||||
// Document actions, always visible (not mode-gated like the tools): momentary actions,
|
||||
// never the teal active-tool state.
|
||||
m_tb_doc = new wxBoxSizer(wxHORIZONTAL);
|
||||
const wxColour tb_glyph_col = StateColor::darkModeColorFor(wxColour(0x36, 0x36, 0x36));
|
||||
const wxColour tb_commit_col(0x00, 0x96, 0x88); // Orca Confirm accent
|
||||
{
|
||||
// Some Orca glyphs (toolbar_add_plate, toolbar_flatten) are drawn for a light toolbar and
|
||||
// come out the same tone as this dark one — Commit was effectively invisible. Re-tint
|
||||
// those: Commit in the teal accent it carries as the tab's primary action, the rest in
|
||||
// the same grey the other toolbar glyphs resolve to.
|
||||
auto doc_btn = [this, tb_bg, tb_hover, &tint](const char* icon, const wxString& tip,
|
||||
const wxColour* glyph = nullptr) {
|
||||
auto* b = new ScalableButton(m_toolbar, wxID_ANY, icon, "", wxSize(40, 40),
|
||||
// Some Orca glyphs (toolbar_flatten) are drawn for Prepare's light GL toolbar and come out
|
||||
// the same tone as a dark ribbon. Those ship a "_dark" twin, picked per theme here (and
|
||||
// again on a theme switch) the way GLToolbar picks it.
|
||||
auto doc_btn = [this](const char* icon, const wxString& tip, bool has_dark_twin = false) {
|
||||
const std::string name(icon);
|
||||
auto themed = [name, has_dark_twin] { return has_dark_twin && dp_dark() ? name + "_dark" : name; };
|
||||
auto* b = new ScalableButton(m_toolbar, wxID_ANY, themed(), "", FromDIP(wxSize(40, 40)),
|
||||
wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, 34);
|
||||
if (glyph != nullptr)
|
||||
b->SetBitmap(tint(create_scaled_bitmap(icon, m_toolbar, 42), *glyph));
|
||||
if (has_dark_twin)
|
||||
m_icon_refresh.push_back([b, themed] { b->SetBitmap_(themed()); });
|
||||
b->SetToolTip(tip);
|
||||
b->SetBackgroundColour(tb_bg);
|
||||
b->Bind(wxEVT_ENTER_WINDOW, [b, tb_hover](wxMouseEvent& e) {
|
||||
if (b->IsEnabled()) { b->SetBackgroundColour(tb_hover); b->Refresh(); } e.Skip(); });
|
||||
b->Bind(wxEVT_LEAVE_WINDOW, [b, tb_bg](wxMouseEvent& e) {
|
||||
b->SetBackgroundColour(tb_bg); b->Refresh(); e.Skip(); });
|
||||
b->SetBackgroundColour(dp_ribbon_bg());
|
||||
b->Bind(wxEVT_ENTER_WINDOW, [b](wxMouseEvent& e) {
|
||||
if (b->IsEnabled()) { b->SetBackgroundColour(dp_ribbon_hover()); b->Refresh(); } e.Skip(); });
|
||||
b->Bind(wxEVT_LEAVE_WINDOW, [b](wxMouseEvent& e) {
|
||||
b->SetBackgroundColour(dp_ribbon_bg()); b->Refresh(); e.Skip(); });
|
||||
return b;
|
||||
};
|
||||
auto add_doc = [this](ScalableButton* b) {
|
||||
@@ -1591,12 +1585,13 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// These act on bodies / the view, so they ride in the feature group, in the slots
|
||||
// the user assigned them (9, 11bis, 16).
|
||||
auto* b_place = doc_btn("toolbar_flatten", _L("Place on Face (F) — lay the picked face on the bed"),
|
||||
&tb_glyph_col);
|
||||
true);
|
||||
b_place->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { place_on_face(); });
|
||||
tb_slot["place"].push_back(b_place);
|
||||
|
||||
auto* b_section = doc_btn("split_parts", _L("Section View — hide part of the model to see inside. "
|
||||
"PageUp/PageDown move the plane; Delete removes it."));
|
||||
"PageUp/PageDown move the plane; Delete removes it."),
|
||||
true);
|
||||
b_section->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { toggle_section_view(); });
|
||||
tb_slot["section"].push_back(b_section);
|
||||
|
||||
@@ -1608,7 +1603,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// Commit is the tab's primary action and sits far right, next to Confirm/Cancel.
|
||||
m_tb_commit = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* b_commit = doc_btn("toolbar_add_plate", _L("Commit to Plate — send the solid to Prepare"),
|
||||
&tb_commit_col);
|
||||
true);
|
||||
b_commit->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_commit(); });
|
||||
m_tb_commit->Add(b_commit, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
|
||||
}
|
||||
@@ -1637,8 +1632,6 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
tbrow->AddSpacer(8);
|
||||
tbrow->Add(m_tb_doc, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
|
||||
add_sep(tbrow);
|
||||
tbrow->Add(m_tb_history, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
|
||||
add_sep(tbrow);
|
||||
tbrow->Add(m_tb_feature, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
|
||||
tbrow->Add(m_tb_sketch, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
|
||||
tbrow->Add(m_tb_relations, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
|
||||
@@ -1647,15 +1640,18 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
tbrow->Add(m_tb_action, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
|
||||
tbrow->AddSpacer(8);
|
||||
m_toolbar->SetSizer(tbrow);
|
||||
build_phase("toolbar");
|
||||
// Apply the body gates once now: an empty document is exactly the state the bug was reported
|
||||
// in, and feed_bodies() has not run yet on a fresh tab.
|
||||
update_body_gates();
|
||||
|
||||
// Onshape-style dialog-card header: feature icon + bold title. out receives
|
||||
// the title control so open_tool() can retitle it per feature.
|
||||
auto card_header = [](wxWindow* card, const char* icon, const wxString& title, wxStaticText*& out) -> wxSizer* {
|
||||
auto card_header = [this](wxWindow* card, const char* icon, const wxString& title, wxStaticText*& out) -> wxSizer* {
|
||||
auto* h = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* ic = new wxStaticBitmap(card, wxID_ANY, create_scaled_bitmap(icon, card, 18));
|
||||
m_icon_refresh.push_back([ic, card, name = std::string(icon)] {
|
||||
ic->SetBitmap(create_scaled_bitmap(name, card, 18)); });
|
||||
out = new wxStaticText(card, wxID_ANY, title);
|
||||
out->SetFont(Label::Head_14); // Orca shared HarmonyOS card-title font
|
||||
h->Add(ic, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 8);
|
||||
@@ -2191,8 +2187,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
plform->Add(m_plane_tilt_axis, 0, wxEXPAND);
|
||||
|
||||
// Contextual reference picks: arm a target, then click a solid face/edge in the canvas.
|
||||
auto pick_row = [&](const wxString& label, wxButton*& btn, wxStaticText*& lbl, PlanePick target) {
|
||||
btn = new wxButton(m_cards, wxID_ANY, label);
|
||||
auto pick_row = [&](const wxString& label, ::Button*& btn, wxStaticText*& lbl, PlanePick target) {
|
||||
btn = new ::Button(m_cards, label);
|
||||
btn->SetStyle(ButtonStyle::Regular, ButtonType::Parameter);
|
||||
lbl = new wxStaticText(m_cards, wxID_ANY, _L("(none)"));
|
||||
btn->Bind(wxEVT_BUTTON, [this, target](wxCommandEvent&) { arm_plane_pick(target); });
|
||||
plform->Add(btn);
|
||||
@@ -2220,7 +2217,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
m_box_loft->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
|
||||
m_box_loft->Add(new wxStaticText(m_cards, wxID_ANY, _L("Profiles (check 2+, in order):")),
|
||||
0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_loft_list = new wxCheckListBox(m_cards, wxID_ANY, wxDefaultPosition, wxSize(-1, 120));
|
||||
m_loft_list = new wxCheckListBox(m_cards, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(-1, 120)));
|
||||
m_loft_list->Bind(wxEVT_CHECKLISTBOX, [this](wxCommandEvent&) { refresh_preview(); });
|
||||
m_box_loft->Add(m_loft_list, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
{
|
||||
@@ -2292,7 +2289,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
m_box_surf_loft->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
|
||||
m_box_surf_loft->Add(new wxStaticText(m_cards, wxID_ANY, _L("Profiles (check 2+, in order):")),
|
||||
0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_surf_loft_list = new wxCheckListBox(m_cards, wxID_ANY, wxDefaultPosition, wxSize(-1, 120));
|
||||
m_surf_loft_list = new wxCheckListBox(m_cards, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(-1, 120)));
|
||||
m_surf_loft_list->Bind(wxEVT_CHECKLISTBOX, [this](wxCommandEvent&) { refresh_preview(); });
|
||||
m_box_surf_loft->Add(m_surf_loft_list, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_surf_loft_ruled = new CheckBox(m_cards);
|
||||
@@ -2532,7 +2529,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
m_rib_sketch->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
|
||||
rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Sketch")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
rform->Add(m_rib_sketch, 0, wxEXPAND);
|
||||
m_rib_entity = new wxSpinCtrl(m_cards, wxID_ANY, "", wxDefaultPosition, wxSize(90, -1),
|
||||
m_rib_entity = new wxSpinCtrl(m_cards, wxID_ANY, "", wxDefaultPosition, FromDIP(wxSize(90, -1)),
|
||||
wxSP_ARROW_KEYS | wxBORDER_SIMPLE);
|
||||
m_rib_entity->SetRange(0, 999);
|
||||
m_rib_entity->SetValue(0);
|
||||
@@ -2589,7 +2586,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
m_del_face_body->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
|
||||
dform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Body")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
dform->Add(m_del_face_body, 0, wxEXPAND);
|
||||
m_del_face_add_btn = new wxButton(m_cards, wxID_ANY, _L("Add picked face"));
|
||||
m_del_face_add_btn = new ::Button(m_cards, _L("Add picked face"));
|
||||
m_del_face_add_btn->SetStyle(ButtonStyle::Regular, ButtonType::Parameter);
|
||||
m_del_face_add_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
// Say why nothing happened. Clicking with no face picked used to be a silent no-op,
|
||||
// which is indistinguishable from the button being broken.
|
||||
@@ -2686,8 +2684,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
auto* axform = two_col_form();
|
||||
|
||||
auto ax_pick = [&](const wxString& label, wxButton*& btn, wxStaticText*& lbl, AxisPick target) {
|
||||
btn = new wxButton(m_cards, wxID_ANY, label);
|
||||
auto ax_pick = [&](const wxString& label, ::Button*& btn, wxStaticText*& lbl, AxisPick target) {
|
||||
btn = new ::Button(m_cards, label);
|
||||
btn->SetStyle(ButtonStyle::Regular, ButtonType::Parameter);
|
||||
lbl = new wxStaticText(m_cards, wxID_ANY, _L("(none)"));
|
||||
btn->Bind(wxEVT_BUTTON, [this, target](wxCommandEvent&) { arm_axis_pick(target); });
|
||||
axform->Add(btn);
|
||||
@@ -2766,8 +2765,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Z")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(spin_frame(m_cs_z), 0, wxEXPAND);
|
||||
|
||||
auto cs_pick = [&](const wxString& label, wxButton*& btn, wxStaticText*& lbl, CoordSysPick target) {
|
||||
btn = new wxButton(m_cards, wxID_ANY, label);
|
||||
auto cs_pick = [&](const wxString& label, ::Button*& btn, wxStaticText*& lbl, CoordSysPick target) {
|
||||
btn = new ::Button(m_cards, label);
|
||||
btn->SetStyle(ButtonStyle::Regular, ButtonType::Parameter);
|
||||
lbl = new wxStaticText(m_cards, wxID_ANY, _L("(none)"));
|
||||
btn->Bind(wxEVT_BUTTON, [this, target](wxCommandEvent&) { arm_coordsys_pick(target); });
|
||||
csform->Add(btn);
|
||||
@@ -2776,7 +2776,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
cs_pick(_L("Pick Face"), m_cs_pick_face, m_cs_face_lbl, CoordSysPick::Face);
|
||||
|
||||
// ponytail: edge pick for CoordSys with rotation-direction hint
|
||||
m_cs_pick_edge = new wxButton(m_cards, wxID_ANY, _L("Edge (sets in-plane direction)"));
|
||||
m_cs_pick_edge = new ::Button(m_cards, _L("Edge (sets in-plane direction)"));
|
||||
m_cs_pick_edge->SetStyle(ButtonStyle::Regular, ButtonType::Parameter);
|
||||
m_cs_edge_lbl = new wxStaticText(m_cards, wxID_ANY, _L("(none)"));
|
||||
m_cs_pick_edge->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { arm_coordsys_pick(CoordSysPick::Edge); });
|
||||
csform->Add(m_cs_pick_edge);
|
||||
@@ -2920,8 +2921,10 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
m_box_expr->Add(eform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
|
||||
auto* brow = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_expr_set_btn = new wxButton(m_cards, wxID_ANY, _L("Set"), wxDefaultPosition, wxSize(50, 24));
|
||||
m_expr_clear_btn = new wxButton(m_cards, wxID_ANY, _L("Clear"), wxDefaultPosition, wxSize(50, 24));
|
||||
m_expr_set_btn = new ::Button(m_cards, _L("Set"));
|
||||
m_expr_clear_btn = new ::Button(m_cards, _L("Clear"));
|
||||
m_expr_set_btn->SetStyle(ButtonStyle::Confirm, ButtonType::Parameter);
|
||||
m_expr_clear_btn->SetStyle(ButtonStyle::Regular, ButtonType::Parameter);
|
||||
m_expr_set_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_set_expr(); });
|
||||
m_expr_clear_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_clear_expr(); });
|
||||
brow->Add(m_expr_set_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6);
|
||||
@@ -2948,7 +2951,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// formats per the user locale (comma) with no clean override, so we own the
|
||||
// formatting here to guarantee international '.' decimals.
|
||||
m_value_input = new wxTextCtrl(m_cards, wxID_ANY, "", wxDefaultPosition,
|
||||
wxSize(90, -1), wxTE_PROCESS_ENTER);
|
||||
FromDIP(wxSize(90, -1)), wxTE_PROCESS_ENTER);
|
||||
m_value_input->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) { confirm_value(); });
|
||||
vrow->Add(new wxStaticText(m_cards, wxID_ANY, _L("Value")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 8);
|
||||
vrow->Add(m_value_input, 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -3016,19 +3019,27 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
FromDIP(SidebarProps::ContentMargin()));
|
||||
tree_inner->Add(new wxStaticLine(m_tree_box), 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
|
||||
FromDIP(SidebarProps::TitlebarMargin()));
|
||||
m_tree = new wxTreeCtrl(m_tree_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 64),
|
||||
m_tree = new wxTreeCtrl(m_tree_box, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(-1, 64)),
|
||||
wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES |
|
||||
wxTR_FULL_ROW_HIGHLIGHT | wxBORDER_SIMPLE | wxTR_EDIT_LABELS);
|
||||
if (!dp_dark()) m_tree->SetBackgroundColour(dp_panel_bg());
|
||||
m_tree->SetBackgroundColour(dp_panel_bg());
|
||||
// Per-feature-type icons (indices match tree_icon_for): sketch/extrude/dressup/hole/thread.
|
||||
m_tree_images = new wxImageList(16, 16);
|
||||
m_tree_images->Add(create_scaled_bitmap("design_sketch", nullptr, 16)); // 0 Sketch
|
||||
m_tree_images->Add(create_scaled_bitmap("design_extrude", nullptr, 16)); // 1 Extrude
|
||||
m_tree_images->Add(create_scaled_bitmap("design_dressup", nullptr, 16)); // 2 Fillet/Chamfer
|
||||
m_tree_images->Add(create_scaled_bitmap("design_hole", nullptr, 16)); // 3 Hole
|
||||
m_tree_images->Add(create_scaled_bitmap("design_thread", nullptr, 16)); // 4 Thread
|
||||
m_tree_images->Add(create_scaled_bitmap("design_shell", nullptr, 16)); // 5 Shell
|
||||
m_tree->AssignImageList(m_tree_images);
|
||||
// The list is sized from the bitmaps themselves (image-list sizes are physical and must match
|
||||
// them), and rebuilt with the other icons on a DPI or theme change.
|
||||
auto tree_images = [this] {
|
||||
static const char* const kIcons[] = { "design_sketch", "design_extrude", "design_dressup",
|
||||
"design_hole", "design_thread", "design_shell" };
|
||||
std::vector<wxBitmap> bmps;
|
||||
for (const char* name : kIcons)
|
||||
bmps.push_back(create_scaled_bitmap(name, this, 16));
|
||||
const wxSize sz = bmps.front().GetSize();
|
||||
m_tree_images = new wxImageList(sz.x, sz.y);
|
||||
for (const wxBitmap& b : bmps)
|
||||
m_tree_images->Add(b);
|
||||
m_tree->AssignImageList(m_tree_images); // takes ownership; frees the previous list
|
||||
};
|
||||
tree_images();
|
||||
m_icon_refresh.push_back(tree_images);
|
||||
tree_inner->Add(m_tree, 0, wxEXPAND | wxALL, 12);
|
||||
|
||||
// Selecting a body-producing feature (Extrude/Fillet/Chamfer/Hole/Thread) in the
|
||||
@@ -3164,7 +3175,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
wxBoxSizer* trow = m_hdr_tree_row;
|
||||
auto edit_btn = [this](const char* icon, const wxString& tip) {
|
||||
// Header-sized: reads as a section action, not a primary control.
|
||||
auto* b = new ScalableButton(m_tree_box, wxID_ANY, icon, "", wxSize(24, 24),
|
||||
auto* b = new ScalableButton(m_tree_box, wxID_ANY, icon, "", FromDIP(wxSize(24, 24)),
|
||||
wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, 20);
|
||||
b->SetToolTip(tip);
|
||||
return b;
|
||||
@@ -3202,7 +3213,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
trow->Add(down, 0, wxALIGN_CENTER_VERTICAL);
|
||||
// Interference check sits after a rule: it reports, it does not edit the recipe.
|
||||
trow->AddSpacer(8);
|
||||
trow->Add(new wxStaticLine(m_tree_box, wxID_ANY, wxDefaultPosition, wxSize(1, 22), wxLI_VERTICAL),
|
||||
trow->Add(new wxStaticLine(m_tree_box, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(1, 22)), wxLI_VERTICAL),
|
||||
0, wxALIGN_CENTER_VERTICAL);
|
||||
trow->AddSpacer(8);
|
||||
trow->Add(m_btn_interfere, 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -3230,7 +3241,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// policy — the card only gives them a home next to the rows they act on.
|
||||
{
|
||||
auto body_btn = [this](const char* icon, const wxString& tip) {
|
||||
auto* b = new ScalableButton(m_parts_box, wxID_ANY, icon, "", wxSize(24, 24),
|
||||
auto* b = new ScalableButton(m_parts_box, wxID_ANY, icon, "", FromDIP(wxSize(24, 24)),
|
||||
wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, 20);
|
||||
b->SetToolTip(tip);
|
||||
return b;
|
||||
@@ -3268,10 +3279,10 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
FromDIP(SidebarProps::TitlebarMargin()));
|
||||
m_parts_hdr->ShowItems(false); // no bodies yet on a fresh document
|
||||
m_parts_rule->Hide();
|
||||
m_parts = new wxTreeCtrl(m_parts_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 48),
|
||||
m_parts = new wxTreeCtrl(m_parts_box, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(-1, 48)),
|
||||
wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES |
|
||||
wxTR_FULL_ROW_HIGHLIGHT | wxBORDER_SIMPLE | wxTR_EDIT_LABELS);
|
||||
if (!dp_dark()) m_parts->SetBackgroundColour(dp_panel_bg());
|
||||
m_parts->SetBackgroundColour(dp_panel_bg());
|
||||
parts_inner->Add(m_parts, 0, wxEXPAND | wxALL, 12);
|
||||
// Start hidden: a fresh document has no bodies, and refresh_parts() only runs on the first
|
||||
// tree rebuild — until then an empty box would sit under the header.
|
||||
@@ -3358,9 +3369,16 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
var_hdr->Add(card_header(m_var_box, "design_constrain", _L("Variables"), var_hdr_title), 0,
|
||||
wxALIGN_CENTER_VERTICAL);
|
||||
var_hdr->AddStretchSpacer();
|
||||
m_btn_add_var = new wxButton(m_var_box, wxID_ANY, _L("+"), wxDefaultPosition, wxSize(30, 24));
|
||||
m_btn_edit_var = new wxButton(m_var_box, wxID_ANY, _L("Edit"), wxDefaultPosition, wxSize(50, 24));
|
||||
m_btn_del_var = new wxButton(m_var_box, wxID_ANY, _L("Del"), wxDefaultPosition, wxSize(42, 24));
|
||||
// Icon actions in the card header, as the Feature tree and Bodies cards have them.
|
||||
auto var_btn = [this](const char* icon, const wxString& tip) {
|
||||
auto* b = new ScalableButton(m_var_box, wxID_ANY, icon, "", FromDIP(wxSize(24, 24)),
|
||||
wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, 20);
|
||||
b->SetToolTip(tip);
|
||||
return b;
|
||||
};
|
||||
m_btn_add_var = var_btn("add", _L("Add variable"));
|
||||
m_btn_edit_var = var_btn("design_edit", _L("Edit variable"));
|
||||
m_btn_del_var = var_btn("design_delete", _L("Delete variable"));
|
||||
m_btn_add_var->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_add_variable(); });
|
||||
m_btn_edit_var->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_edit_variable(); });
|
||||
m_btn_del_var->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_remove_variable(); });
|
||||
@@ -3375,7 +3393,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
m_var_list = new wxListCtrl(m_var_box, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(-1, FromDIP(64)), wxLC_REPORT | wxLC_SINGLE_SEL);
|
||||
if (!dp_dark()) m_var_list->SetBackgroundColour(dp_panel_bg());
|
||||
m_var_list->SetBackgroundColour(dp_panel_bg());
|
||||
m_var_list->AppendColumn(_L("Name"), wxLIST_FORMAT_LEFT, FromDIP(90));
|
||||
m_var_list->AppendColumn(_L("Expression"), wxLIST_FORMAT_LEFT, FromDIP(120));
|
||||
var_inner->Add(m_var_list, 0, wxEXPAND | wxALL, FromDIP(SidebarProps::ContentMargin()));
|
||||
@@ -3388,7 +3406,6 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
m_status = new wxStaticText(m_form, wxID_ANY, "");
|
||||
m_status->Hide(); // storage only — the line is drawn over the viewport, see set_status()
|
||||
m_status_default_fg = m_status->GetForegroundColour(); // capture BEFORE any caller writes
|
||||
root->Add(m_status, 0, wxLEFT | wxRIGHT | wxBOTTOM, 12);
|
||||
|
||||
// DoF / constraint-state readout (P3). Dedicated line so it never clobbers the
|
||||
@@ -3460,11 +3477,13 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
m_form->FitInside();
|
||||
m_form->SetScrollRate(0, FromDIP(20)); // vertical only, like Prepare's sidebar: never scroll labels out
|
||||
m_form->SetMinSize(wxSize(264, -1));
|
||||
m_form->SetMinSize(FromDIP(wxSize(264, -1)));
|
||||
|
||||
build_phase("sidebar and tool cards");
|
||||
// Right column: a small view toolbar over the live 3D viewport that mirrors
|
||||
// the CadDocument body.
|
||||
m_viewport = new DesignCanvas(this);
|
||||
build_phase("3D canvas");
|
||||
|
||||
m_viewport->set_on_sketch_commit([this](const SketchProfile& prof, const SketchPlane& plane) {
|
||||
m_doc.checkpoint(); // undo boundary: committing a sketch
|
||||
@@ -4346,6 +4365,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
}
|
||||
|
||||
set_ui_mode(UiMode::Feature);
|
||||
build_phase("wiring");
|
||||
BOOST_LOG_TRIVIAL(info) << "Design tab build: total " << build_clock.Time() << " ms";
|
||||
}
|
||||
|
||||
void DesignPanel::set_active_tool_btn(ScalableButton* b)
|
||||
@@ -4928,7 +4949,8 @@ void DesignPanel::on_import_mesh()
|
||||
"merging, so importing it may take a long time and leave a body that is slow to "
|
||||
"edit. Decimating the mesh first is usually better.\n\nImport anyway?"),
|
||||
int(mesh.its.indices.size()));
|
||||
if (wxMessageBox(q, _L("Large mesh"), wxYES_NO | wxICON_WARNING, this) != wxYES)
|
||||
MessageDialog dlg(this, q, _L("Large mesh"), wxYES_NO | wxICON_WARNING);
|
||||
if (dlg.ShowModal() != wxID_YES)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6288,14 +6310,11 @@ void DesignPanel::set_status(StatusKind kind, const wxString& body)
|
||||
// 8cc), which silently length-limited every hint in the tab. The viewport's bottom
|
||||
// margin has the whole window width, so a sentence can be a sentence.
|
||||
if (m_viewport != nullptr) {
|
||||
// wxNullColour means "no opinion", and the dark default text colour is nearly invisible
|
||||
// on the dark HUD; only a colour a caller actually chose (the error red, the plane-pick
|
||||
// green) is carried over. Compared against the colour the label was CREATED with —
|
||||
// comparing against the parent's foreground instead reported "chosen" for every line,
|
||||
// and the neutral text came out the panel's grey.
|
||||
const wxColour fg = m_status->GetForegroundColour();
|
||||
m_viewport->set_status_text(text, fg != m_status_default_fg ? fg
|
||||
: wxColour(0xDD, 0xE1, 0xE6));
|
||||
// Only a colour a caller actually chose (the error red, the plane-pick green) is carried
|
||||
// over; a neutral line (wxNullColour above) takes the viewport overlay's own text colour,
|
||||
// which follows the theme.
|
||||
m_viewport->set_status_text(text, m_status->UseForegroundColour() ? m_status->GetForegroundColour()
|
||||
: wxNullColour);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7155,30 +7174,80 @@ wxString DesignPanel::idle_hint() const
|
||||
// Nothing else in the panel needs to know: the popup keeps its text and comes straight back.
|
||||
void DesignPanel::on_tab_hidden()
|
||||
{
|
||||
if (m_viewport) {
|
||||
m_viewport->show_status_hud(false);
|
||||
if (m_viewport)
|
||||
m_viewport->leave_viewport(); // hand the shared camera back to the editor tabs
|
||||
}
|
||||
}
|
||||
|
||||
void DesignPanel::on_tab_shown()
|
||||
{
|
||||
if (m_viewport) {
|
||||
m_viewport->show_status_hud(true); // ...and back on the way in
|
||||
if (m_viewport)
|
||||
m_viewport->enter_viewport(); // borrow the shared camera; on_tab_hidden gives it back
|
||||
}
|
||||
|
||||
if (m_active == Tool::None && m_doc.display_mesh.its.indices.empty())
|
||||
set_status(idle_hint()); // first paint: the tab has never been edited
|
||||
|
||||
wxStopWatch show_clock;
|
||||
if (m_viewport) m_viewport->refresh_bed();
|
||||
|
||||
hydrate_from_model();
|
||||
update_reference_planes(); // entering the Design tab: show the XY/XZ/YZ planes if no object yet
|
||||
if (show_clock.Time() > 100) // a slow first show is what users report; the usual one is not news
|
||||
BOOST_LOG_TRIVIAL(info) << "Design tab shown: bed, project recipe and planes in " << show_clock.Time() << " ms";
|
||||
sync_sidebar_width(); // keep the panel as wide as Prepare's so the canvas edge doesn't jump
|
||||
if (m_viewport) m_viewport->force_repaint(); // the page was just re-shown: paint it for real
|
||||
}
|
||||
|
||||
void DesignPanel::refresh_icons()
|
||||
{
|
||||
// Plain ScalableButtons re-read their icon for the current scale and theme, and the Orca
|
||||
// widgets re-measure themselves...
|
||||
std::function<void(wxWindow*)> walk = [&walk](wxWindow* w) {
|
||||
if (auto* b = dynamic_cast<ScalableButton*>(w))
|
||||
b->msw_rescale();
|
||||
else if (auto* b = dynamic_cast<::Button*>(w))
|
||||
b->Rescale();
|
||||
else if (auto* c = dynamic_cast<::CheckBox*>(w))
|
||||
c->Rescale();
|
||||
else if (auto* c = dynamic_cast<::ComboBox*>(w))
|
||||
c->Rescale();
|
||||
for (wxWindow* child : w->GetChildren())
|
||||
walk(child);
|
||||
};
|
||||
walk(this);
|
||||
// ...then the icons that are not a button face (and the buttons that swap to a "_dark" twin).
|
||||
for (auto& refresh : m_icon_refresh)
|
||||
refresh();
|
||||
}
|
||||
|
||||
void DesignPanel::msw_rescale()
|
||||
{
|
||||
refresh_icons();
|
||||
if (m_viewport) m_viewport->Refresh();
|
||||
Layout();
|
||||
}
|
||||
|
||||
void DesignPanel::on_sys_color_changed()
|
||||
{
|
||||
// Every chrome colour here was set from a DpToken in the theme that was current at the time.
|
||||
// Move each one, background and text, onto the same token in the new theme; any other colour
|
||||
// (the teal accents, the status colours) is the same in both and stays.
|
||||
const bool to_dark = dp_dark();
|
||||
std::function<void(wxWindow*)> walk = [&walk, to_dark](wxWindow* w) {
|
||||
if (w->UseBackgroundColour())
|
||||
w->SetBackgroundColour(dp_retheme(w->GetBackgroundColour(), to_dark));
|
||||
if (w->UseForegroundColour())
|
||||
w->SetForegroundColour(dp_retheme(w->GetForegroundColour(), to_dark));
|
||||
for (wxWindow* child : w->GetChildren())
|
||||
walk(child);
|
||||
};
|
||||
walk(this);
|
||||
// The native controls (trees, lists, spins) take the app's own dark pass.
|
||||
wxGetApp().UpdateDarkUIWin(this);
|
||||
refresh_icons();
|
||||
refresh_tree(); // the rows carry their own text colours
|
||||
Refresh();
|
||||
}
|
||||
|
||||
// Rehydrate the parametric model from a freshly loaded project (the 3MF carried the recipe in
|
||||
// Metadata/orca_cad.bin). Only into an EMPTY document, so an active design is never clobbered
|
||||
// when the user toggles back to the tab. Called on tab show and by the control socket — which
|
||||
@@ -7671,7 +7740,7 @@ void DesignPanel::after_tree_edit(bool ok)
|
||||
void DesignPanel::on_new_design()
|
||||
{
|
||||
if (m_doc.features.empty() && m_doc.bodies.empty()) { set_status_ok(); return; }
|
||||
wxMessageDialog dlg(this,
|
||||
MessageDialog dlg(this,
|
||||
_L("Erase all features and bodies and start a new design? This cannot be undone."),
|
||||
_L("New Design"), wxYES_NO | wxICON_EXCLAMATION);
|
||||
if (dlg.ShowModal() != wxID_YES) return;
|
||||
@@ -8188,14 +8257,14 @@ void DesignPanel::rebuild_constraint_list()
|
||||
auto* row = new wxBoxSizer(wxHORIZONTAL);
|
||||
// Delete button first (fixed left position, always visible — long labels can
|
||||
// horizontally scroll but ✗ stays put and clickable). BMP-safe ✗ glyph.
|
||||
auto* del = new wxButton(m_cards, wxID_ANY, wxString::FromUTF8("✗"),
|
||||
wxDefaultPosition, wxSize(26, -1));
|
||||
auto* del = new ScalableButton(m_cards, wxID_ANY, "design_delete", "", FromDIP(wxSize(24, 24)),
|
||||
wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, 16);
|
||||
del->SetToolTip(_L("Delete constraint"));
|
||||
del->Bind(wxEVT_BUTTON, [this, i](wxCommandEvent&) { delete_constraint(i); });
|
||||
// Clickable label: selecting it highlights the referenced entities.
|
||||
auto* lbl = new wxButton(m_cards, wxID_ANY, constraint_label(cons[i]),
|
||||
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBORDER_NONE);
|
||||
lbl->Bind(wxEVT_BUTTON, [this, i](wxCommandEvent&) { highlight_constraint_entities(i); });
|
||||
auto* lbl = new wxStaticText(m_cards, wxID_ANY, constraint_label(cons[i]));
|
||||
lbl->SetCursor(wxCursor(wxCURSOR_HAND));
|
||||
lbl->Bind(wxEVT_LEFT_UP, [this, i](wxMouseEvent&) { highlight_constraint_entities(i); });
|
||||
row->Add(del, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6);
|
||||
row->Add(lbl, 1, wxALIGN_CENTER_VERTICAL);
|
||||
m_constraint_rows->Add(row, 0, wxEXPAND | wxTOP, 2);
|
||||
@@ -10792,7 +10861,7 @@ void DesignPanel::refresh_preview()
|
||||
default: ready = _L("Sketch ready"); break;
|
||||
}
|
||||
set_status(StatusKind::Ok, ready);
|
||||
for (wxButton* b : m_confirm_btns) if (b) b->Enable(true);
|
||||
for (::Button* b : m_confirm_btns) if (b) b->Enable(true);
|
||||
m_status->Refresh();
|
||||
update_datum_gizmo(); // Plane card: show/refresh the in-canvas resize handles
|
||||
update_helix_gizmo(); // Helix card: draw the live curve + drag handles (no solid ghost)
|
||||
@@ -10835,7 +10904,7 @@ void DesignPanel::refresh_preview()
|
||||
set_status(StatusKind::Error, wxString::Format(_L("Invalid: %s"), kernel_error_text(err)));
|
||||
}
|
||||
}
|
||||
for (wxButton* b : m_confirm_btns) if (b) b->Enable(ok);
|
||||
for (::Button* b : m_confirm_btns) if (b) b->Enable(ok);
|
||||
m_status->Refresh();
|
||||
return;
|
||||
}
|
||||
@@ -10881,7 +10950,7 @@ void DesignPanel::refresh_preview()
|
||||
}
|
||||
// Onshape parity: a broken candidate cannot be committed. Grey the active dialog's
|
||||
// Confirm so the user sees the gate before clicking; the red status says why.
|
||||
for (wxButton* b : m_confirm_btns)
|
||||
for (::Button* b : m_confirm_btns)
|
||||
if (b != nullptr) b->Enable(ok);
|
||||
// Fillet/Chamfer/Draft: once the target edge/face yields a valid result, show ONLY the
|
||||
// preview (hide the base bodies) so the user sees the finished shape, not the old solid
|
||||
@@ -11450,7 +11519,7 @@ void DesignPanel::tool_cancel()
|
||||
// the user to press the very button they had just pressed: a sketch could be kept but
|
||||
// never discarded.
|
||||
if (m_viewport && m_viewport->live_sketch_has_work()) {
|
||||
wxMessageDialog dlg(this,
|
||||
RichMessageDialog dlg(this,
|
||||
_L("Discard this sketch and everything drawn in it?"),
|
||||
_L("Discard sketch"),
|
||||
wxYES_NO | wxNO_DEFAULT | wxICON_EXCLAMATION);
|
||||
@@ -11481,9 +11550,9 @@ bool DesignPanel::confirm_enabled() const
|
||||
return (m_viewport && m_viewport->moving_body())
|
||||
|| m_ui_mode == UiMode::Sketch || m_ui_mode == UiMode::Constrain;
|
||||
if (m_active == Tool::Insert) return true;
|
||||
for (wxButton* b : m_confirm_btns)
|
||||
for (::Button* b : m_confirm_btns)
|
||||
if (b != nullptr && b->IsShownOnScreen()) return b->IsEnabled();
|
||||
for (wxButton* b : m_confirm_btns)
|
||||
for (::Button* b : m_confirm_btns)
|
||||
if (b != nullptr) return b->IsEnabled();
|
||||
return true;
|
||||
}
|
||||
@@ -11582,18 +11651,10 @@ bool DesignPanel::menu_can_undo_redo(bool redo) const
|
||||
|
||||
void DesignPanel::update_undo_redo_buttons()
|
||||
{
|
||||
// Grey Undo/Redo to mirror exactly what do_undo_redo will do: it acts only in Feature
|
||||
// mode with no tool/dialog open (otherwise Esc is the way out), so reflect that gate here
|
||||
// as well as the document's available history.
|
||||
if (m_btn_undo == nullptr || m_btn_redo == nullptr) return;
|
||||
if (m_ui_mode == UiMode::Sketch && m_viewport && m_viewport->is_sketching()) {
|
||||
m_btn_undo->Enable(m_viewport->can_undo_sketch_entity());
|
||||
m_btn_redo->Enable(m_viewport->can_redo_sketch_entity());
|
||||
return;
|
||||
}
|
||||
const bool gated = (m_ui_mode != UiMode::Feature) || (m_active != Tool::None);
|
||||
m_btn_undo->Enable(!gated && m_doc.can_undo());
|
||||
m_btn_redo->Enable(!gated && m_doc.can_redo());
|
||||
// The tab has no Undo/Redo of its own: the app's (the top bar, Ctrl+Z, Edit) drive this
|
||||
// history while the tab is shown, greyed to exactly what do_undo_redo will do.
|
||||
if (MainFrame* frame = wxGetApp().mainframe; frame != nullptr && IsShownOnScreen())
|
||||
frame->set_undo_redo_enabled(menu_can_undo_redo(false), menu_can_undo_redo(true));
|
||||
}
|
||||
|
||||
void DesignPanel::update_action_bar()
|
||||
@@ -11674,19 +11735,78 @@ void DesignPanel::refresh_variables()
|
||||
}
|
||||
}
|
||||
|
||||
// A design variable's name and expression, in Orca's dialog style: one dialog for both fields
|
||||
// rather than two bare text prompts. Editing an existing variable keeps its name fixed.
|
||||
class DesignVariableDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
DesignVariableDialog(wxWindow* parent, const wxString& title, const wxString& name,
|
||||
const wxString& expr, bool name_editable)
|
||||
: DPIDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
SetFont(Label::Body_14);
|
||||
auto* grid = new wxFlexGridSizer(2, FromDIP(8), FromDIP(12));
|
||||
grid->AddGrowableCol(1);
|
||||
auto field = [this, grid](const wxString& label, const wxString& value) {
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, label), 0, wxALIGN_CENTER_VERTICAL);
|
||||
auto* in = new ::TextInput(this, value, "", "", wxDefaultPosition, wxSize(FromDIP(240), -1),
|
||||
wxTE_PROCESS_ENTER);
|
||||
in->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) { EndModal(wxID_OK); });
|
||||
grid->Add(in, 1, wxEXPAND);
|
||||
return in;
|
||||
};
|
||||
m_name = field(_L("Name"), name);
|
||||
m_expr = field(_L("Expression"), expr);
|
||||
m_name->Enable(name_editable);
|
||||
|
||||
m_buttons = new DialogButtons(this, {"OK", "Cancel"});
|
||||
m_buttons->GetOK()->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { EndModal(wxID_OK); });
|
||||
m_buttons->GetCANCEL()->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { EndModal(wxID_CANCEL); });
|
||||
|
||||
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(grid, 1, wxEXPAND | wxALL, FromDIP(16));
|
||||
sizer->Add(m_buttons, 0, wxEXPAND);
|
||||
SetSizerAndFit(sizer);
|
||||
CenterOnParent();
|
||||
(name_editable ? m_name : m_expr)->GetTextCtrl()->SetFocus();
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
}
|
||||
|
||||
wxString name() const { return trimmed(m_name); }
|
||||
wxString expression() const { return trimmed(m_expr); }
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect&) override
|
||||
{
|
||||
m_name->Rescale();
|
||||
m_expr->Rescale();
|
||||
GetSizer()->SetSizeHints(this);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private:
|
||||
static wxString trimmed(const ::TextInput* in)
|
||||
{
|
||||
wxString v = in->GetTextCtrl()->GetValue();
|
||||
return v.Trim(true).Trim(false);
|
||||
}
|
||||
::TextInput* m_name{nullptr};
|
||||
::TextInput* m_expr{nullptr};
|
||||
DialogButtons* m_buttons{nullptr};
|
||||
};
|
||||
|
||||
void DesignPanel::on_add_variable()
|
||||
{
|
||||
wxString name = ::wxGetTextFromUser(_L("Variable name:"), _L("Add Variable"), "", this);
|
||||
if (name.IsEmpty()) return;
|
||||
name.Trim(true).Trim(false);
|
||||
DesignVariableDialog dlg(this, _L("Add Variable"), "", "0", true);
|
||||
if (dlg.ShowModal() != wxID_OK) return;
|
||||
const wxString name = dlg.name();
|
||||
const wxString expr = dlg.expression();
|
||||
if (name.IsEmpty() || expr.IsEmpty()) return;
|
||||
if (name.Contains(' ')) {
|
||||
set_status(StatusKind::Error, _L("Variable name must not contain spaces"));
|
||||
return;
|
||||
}
|
||||
wxString expr = ::wxGetTextFromUser(
|
||||
wxString::Format(_L("Expression for '%s':"), name),
|
||||
_L("Add Variable"), "0", this);
|
||||
if (expr.IsEmpty()) return;
|
||||
|
||||
const std::string name_str = name.ToUTF8().data();
|
||||
const std::string expr_str = expr.ToUTF8().data();
|
||||
@@ -11712,9 +11832,10 @@ void DesignPanel::on_edit_variable()
|
||||
}
|
||||
const std::string name_str = m_var_list->GetItemText(sel, 0).ToUTF8().data();
|
||||
const std::string old_expr = m_var_list->GetItemText(sel, 1).ToUTF8().data();
|
||||
wxString expr = ::wxGetTextFromUser(
|
||||
wxString::Format(_L("Expression for '%s':"), m_var_list->GetItemText(sel, 0)),
|
||||
_L("Edit Variable"), wxString::FromUTF8(old_expr), this);
|
||||
DesignVariableDialog dlg(this, _L("Edit Variable"), m_var_list->GetItemText(sel, 0),
|
||||
wxString::FromUTF8(old_expr), false);
|
||||
if (dlg.ShowModal() != wxID_OK) return;
|
||||
const wxString expr = dlg.expression();
|
||||
if (expr.IsEmpty()) return;
|
||||
|
||||
const std::string expr_str = expr.ToUTF8().data();
|
||||
|
||||
@@ -36,7 +36,7 @@ class wxSizer;
|
||||
class wxBoxSizer;
|
||||
class wxTextCtrl;
|
||||
class wxListCtrl;
|
||||
class wxButton;
|
||||
class Button;
|
||||
class wxPanel;
|
||||
class ScalableButton;
|
||||
|
||||
@@ -55,6 +55,10 @@ public:
|
||||
explicit DesignPanel(wxWindow* parent);
|
||||
void on_tab_shown(); // re-sync bed to the active printer when the Design tab is activated
|
||||
void on_tab_hidden(); // another tab took over: take the viewport status line down with us
|
||||
// From MainFrame, like the other pages: re-rasterise the icons at the new scale, and on a theme
|
||||
// switch also move every token colour onto the new theme.
|
||||
void msw_rescale();
|
||||
void on_sys_color_changed();
|
||||
void unbind_canvas_event_handlers(); // app close / language switch, from the plater's teardown
|
||||
void reset_canvas_volumes();
|
||||
void clear_document(); // New Project / Open Project: drop the document with the project
|
||||
@@ -296,6 +300,8 @@ public:
|
||||
// and the toolbar buttons.
|
||||
void menu_undo_redo(bool redo) { do_undo_redo(redo); }
|
||||
bool menu_can_undo_redo(bool redo) const;
|
||||
// Grey the app's Undo/Redo to this tab's history and gate (shown tab only).
|
||||
void update_undo_redo_buttons();
|
||||
private:
|
||||
// The plane the Hole tool drills on: a picked face (inward, centred) or the dropdown.
|
||||
SketchPlane hole_plane() const;
|
||||
@@ -500,19 +506,16 @@ private:
|
||||
// Unified Confirm/Cancel action bar (right end of the ribbon). Shown whenever any
|
||||
// tool or mode is active; the single confirm/cancel surface for the whole tab.
|
||||
wxSizer* m_tb_action{nullptr};
|
||||
// Persistent Undo/Redo group at the left of the ribbon — always visible, independent
|
||||
// of the mode-gated tool groups. The buttons are greyed per the document history and
|
||||
// the do_undo_redo gate (see update_undo_redo_buttons).
|
||||
wxSizer* m_tb_history{nullptr};
|
||||
ScalableButton* m_btn_undo{nullptr};
|
||||
ScalableButton* m_btn_redo{nullptr};
|
||||
void update_undo_redo_buttons(); // enable/disable Undo/Redo from can_undo/can_redo + gate
|
||||
// All tool buttons, for the active-tool teal highlight (Onshape-style).
|
||||
std::vector<ScalableButton*> m_tool_btns;
|
||||
ScalableButton* m_active_tool_btn{nullptr};
|
||||
void set_active_tool_btn(ScalableButton* b); // nullptr clears the highlight
|
||||
// Owns the themed DropDown flyouts (and the item vectors they hold by ref).
|
||||
std::vector<std::shared_ptr<void>> m_flyout_keepalive;
|
||||
// Icons that are not a plain ScalableButton face (flyout rows, card headers, the tree's image
|
||||
// list, theme-twinned buttons): each re-creates its bitmaps for the current scale and theme.
|
||||
std::vector<std::function<void()>> m_icon_refresh;
|
||||
void refresh_icons();
|
||||
wxCheckBox* m_construction{nullptr}; // sketch-mode construction toggle
|
||||
wxSpinCtrlDouble* m_move_dx{nullptr}; // Move/Rotate card: world translation
|
||||
wxSpinCtrlDouble* m_move_dy{nullptr};
|
||||
@@ -646,7 +649,7 @@ private:
|
||||
|
||||
// Delete Face controls (remove faces, heal the solid).
|
||||
ComboBox* m_del_face_body{nullptr}; // target body
|
||||
wxButton* m_del_face_add_btn{nullptr}; // "Add picked face" button
|
||||
::Button* m_del_face_add_btn{nullptr}; // "Add picked face" button
|
||||
wxStaticText* m_del_face_list{nullptr}; // shows the accumulated face ids
|
||||
std::vector<int> m_del_faces; // accumulated face list
|
||||
|
||||
@@ -671,8 +674,8 @@ private:
|
||||
// Expression binding (per-feature, visible during edit only)
|
||||
ComboBox* m_expr_field{nullptr}; // field-name picker (editable)
|
||||
wxTextCtrl* m_expr_text{nullptr}; // expression string
|
||||
wxButton* m_expr_set_btn{nullptr}; // Apply / bind
|
||||
wxButton* m_expr_clear_btn{nullptr}; // Remove binding
|
||||
::Button* m_expr_set_btn{nullptr}; // Apply / bind
|
||||
::Button* m_expr_clear_btn{nullptr}; // Remove binding
|
||||
wxStaticText* m_expr_status{nullptr}; // shows current bindings for the edited feature
|
||||
void populate_expr_fields(Tool t); // fill m_expr_field from feature-type fields
|
||||
void on_set_expr(); // checkpoint + write -> recompute -> undo on fail
|
||||
@@ -681,9 +684,9 @@ private:
|
||||
// Document variables panel (below the feature tree / parts)
|
||||
StaticBox* m_var_box{nullptr};
|
||||
wxListCtrl* m_var_list{nullptr};
|
||||
wxButton* m_btn_add_var{nullptr};
|
||||
wxButton* m_btn_edit_var{nullptr};
|
||||
wxButton* m_btn_del_var{nullptr};
|
||||
ScalableButton* m_btn_add_var{nullptr};
|
||||
ScalableButton* m_btn_edit_var{nullptr};
|
||||
ScalableButton* m_btn_del_var{nullptr};
|
||||
void refresh_variables(); // rebuild m_var_list from m_doc.variables
|
||||
void on_add_variable();
|
||||
void on_edit_variable();
|
||||
@@ -721,10 +724,10 @@ private:
|
||||
ComboBox* m_plane_tilt_axis{nullptr}; // 0 = base X, 1 = base Y
|
||||
// Plane construction method + contextual face/edge reference picks (Onshape/Fusion parity).
|
||||
ComboBox* m_plane_type{nullptr}; // PlaneType: Offset/Angle/Midplane/Tangent/TwoEdges/Coincident
|
||||
wxButton* m_plane_pick_faceA{nullptr}; wxStaticText* m_plane_faceA_lbl{nullptr};
|
||||
wxButton* m_plane_pick_faceB{nullptr}; wxStaticText* m_plane_faceB_lbl{nullptr};
|
||||
wxButton* m_plane_pick_edgeA{nullptr}; wxStaticText* m_plane_edgeA_lbl{nullptr};
|
||||
wxButton* m_plane_pick_edgeB{nullptr}; wxStaticText* m_plane_edgeB_lbl{nullptr};
|
||||
::Button* m_plane_pick_faceA{nullptr}; wxStaticText* m_plane_faceA_lbl{nullptr};
|
||||
::Button* m_plane_pick_faceB{nullptr}; wxStaticText* m_plane_faceB_lbl{nullptr};
|
||||
::Button* m_plane_pick_edgeA{nullptr}; wxStaticText* m_plane_edgeA_lbl{nullptr};
|
||||
::Button* m_plane_pick_edgeB{nullptr}; wxStaticText* m_plane_edgeB_lbl{nullptr};
|
||||
wxSpinCtrlDouble* m_plane_usize{nullptr}; // datum rectangle extent u (mm) — also driven by drag handles
|
||||
wxSpinCtrlDouble* m_plane_vsize{nullptr}; // datum rectangle extent v (mm)
|
||||
// Captured references for the candidate datum (body index + face/edge index, -1 = none).
|
||||
@@ -847,8 +850,8 @@ private:
|
||||
|
||||
// Axis controls (datum axis: line through two points or derived from geometry).
|
||||
ComboBox* m_axis_type{nullptr}; // AxisType: TwoPoints/FaceNormal/CylinderCenterline/PlaneIntersection/AlongEdge
|
||||
wxButton* m_axis_pick_face{nullptr}; wxStaticText* m_axis_face_lbl{nullptr};
|
||||
wxButton* m_axis_pick_edge{nullptr}; wxStaticText* m_axis_edge_lbl{nullptr};
|
||||
::Button* m_axis_pick_face{nullptr}; wxStaticText* m_axis_face_lbl{nullptr};
|
||||
::Button* m_axis_pick_edge{nullptr}; wxStaticText* m_axis_edge_lbl{nullptr};
|
||||
ComboBox* m_axis_plane_a{nullptr};
|
||||
ComboBox* m_axis_plane_b{nullptr};
|
||||
wxSpinCtrlDouble* m_axis_p1x{nullptr}; wxSpinCtrlDouble* m_axis_p1y{nullptr}; wxSpinCtrlDouble* m_axis_p1z{nullptr};
|
||||
@@ -861,8 +864,8 @@ private:
|
||||
ComboBox* m_coordsys_type{nullptr}; // CoordSysType: PointWorld/FaceAndDirection
|
||||
ComboBox* m_cs_body{nullptr}; // body-focus chooser: restrict picking to one body
|
||||
wxSpinCtrlDouble* m_cs_x{nullptr}; wxSpinCtrlDouble* m_cs_y{nullptr}; wxSpinCtrlDouble* m_cs_z{nullptr};
|
||||
wxButton* m_cs_pick_face{nullptr}; wxStaticText* m_cs_face_lbl{nullptr};
|
||||
wxButton* m_cs_pick_edge{nullptr}; wxStaticText* m_cs_edge_lbl{nullptr};
|
||||
::Button* m_cs_pick_face{nullptr}; wxStaticText* m_cs_face_lbl{nullptr};
|
||||
::Button* m_cs_pick_edge{nullptr}; wxStaticText* m_cs_edge_lbl{nullptr};
|
||||
wxSpinCtrlDouble* m_cs_hx{nullptr}; wxSpinCtrlDouble* m_cs_hy{nullptr}; wxSpinCtrlDouble* m_cs_hz{nullptr};
|
||||
int m_cs_face_body{-1}, m_cs_face{-1};
|
||||
int m_cs_edge_body{-1}, m_cs_edge{-1};
|
||||
@@ -934,7 +937,6 @@ private:
|
||||
// m_status's foreground as created, captured before any caller touches it. Callers signal
|
||||
// "no opinion" by setting wxNullColour, which restores exactly this — so it is the only
|
||||
// reliable way to tell a chosen colour (the error red) from the default. See set_status().
|
||||
wxColour m_status_default_fg;
|
||||
// The guidance sentence for the step the armed sketch tool is on, kept so a transient
|
||||
// readout (the live length/angle while a segment is being dragged) can be appended to it
|
||||
// instead of replacing it — the guidance used to vanish on the first mouse move after a
|
||||
@@ -950,7 +952,7 @@ private:
|
||||
bool m_dof_last_has{false};
|
||||
int m_feature_counter{0};
|
||||
|
||||
std::vector<wxButton*> m_confirm_btns;
|
||||
std::vector<::Button*> m_confirm_btns;
|
||||
|
||||
// Edit-in-place state: add-mode is m_edit_index == -1. Single-feature edit
|
||||
// (Sketch or Extrude independently) uses only m_edit_index as the row to replace.
|
||||
|
||||
@@ -8988,6 +8988,8 @@ void DesignSketchTool::render(GLCanvas3D& canvas)
|
||||
// not there — there is no window to fall back to any more.
|
||||
if (inline_editor != nullptr)
|
||||
inline_editor->render(*wxGetApp().imgui(), m_render_scale);
|
||||
if (render_overlays)
|
||||
render_overlays();
|
||||
(void)canvas;
|
||||
if (!has_display()) {
|
||||
if (on_readout) on_readout(std::string()); // nothing to show -> hide HUD
|
||||
@@ -10510,9 +10512,9 @@ bool DesignSketchTool::on_mouse_impl(wxMouseEvent& evt, GLCanvas3D& canvas)
|
||||
// Left-drag rubber band -> whole body. Past the click budget the press becomes a sweep:
|
||||
// the rectangle is anchored at the ORIGINAL press point (not at the frame where the
|
||||
// threshold was crossed, which would lose the first few pixels) and the events are
|
||||
// consumed from here on. Left-drag no longer orbits in this canvas — DesignCanvas puts
|
||||
// orbit on middle-drag and pan on right-drag, the CAD convention — so nothing downstream
|
||||
// is being starved of a gesture it used to own.
|
||||
// consumed from here on. The camera follows Preferences > Control as in Prepare, so while
|
||||
// left-drag is given to Rotate or Pan the band takes Shift+left-drag, Prepare's own
|
||||
// rectangle selection.
|
||||
// HOVER PRE-HIGHLIGHT (9xw part 3): say what a click would take, before it is
|
||||
// taken. Plain motion only — no button down, no band running — because during a drag the
|
||||
// pointer is doing something else and a promise about clicking would be a lie. Returns
|
||||
@@ -10522,7 +10524,9 @@ bool DesignSketchTool::on_mouse_impl(wxMouseEvent& evt, GLCanvas3D& canvas)
|
||||
if (update_solid_hover(canvas, evt)) canvas.set_as_dirty();
|
||||
return false;
|
||||
}
|
||||
if (evt.Dragging() && evt.LeftIsDown() && m_pick_pending) {
|
||||
const bool left_drag_sweeps =
|
||||
evt.ShiftDown() || std::atoi(wxGetApp().app_config->get("left_mouse_drag_action").c_str()) == 0;
|
||||
if (evt.Dragging() && evt.LeftIsDown() && m_pick_pending && (left_drag_sweeps || m_rubber.is_dragging())) {
|
||||
if (!m_rubber.is_dragging()) {
|
||||
if (std::max(std::abs(evt.GetX() - m_pick_press_x),
|
||||
std::abs(evt.GetY() - m_pick_press_y)) <= 8)
|
||||
|
||||
@@ -154,6 +154,8 @@ public:
|
||||
// The in-canvas value field, drawn by render() before any early return. Owned by
|
||||
// DesignCanvas; null until it sets it. Not a window — see SketchInlineEditor.hpp.
|
||||
class SketchInlineEditor* inline_editor{nullptr};
|
||||
// The canvas's own overlays (its status and readout chips), drawn in this tool's ImGui pass.
|
||||
std::function<void()> render_overlays;
|
||||
|
||||
// Persistent committed sketches to draw even when no session is active (e.g. an
|
||||
// un-consumed sketch left visible after its extrude is removed). Each carries its
|
||||
@@ -1244,9 +1246,8 @@ private:
|
||||
// change) read honestly — the change has to be predictable before the click, not only after.
|
||||
SolidPick m_pre; // what the pointer is currently over (kind None = nothing)
|
||||
bool update_solid_hover(GLCanvas3D& canvas, const wxMouseEvent& evt); // true when it changed
|
||||
// Left-drag rubber band: sweep a rectangle over the plate to take a whole body. Orbit
|
||||
// moves to middle-drag in this canvas (DesignCanvas::set_cad_navigation) so the left
|
||||
// button is free for it, which is the CAD convention (Onshape/SolidWorks).
|
||||
// Left-drag rubber band: sweep a rectangle over the plate to take a whole body. While
|
||||
// Preferences give left-drag to the camera it takes Shift+left-drag, as in Prepare.
|
||||
GLSelectionRectangle m_rubber;
|
||||
void pick_bodies_in_rectangle(); // resolve the swept rectangle -> whole-body selection
|
||||
bool on_mouse_impl(wxMouseEvent& evt, GLCanvas3D& canvas); // the body; on_mouse wraps it
|
||||
|
||||
@@ -3,17 +3,18 @@
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/Widgets/DialogButtons.hpp"
|
||||
#include "slic3r/GUI/Widgets/TextInput.hpp"
|
||||
#include "slic3r/GUI/Widgets/ComboBox.hpp"
|
||||
#include "slic3r/GUI/Widgets/CheckBox.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
#include "slic3r/Utils/WxFontUtils.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "libslic3r/Emboss.hpp"
|
||||
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/fontenum.h>
|
||||
#include <wx/graphics.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/textctrl.h>
|
||||
|
||||
@@ -25,11 +26,31 @@ namespace Slic3r { namespace GUI {
|
||||
static const char* kFontKey = "cad_text_font"; // WxFontUtils::store_wxFont descriptor
|
||||
static const char* kHeightKey = "cad_text_height"; // mm
|
||||
|
||||
DesignTextDialog::DesignTextDialog(wxWindow* parent, const Spec* initial)
|
||||
: DPIDialog(parent, wxID_ANY, _L("Text"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
|
||||
// Installed scalable faces, sorted, enumerated once per session: listing them takes long enough on
|
||||
// a machine with many fonts to be felt each time the dialog opened.
|
||||
static const wxArrayString& font_faces()
|
||||
{
|
||||
SetFont(wxGetApp().normal_font());
|
||||
SetBackgroundColour(wxGetApp().dark_mode() ? wxColour(0x2d, 0x2d, 0x31) : *wxWHITE);
|
||||
static wxArrayString faces = [] {
|
||||
// '@' faces are the vertical-writing aliases GTK/MSW list twice.
|
||||
wxArrayString f = wxFontEnumerator::GetFacenames(wxFONTENCODING_SYSTEM, false);
|
||||
f.erase(std::remove_if(f.begin(), f.end(), [](const wxString& n) { return n.StartsWith("@"); }), f.end());
|
||||
f.Sort();
|
||||
return f;
|
||||
}();
|
||||
return faces;
|
||||
}
|
||||
|
||||
static bool parse_mm(wxString t, double& out)
|
||||
{
|
||||
t.Replace(",", ".");
|
||||
return t.Trim(true).Trim(false).ToCDouble(&out) && out >= 0.5 && out <= 500.0;
|
||||
}
|
||||
|
||||
DesignTextDialog::DesignTextDialog(wxWindow* parent, const Spec* initial)
|
||||
: DPIDialog(parent, wxID_ANY, _L("Text"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
||||
{
|
||||
SetFont(Label::Body_14);
|
||||
SetBackgroundColour(*wxWHITE); // light palette colour, dark-mapped by UpdateDlgDarkUI
|
||||
const int em = em_unit();
|
||||
|
||||
// The text being edited, else the last used font and height, else the GUI font at 10 mm.
|
||||
@@ -50,41 +71,47 @@ DesignTextDialog::DesignTextDialog(wxWindow* parent, const Spec* initial)
|
||||
try { height = std::clamp(std::stod(h), 0.5, 500.0); } catch (...) {}
|
||||
}
|
||||
}
|
||||
m_last_height = height;
|
||||
|
||||
auto* form = new wxFlexGridSizer(2, em / 2, em);
|
||||
form->AddGrowableCol(1, 1);
|
||||
auto label = [this, form](const wxString& text) {
|
||||
form->Add(new wxStaticText(this, wxID_ANY, text), 0, wxALIGN_CENTER_VERTICAL);
|
||||
};
|
||||
|
||||
m_text = new wxTextCtrl(this, wxID_ANY, initial ? initial->text : wxString(), wxDefaultPosition,
|
||||
wxSize(30 * em, -1), wxTE_PROCESS_ENTER);
|
||||
m_text->SetHint(_L("Type the text to insert"));
|
||||
form->Add(new wxStaticText(this, wxID_ANY, _L("Text")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_text = new ::TextInput(this, initial ? initial->text : wxString(), "", "", wxDefaultPosition,
|
||||
wxSize(30 * em, -1), wxTE_PROCESS_ENTER);
|
||||
m_text->GetTextCtrl()->SetHint(_L("Type the text to insert"));
|
||||
label(_L("Text"));
|
||||
form->Add(m_text, 1, wxEXPAND);
|
||||
|
||||
// Scalable faces only ('@' faces are the vertical-writing aliases GTK/MSW list twice).
|
||||
wxArrayString faces = wxFontEnumerator::GetFacenames(wxFONTENCODING_SYSTEM, false);
|
||||
faces.erase(std::remove_if(faces.begin(), faces.end(), [](const wxString& f) { return f.StartsWith("@"); }),
|
||||
faces.end());
|
||||
faces.Sort();
|
||||
m_face = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, faces);
|
||||
int sel = m_face->FindString(font.GetFaceName());
|
||||
m_face = new ::ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(30 * em, -1), 0, nullptr,
|
||||
wxCB_READONLY);
|
||||
const wxArrayString& faces = font_faces();
|
||||
for (const wxString& f : faces)
|
||||
m_face->Append(f);
|
||||
const int sel = faces.Index(font.GetFaceName());
|
||||
m_face->SetSelection(sel != wxNOT_FOUND ? sel : 0);
|
||||
form->Add(new wxStaticText(this, wxID_ANY, _L("Font")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
label(_L("Font"));
|
||||
form->Add(m_face, 1, wxEXPAND);
|
||||
|
||||
// Orca's CheckBox carries no label: each one sits beside its own text.
|
||||
auto* style = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_bold = new wxCheckBox(this, wxID_ANY, _L("Bold"));
|
||||
m_italic = new wxCheckBox(this, wxID_ANY, _L("Italic"));
|
||||
m_bold->SetValue(WxFontUtils::is_bold(font));
|
||||
m_italic->SetValue(WxFontUtils::is_italic(font));
|
||||
style->Add(m_bold, 0, wxRIGHT, em);
|
||||
style->Add(m_italic, 0);
|
||||
auto check = [this, style, em](const wxString& text, bool value) {
|
||||
auto* c = new ::CheckBox(this);
|
||||
c->SetValue(value);
|
||||
style->Add(c, 0, wxALIGN_CENTER_VERTICAL);
|
||||
style->Add(new wxStaticText(this, wxID_ANY, text), 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, em / 2);
|
||||
return c;
|
||||
};
|
||||
m_bold = check(_L("Bold"), WxFontUtils::is_bold(font));
|
||||
m_italic = check(_L("Italic"), WxFontUtils::is_italic(font));
|
||||
form->AddSpacer(0);
|
||||
form->Add(style, 0);
|
||||
|
||||
m_height = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(10 * em, -1),
|
||||
wxSP_ARROW_KEYS, 0.5, 500.0, height, 0.5);
|
||||
m_height->SetDigits(1);
|
||||
form->Add(new wxStaticText(this, wxID_ANY, _L("Height (mm)")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_height = new ::TextInput(this, wxString::FromCDouble(height, 1), _L("mm"), "", wxDefaultPosition,
|
||||
wxSize(10 * em, -1), wxTE_PROCESS_ENTER);
|
||||
label(_L("Height"));
|
||||
form->Add(m_height, 0);
|
||||
|
||||
// The outline of what will be inserted, fitted to the box, with its real size under it.
|
||||
@@ -104,26 +131,21 @@ DesignTextDialog::DesignTextDialog(wxWindow* parent, const Spec* initial)
|
||||
top->Add(m_size, 0, wxLEFT | wxRIGHT | wxTOP, em);
|
||||
top->Add(buttons, 0, wxEXPAND);
|
||||
|
||||
m_text->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update_preview(); });
|
||||
m_text->Bind(wxEVT_TEXT, [this](wxCommandEvent& e) { update_preview(); e.Skip(); });
|
||||
m_text->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) { accept(); }); // Enter = OK, as everywhere in the tab
|
||||
auto refont = [this](wxCommandEvent&) { load_font(); update_preview(); };
|
||||
m_face->Bind(wxEVT_CHOICE, refont);
|
||||
m_bold->Bind(wxEVT_CHECKBOX, refont);
|
||||
m_italic->Bind(wxEVT_CHECKBOX, refont);
|
||||
m_height->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { update_preview(); });
|
||||
m_height->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update_preview(); });
|
||||
Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) {
|
||||
if (e.GetId() == wxID_OK) accept();
|
||||
else if (e.GetId() == wxID_CANCEL) cancel();
|
||||
else e.Skip();
|
||||
});
|
||||
m_face->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& e) { load_font(); update_preview(); e.Skip(); });
|
||||
for (::CheckBox* c : {m_bold, m_italic})
|
||||
c->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& e) { e.Skip(); CallAfter([this] { load_font(); update_preview(); }); });
|
||||
m_height->Bind(wxEVT_TEXT, [this](wxCommandEvent& e) { update_preview(); e.Skip(); });
|
||||
m_height->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) { accept(); });
|
||||
buttons->GetOK()->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); });
|
||||
buttons->GetCANCEL()->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { cancel(); });
|
||||
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent&) { cancel(); });
|
||||
Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& e) {
|
||||
if (e.GetKeyCode() == WXK_ESCAPE) cancel();
|
||||
else e.Skip();
|
||||
});
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
SetSizerAndFit(top);
|
||||
// Out of the middle of the window, where the text is being placed: top right of the parent.
|
||||
if (parent != nullptr) {
|
||||
@@ -132,17 +154,26 @@ DesignTextDialog::DesignTextDialog(wxWindow* parent, const Spec* initial)
|
||||
} else {
|
||||
CenterOnParent();
|
||||
}
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
|
||||
load_font();
|
||||
update_preview();
|
||||
m_text->SetFocus();
|
||||
m_text->GetTextCtrl()->SetFocus();
|
||||
}
|
||||
|
||||
wxString DesignTextDialog::text() const { return m_text->GetValue(); }
|
||||
double DesignTextDialog::height_mm() const
|
||||
{
|
||||
double h = 0.0;
|
||||
if (parse_mm(m_height->GetTextCtrl()->GetValue(), h))
|
||||
const_cast<DesignTextDialog*>(this)->m_last_height = h;
|
||||
return m_last_height;
|
||||
}
|
||||
|
||||
wxString DesignTextDialog::text() const { return m_text->GetTextCtrl()->GetValue(); }
|
||||
|
||||
DesignTextDialog::Spec DesignTextDialog::spec() const
|
||||
{
|
||||
return { m_text->GetValue(), WxFontUtils::store_wxFont(current_font()), m_height->GetValue() };
|
||||
return { text(), WxFontUtils::store_wxFont(current_font()), height_mm() };
|
||||
}
|
||||
|
||||
void DesignTextDialog::accept()
|
||||
@@ -151,7 +182,7 @@ void DesignTextDialog::accept()
|
||||
m_done = true;
|
||||
if (AppConfig* cfg = wxGetApp().app_config) {
|
||||
cfg->set(kFontKey, WxFontUtils::store_wxFont(current_font()));
|
||||
cfg->set(kHeightKey, std::to_string(m_height->GetValue()));
|
||||
cfg->set(kHeightKey, std::to_string(height_mm()));
|
||||
}
|
||||
if (on_accept) on_accept();
|
||||
}
|
||||
@@ -167,7 +198,7 @@ wxFont DesignTextDialog::current_font() const
|
||||
{
|
||||
wxFontInfo info(12);
|
||||
if (m_face->GetSelection() != wxNOT_FOUND)
|
||||
info.FaceName(m_face->GetStringSelection());
|
||||
info.FaceName(m_face->GetString(m_face->GetSelection()));
|
||||
info.Bold(m_bold->GetValue()).Italic(m_italic->GetValue());
|
||||
return wxFont(info);
|
||||
}
|
||||
@@ -182,10 +213,10 @@ void DesignTextDialog::load_font()
|
||||
|
||||
void DesignTextDialog::update_preview()
|
||||
{
|
||||
const std::string utf8(m_text->GetValue().ToUTF8().data());
|
||||
const std::string utf8(text().ToUTF8().data());
|
||||
m_regions.clear();
|
||||
if (m_font_file && !utf8.empty())
|
||||
m_regions = text_to_regions(utf8, m_height->GetValue(), m_font_file);
|
||||
m_regions = text_to_regions(utf8, height_mm(), m_font_file);
|
||||
|
||||
wxString line;
|
||||
if (!m_font_file)
|
||||
@@ -250,7 +281,20 @@ void DesignTextDialog::draw_preview(wxWindow* canvas)
|
||||
|
||||
void DesignTextDialog::on_dpi_changed(const wxRect&)
|
||||
{
|
||||
Fit();
|
||||
m_text->Rescale();
|
||||
m_face->Rescale();
|
||||
m_height->Rescale();
|
||||
m_bold->Rescale();
|
||||
m_italic->Rescale();
|
||||
GetSizer()->SetSizeHints(this);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void DesignTextDialog::on_sys_color_changed()
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
m_preview->Refresh(); // its colours are read at paint time
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,10 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class wxTextCtrl;
|
||||
class wxChoice;
|
||||
class wxCheckBox;
|
||||
class wxSpinCtrlDouble;
|
||||
class wxStaticText;
|
||||
class TextInput;
|
||||
class ComboBox;
|
||||
class CheckBox;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Emboss { struct FontFile; }
|
||||
@@ -48,6 +47,7 @@ public:
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
void on_sys_color_changed() override;
|
||||
|
||||
private:
|
||||
void load_font(); // m_face/m_bold/m_italic -> m_font_file
|
||||
@@ -58,11 +58,14 @@ private:
|
||||
void cancel();
|
||||
bool m_done{false}; // accept/cancel fire once
|
||||
|
||||
wxTextCtrl* m_text{nullptr};
|
||||
wxChoice* m_face{nullptr};
|
||||
wxCheckBox* m_bold{nullptr};
|
||||
wxCheckBox* m_italic{nullptr};
|
||||
wxSpinCtrlDouble* m_height{nullptr};
|
||||
double height_mm() const; // the typed height, or the last valid one while it is being typed
|
||||
|
||||
::TextInput* m_text{nullptr};
|
||||
::ComboBox* m_face{nullptr};
|
||||
::CheckBox* m_bold{nullptr};
|
||||
::CheckBox* m_italic{nullptr};
|
||||
::TextInput* m_height{nullptr}; // mm; Orca's SpinInput is integer-only
|
||||
double m_last_height{10.0};
|
||||
wxWindow* m_preview{nullptr};
|
||||
wxStaticText* m_size{nullptr};
|
||||
wxWindow* m_ok{nullptr};
|
||||
|
||||
@@ -4846,8 +4846,6 @@ bool GLCanvas3D::is_camera_rotate(const wxMouseEvent& evt, const std::map<MouseB
|
||||
{
|
||||
if (m_is_touchpad_navigation) {
|
||||
return evt.Moving() && evt.AltDown() && !evt.ShiftDown();
|
||||
} else if (m_cad_navigation) {
|
||||
return evt.Dragging() && evt.MiddleIsDown(); // left-drag is the selection rubber band
|
||||
} else {
|
||||
return evt.Dragging() && clicked_button_matches_action(evt, MouseAction::Rotation, mappings);
|
||||
}
|
||||
@@ -4857,8 +4855,6 @@ bool GLCanvas3D::is_camera_pan(const wxMouseEvent& evt, const std::map<MouseButt
|
||||
{
|
||||
if (m_is_touchpad_navigation) {
|
||||
return evt.Moving() && evt.ShiftDown() && !evt.AltDown();
|
||||
} else if (m_cad_navigation) {
|
||||
return evt.Dragging() && evt.RightIsDown(); // middle now orbits, so pan is right only
|
||||
} else {
|
||||
return evt.Dragging() && clicked_button_matches_action(evt, MouseAction::Pan, mappings);
|
||||
;
|
||||
|
||||
@@ -617,11 +617,6 @@ private:
|
||||
std::array<unsigned int, 2> m_old_size{ 0, 0 };
|
||||
|
||||
bool m_is_touchpad_navigation{ false };
|
||||
// CAD navigation (Design tab only): left-drag is a selection rubber band, so orbit moves
|
||||
// to middle-drag and pan to right-drag — the Onshape/SolidWorks mapping. Off everywhere
|
||||
// else, so Prepare/Preview keep the mouse the user already learned.
|
||||
bool m_cad_navigation{ false };
|
||||
|
||||
// Screen is only refreshed from the OnIdle handler if it is dirty.
|
||||
bool m_dirty;
|
||||
// A frame is needed, and only for the overlay.
|
||||
@@ -1144,7 +1139,6 @@ public:
|
||||
bool clicked_button_matches_action(const wxMouseEvent& evt, MouseAction action, const std::map<MouseButton, MouseAction>& mappings) const;
|
||||
bool is_camera_rotate(const wxMouseEvent& evt, const std::map<MouseButton, MouseAction>& mappings) const;
|
||||
bool is_camera_pan(const wxMouseEvent& evt, const std::map<MouseButton, MouseAction>& mappings) const;
|
||||
void set_cad_navigation(bool b) { m_cad_navigation = b; }
|
||||
|
||||
Size get_canvas_size() const;
|
||||
Vec2d get_local_mouse_position() const;
|
||||
|
||||
@@ -1320,6 +1320,15 @@ void MainFrame::show_option(bool show)
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::set_undo_redo_enabled(bool undo, bool redo)
|
||||
{
|
||||
#ifndef __APPLE__
|
||||
m_topbar->EnableUndoRedo(undo, redo);
|
||||
#else
|
||||
(void) undo; (void) redo; // macOS has no top bar; Edit asks the tab when it opens
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_CAD
|
||||
DesignPanel* MainFrame::shown_design_panel() const
|
||||
{
|
||||
@@ -1393,6 +1402,11 @@ void MainFrame::init_tabpanel() {
|
||||
m_topbar->DisableUndoRedoItems();
|
||||
}
|
||||
#endif
|
||||
#ifdef SLIC3R_CAD
|
||||
// Design keeps its own history, and the top bar's Undo/Redo drive it while it is shown.
|
||||
if (m_design_page != nullptr && panel == m_design_page)
|
||||
DesignPanel::ensure()->update_undo_redo_buttons();
|
||||
#endif
|
||||
|
||||
if (panel)
|
||||
panel->SetFocus();
|
||||
@@ -2612,6 +2626,9 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect)
|
||||
MonitorPanel::when_built([](MonitorPanel& monitor) { monitor.msw_rescale(); });
|
||||
MultiMachinePage::when_built([](MultiMachinePage& multi_machine) { multi_machine.msw_rescale(); });
|
||||
CalibrationPanel::when_built([](CalibrationPanel& calibration) { calibration.msw_rescale(); });
|
||||
#ifdef SLIC3R_CAD
|
||||
DesignPanel::when_built([](DesignPanel& design) { design.msw_rescale(); });
|
||||
#endif
|
||||
|
||||
// BBS
|
||||
#if 0
|
||||
@@ -2676,6 +2693,9 @@ void MainFrame::on_sys_color_changed()
|
||||
wxGetApp().plater()->sys_color_changed();
|
||||
MonitorPanel::when_built([](MonitorPanel& monitor) { monitor.on_sys_color_changed(); });
|
||||
CalibrationPanel::when_built([](CalibrationPanel& calibration) { calibration.on_sys_color_changed(); });
|
||||
#ifdef SLIC3R_CAD
|
||||
DesignPanel::when_built([](DesignPanel& design) { design.on_sys_color_changed(); });
|
||||
#endif
|
||||
// update Tabs
|
||||
for (auto tab : wxGetApp().tabs_list)
|
||||
tab->sys_color_changed();
|
||||
|
||||
@@ -499,6 +499,8 @@ public:
|
||||
// the tab that is shown: its own history when that is Design, the plater's otherwise.
|
||||
DesignPanel* shown_design_panel() const;
|
||||
#endif
|
||||
// The top bar's Undo/Redo, for a tab that keeps its own history (Design).
|
||||
void set_undo_redo_enabled(bool undo, bool redo);
|
||||
//BBS: GUI refactor
|
||||
LazyPage<MonitorPanel>* m_monitor_page{ nullptr };
|
||||
|
||||
|
||||