Skip to content

Commit ad609d2

Browse files
committed
refactoring
1 parent 2c03e3b commit ad609d2

6 files changed

Lines changed: 748 additions & 746 deletions

File tree

src/app/points_state.rs

Lines changed: 96 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub(super) struct PointsEditorState {
1212
pub(super) text: String,
1313
pub(super) cache: Option<ParsedPointsCache>,
1414
pub(super) cache_dirty: bool,
15+
pub(super) text_sync_pending: bool,
1516
pub(super) parse_debounce_deadline: Option<Instant>,
1617
pub(super) undo_stack: Vec<String>,
1718
pub(super) redo_stack: Vec<String>,
@@ -23,6 +24,7 @@ impl Default for PointsEditorState {
2324
text: String::new(),
2425
cache: None,
2526
cache_dirty: true,
27+
text_sync_pending: false,
2628
parse_debounce_deadline: None,
2729
undo_stack: Vec::new(),
2830
redo_stack: Vec::new(),
@@ -33,6 +35,7 @@ impl Default for PointsEditorState {
3335
impl CurveFitApp {
3436
pub(super) fn invalidate_points_cache(&mut self) {
3537
self.points.cache_dirty = true;
38+
self.points.text_sync_pending = false;
3639
// Небольшой debounce уменьшает число парсингов во время быстрого ввода текста.
3740
self.points.parse_debounce_deadline =
3841
Some(Instant::now() + Duration::from_millis(POINTS_PARSE_DEBOUNCE_MS));
@@ -59,6 +62,7 @@ impl CurveFitApp {
5962
if should_parse {
6063
self.points.cache = Some(parse_points_text_cache(&self.points.text));
6164
self.points.cache_dirty = false;
65+
self.points.text_sync_pending = false;
6266
self.points.parse_debounce_deadline = None;
6367
}
6468
self.points
@@ -130,6 +134,88 @@ impl CurveFitApp {
130134
}
131135
}
132136

137+
pub(super) fn flush_points_text_from_cache_if_pending(&mut self) {
138+
if !self.points.text_sync_pending {
139+
return;
140+
}
141+
142+
let synced_text = self.points.cache.as_ref().and_then(|cache| {
143+
cache
144+
.parsed_points
145+
.as_ref()
146+
.ok()
147+
.map(|points| points_to_text(points))
148+
});
149+
if let Some(synced_text) = synced_text {
150+
self.points.text = synced_text;
151+
}
152+
self.points.text_sync_pending = false;
153+
}
154+
155+
pub(super) fn push_current_points_undo_snapshot(&mut self) {
156+
self.flush_points_text_from_cache_if_pending();
157+
self.push_points_undo_snapshot(self.points.text.clone());
158+
}
159+
160+
pub(super) fn edit_valid_points_in_cache<F>(
161+
&mut self,
162+
record_undo: bool,
163+
sync_text_immediately: bool,
164+
edit: F,
165+
) -> Result<(), String>
166+
where
167+
F: FnOnce(&mut Vec<Point>),
168+
{
169+
let parse_error = match &self.points_cache_with_policy(true).parsed_points {
170+
Ok(_) => None,
171+
Err(error) => Some(error.clone()),
172+
};
173+
if let Some(error) = parse_error {
174+
return Err(error);
175+
}
176+
177+
if record_undo {
178+
self.push_current_points_undo_snapshot();
179+
}
180+
self.points.redo_stack.clear();
181+
182+
let maybe_synced_text = {
183+
let cache = self
184+
.points
185+
.cache
186+
.as_mut()
187+
.expect("points cache must be initialized");
188+
let points = cache
189+
.parsed_points
190+
.as_mut()
191+
.expect("parse error branch is handled above");
192+
edit(points);
193+
cache.parse_error_line = None;
194+
cache.plot_points = points
195+
.iter()
196+
.map(|point| PlotPoint::new(point.x(), point.y()))
197+
.collect::<Vec<_>>()
198+
.into();
199+
sync_text_immediately.then(|| points_to_text(points))
200+
};
201+
202+
if let Some(synced_text) = maybe_synced_text {
203+
self.points.text = synced_text;
204+
self.points.text_sync_pending = false;
205+
} else {
206+
self.points.text_sync_pending = true;
207+
}
208+
209+
if matches!(
210+
self.status.as_ref(),
211+
Some(StatusMessage::Error(message)) if message.starts_with(POINTS_PARSE_ERROR_PREFIX)
212+
) {
213+
self.status = Some(self.idle_status_after_points_edit());
214+
}
215+
216+
Ok(())
217+
}
218+
133219
pub(super) fn apply_points_text_change(&mut self, new_text: String, keep_redo: bool) {
134220
if self.points.text == new_text {
135221
return;
@@ -160,20 +246,17 @@ impl CurveFitApp {
160246
let Some(next) = self.points.redo_stack.pop() else {
161247
return;
162248
};
163-
self.push_points_undo_snapshot(self.points.text.clone());
249+
self.push_current_points_undo_snapshot();
164250
self.apply_points_text_change(next, true);
165251
self.refresh_status_after_points_edit();
166252
}
167253

168-
pub(super) fn parse_points_for_edit(&mut self) -> Result<Vec<Point>, String> {
169-
match &self.points_cache_with_policy(true).parsed_points {
170-
Ok(points) => Ok(points.clone()),
171-
Err(error) => Err(error.clone()),
172-
}
173-
}
174-
175254
pub(super) fn parse_points_strict(&mut self) -> Result<Points, String> {
176-
Points::try_from(self.parse_points_for_edit()?).map_err(|error| error.to_string())
255+
let parsed_points = match &self.points_cache_with_policy(true).parsed_points {
256+
Ok(points) => points.clone(),
257+
Err(error) => return Err(error.clone()),
258+
};
259+
Points::try_from(parsed_points).map_err(|error| error.to_string())
177260
}
178261

179262
pub(super) fn set_points_cache_from_valid_points(&mut self, points: &[Point]) {
@@ -189,10 +272,12 @@ impl CurveFitApp {
189272
plot_points,
190273
});
191274
self.points.cache_dirty = false;
275+
self.points.text_sync_pending = false;
192276
self.points.parse_debounce_deadline = None;
193277
}
194278

195279
pub(super) fn clear_points_text(&mut self, record_undo: bool) {
280+
self.flush_points_text_from_cache_if_pending();
196281
if self.points.text.is_empty() {
197282
return;
198283
}
@@ -211,12 +296,13 @@ impl CurveFitApp {
211296
}
212297

213298
pub(super) fn write_points_text(&mut self, points: &[Point], record_undo: bool) {
299+
self.flush_points_text_from_cache_if_pending();
214300
let new_text = points_to_text(points);
215301
if self.points.text == new_text {
216302
return;
217303
}
218304
if record_undo {
219-
self.push_points_undo_snapshot(self.points.text.clone());
305+
self.push_current_points_undo_snapshot();
220306
}
221307
self.points.text = new_text;
222308
self.points.redo_stack.clear();

src/app/ui/plot_panel.rs

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
use super::*;
22

33
pub(super) fn add_point_from_plot(app: &mut CurveFitApp, x: f64, y: f64, record_undo: bool) {
4-
let mut points = match app.parse_points_for_edit() {
5-
Ok(points) => points,
4+
let point = match Point::try_new(x, y) {
5+
Ok(point) => point,
66
Err(error) => {
7-
app.status = Some(StatusMessage::Error(error));
7+
app.status = Some(StatusMessage::Error(error.to_string()));
88
return;
99
}
1010
};
1111

12-
match Point::try_new(x, y) {
13-
Ok(point) => {
14-
points.push(point);
15-
app.write_points_text(&points, record_undo);
16-
}
17-
Err(error) => {
18-
app.status = Some(StatusMessage::Error(error.to_string()));
19-
}
12+
if let Err(error) = app.edit_valid_points_in_cache(record_undo, false, move |points| {
13+
points.push(point);
14+
}) {
15+
app.status = Some(StatusMessage::Error(error));
2016
}
2117
}
2218

@@ -32,24 +28,25 @@ pub(super) fn spray_points_from_plot(
3228
return;
3329
}
3430

35-
let mut points = match app.parse_points_for_edit() {
36-
Ok(points) => points,
37-
Err(error) => {
38-
app.status = Some(StatusMessage::Error(error));
39-
return;
40-
}
41-
};
42-
31+
let mut generated_points = Vec::with_capacity(points_to_add);
4332
for _ in 0..points_to_add {
4433
let [offset_x, offset_y] = app.next_spray_unit_disk_offset();
4534
let x = center_x + offset_x * radius_x;
4635
let y = center_y + offset_y * radius_y;
4736
if let Ok(point) = Point::try_new(x, y) {
48-
points.push(point);
37+
generated_points.push(point);
4938
}
5039
}
5140

52-
app.write_points_text(&points, false);
41+
if generated_points.is_empty() {
42+
return;
43+
}
44+
45+
if let Err(error) = app.edit_valid_points_in_cache(false, false, |points| {
46+
points.extend(generated_points);
47+
}) {
48+
app.status = Some(StatusMessage::Error(error));
49+
}
5350
}
5451

5552
pub(super) fn erase_points_from_plot(
@@ -59,25 +56,19 @@ pub(super) fn erase_points_from_plot(
5956
radius_x: f64,
6057
radius_y: f64,
6158
) {
62-
let mut points = match app.parse_points_for_edit() {
63-
Ok(points) => points,
64-
Err(error) => {
65-
app.status = Some(StatusMessage::Error(error));
66-
return;
67-
}
68-
};
69-
7059
if radius_x <= 0.0 || radius_y <= 0.0 {
7160
return;
7261
}
7362

74-
points.retain(|point| {
75-
let dx = (point.x() - center_x) / radius_x;
76-
let dy = (point.y() - center_y) / radius_y;
77-
dx * dx + dy * dy > 1.0
78-
});
79-
80-
app.write_points_text(&points, false);
63+
if let Err(error) = app.edit_valid_points_in_cache(false, false, |points| {
64+
points.retain(|point| {
65+
let dx = (point.x() - center_x) / radius_x;
66+
let dy = (point.y() - center_y) / radius_y;
67+
dx * dx + dy * dy > 1.0
68+
});
69+
}) {
70+
app.status = Some(StatusMessage::Error(error));
71+
}
8172
}
8273

8374
fn plot_position_from_screen(
@@ -111,7 +102,7 @@ pub(super) fn handle_plot_tools(app: &mut CurveFitApp, plot_response: &PlotRespo
111102
.input(|input| input.pointer.button_pressed(egui::PointerButton::Primary));
112103
if is_continuous_tool && primary_down_on_plot {
113104
if app.active_tool_bounds.is_none() {
114-
app.push_points_undo_snapshot(app.points.text.clone());
105+
app.push_current_points_undo_snapshot();
115106
}
116107
app.active_tool_bounds
117108
.get_or_insert(*plot_response.transform.bounds());
@@ -184,6 +175,7 @@ pub(super) fn handle_plot_tools(app: &mut CurveFitApp, plot_response: &PlotRespo
184175
}
185176

186177
if !(is_continuous_tool && primary_down_on_plot) {
178+
app.flush_points_text_from_cache_if_pending();
187179
app.active_tool_bounds = None;
188180
}
189181
}

0 commit comments

Comments
 (0)