1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//! Wires `super::super::retarget` (the "Retarget for material" proposal
//! builder/applier) and `ui/components/retarget_dialog.slint` into the Edit tab --
//! `setup_*` functions, one per `RetargetDialog`/`EditorView` callback, mirroring
//! `solve_actions`'s Optimize callbacks in shape.
//!
//! # Where the target material comes from
//!
//! [`material::resolve_target_selection`] resolves the proposal's TARGET from
//! `RetargetModel`'s OWN `target_material_index`/`target_ri_override_text`
//! properties, NOT from `state.design.material` or the design settings panel's
//! combo: this dialog owns its target end to end, seeded from the design's current
//! material on `open()` (see [`proposal::setup_retarget_open_callback`]) and changed
//! only by this dialog's own picker. `retarget::apply` still only ever produces
//! `Edit::RetargetAngles` -- see [`apply::setup_retarget_apply_callback`] for how a
//! target material change actually reaches `design.material`.
//!
//! # `RetargetMode::Optimize` runs off the UI thread
//!
//! Shift mode is pure angle arithmetic and stays synchronous. `RetargetMode::Optimize`
//! runs a real `optimize_design` search, so
//! [`proposal::setup_retarget_proposal_changed_callback`] hands it to
//! [`super::super::optimize_solve::spawn_optimize_solve`] -- the exact same
//! worker/cancel/progress machinery the standalone Optimize panel uses
//! (`solve_actions::setup_optimize_callback`) -- rather than blocking the UI thread for
//! however long the search takes. [`RETARGET_ASYNC`] is this dialog's own
//! module-local run tracker: deliberately NOT a new `EditorState` field (that group is
//! owned elsewhere) and NOT threaded through any `setup_retarget_*` call site (`gui::
//! editor::mod`'s wiring is likewise owned elsewhere) -- a `thread_local!` is sound
//! here because Slint's event loop is single-threaded, exactly like `EditorState`'s own
//! `RefCell`. The worker's completion handler is `Send` (a hard requirement of
//! [`super::super::optimize_solve::spawn_optimize_solve`]'s own signature) so it cannot
//! capture `Rc<RefCell<EditorState>>` directly; it stashes the finished proposal into
//! [`RETARGET_ASYNC`] instead, and [`apply::setup_retarget_apply_callback`] reads it
//! back out on the main thread exactly like it already reads
//! `EditorState::pending_retarget` for a Shift-mode proposal.
//!
//! # Slint-free view-model split, for testability
//!
//! [`proposal_view::retarget_view`]/[`proposal_view::row_view`]/
//! [`material::resolve_target_selection`]/[`apply::apply_pending_retarget`] take and
//! return only plain Rust types, so this group's `tests` module can exercise the
//! actual decision logic directly -- `proposal_view::push_retarget_view`/
//! `proposal_view::push_target_readout` are the only two functions that touch a
//! Slint type, thin enough not to need tests.
//!
//! # Module layout
//!
//! Split by concern: [`material`] (target material resolution/display),
//! [`proposal_view`] (the Slint-free proposal view model and its `push_*`
//! renderers), [`proposal`] (the open/proposal-changed callbacks and Shift mode's
//! own synchronous rebuild), [`optimize_run`] (`RetargetMode::Optimize`'s off-thread
//! search), [`apply`] (committing a pending proposal, and closing the dialog), and
//! [`snapshot`] (the separate "Snapshot Design"/"Compare to Snapshot" feature that
//! also lives in this dialog). [`RETARGET_ASYNC`] and [`apply_ghost_preview_or_revert`]
//! are shared by more than one of those siblings, so they stay here rather than in
//! any one of them. [`compare_hooks`] is the narrow read-only surface the visual
//! before/after compare window (`gui::editor::compare`) reaches this dialog's pending
//! proposal and snapshot through -- it never commits anything itself; its "Keep
//! after" invokes `RetargetModel.apply()`, i.e. [`apply::setup_retarget_apply_callback`].
pub use ;
pub use ;
pub use ;
pub use ;
use ;
use crate::;
use Design;
use ComponentHandle;
use ;
thread_local!
/// [`RETARGET_ASYNC`]'s payload.
/// Points the dialog's embedded comparison pane (`MainWindow`'s own `CompareModel`
/// instance, handled in `gui::editor::compare`) at the proposal just built:
/// `solved` re-opens the embedded session on it, otherwise the session is dropped so
/// the pane never keeps showing a proposal that no longer exists. The handlers read
/// the editor state, so callers must not hold a borrow of it across this call.
/// Shows `design` retargeted by `proposal` as a ghost overlay
/// in the shared solid viewport, iff `RetargetModel.preview_enabled` is on AND
/// `proposal` is `Some` (a `None` proposal -- an anchored-tier refusal, a solve
/// error -- has nothing to preview). Returns whether the candidate was queued on the
/// background solve worker: the viewport applies the ghost when that solve lands and
/// drops it if a newer request superseded it. The caller resubmits the real, live
/// design through the ordinary [`view::submit_preview_replan`] path when it wasn't
/// queued (see both call sites).
///
/// Builds the candidate via [`Design::apply_edit`] on a clone -- explicitly
/// documented as safe for exactly this ("usable standalone by a caller that wants
/// edit/undo without a `History` stack") -- rather than
/// [`super::super::state::EditorState::apply`], so this never touches
/// `History`/`is_dirty`/the design generation: a preview must never look like a
/// real edit to anything else in this crate.
/// `design` with `proposal`'s angle retarget applied, on a clone -- the candidate
/// geometry both the viewport ghost ([`apply_ghost_preview_or_revert`]) and the
/// visual compare window's "after" side (`gui::editor::compare`, via
/// [`compare_hooks::retarget_compare_inputs`]) show. `None` when the retarget edit
/// no longer applies to `design` (a stale tier index).
///
/// [`Design::apply_edit`] on a clone, never [`super::super::state::EditorState::apply`]:
/// see [`apply_ghost_preview_or_revert`]'s own doc comment for why a preview must
/// never touch `History`. Only the angles move here -- the target MATERIAL is the
/// caller's to attach when it matters (the compare window does; the solid ghost
/// has no material to show).
pub