Skip to content

Commit 02996da

Browse files
committed
working on usability improvements
1 parent ba7b3e1 commit 02996da

File tree

3 files changed

+160
-80
lines changed

3 files changed

+160
-80
lines changed

src/ui.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub fn window_main(renderer: Arc<Mutex<FractalRenderer>>) -> impl Widget<Fractal
3030
root_pos_current: (0.0, 0.0),
3131
cached_image: None,
3232
needs_buffer_refresh: true,
33-
show_selecting_box: false,
33+
mouse_mode: MouseMode::None,
3434
renderer_zoom: FloatExtended::new(0.0, 0),
3535
renderer_rotate: (0.0, 0.0),
3636
});

src/widgets/fractal.rs

Lines changed: 158 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ use std::cmp::min;
2929

3030
use crate::commands::*;
3131

32+
#[derive(PartialEq, Clone, Copy)]
33+
pub enum MouseMode {
34+
None,
35+
Panning,
36+
RootFinding
37+
}
38+
3239
pub struct FractalWidget<'a> {
3340
pub image_width: usize,
3441
pub image_height: usize,
@@ -39,7 +46,7 @@ pub struct FractalWidget<'a> {
3946
pub root_pos_current: (f64, f64),
4047
pub cached_image: Option<<D2DRenderContext<'a> as RenderContext>::Image>,
4148
pub needs_buffer_refresh: bool,
42-
pub show_selecting_box: bool,
49+
pub mouse_mode: MouseMode,
4350
pub renderer_zoom: FloatExtended,
4451
pub renderer_rotate: (f64, f64)
4552
}
@@ -128,20 +135,30 @@ impl<'a> Widget<FractalData> for FractalWidget<'a> {
128135

129136
data.sender.lock().send(THREAD_RESET_RENDERER_FULL).unwrap();
130137
}
131-
// TODO this section sometimes blocks. Needs to be fixed
132138
Event::MouseMove(e) => {
133-
// if data.mouse_mode != 0 {
134-
if data.mouse_mode != 0 {
135-
self.pos2 = (e.pos.x, e.pos.y);
136-
137-
let top_left = (self.pos1.0.min(self.pos2.0), self.pos1.1.min(self.pos2.1));
138-
let bottom_right = (self.pos1.0.max(self.pos2.0), self.pos1.1.max(self.pos2.1));
139+
// If the rendering / root finding has not completed, stop
140+
if data.rendering_stage != 0 || data.root_stage == 1 {
141+
return;
142+
}
139143

140-
self.root_pos_current = (0.5 * (top_left.0 + bottom_right.0), 0.5 * (top_left.1 + bottom_right.1));
144+
match self.mouse_mode {
145+
MouseMode::RootFinding => {
146+
self.pos2 = (e.pos.x, e.pos.y);
141147

142-
ctx.request_paint();
143-
}
148+
let top_left = (self.pos1.0.min(self.pos2.0), self.pos1.1.min(self.pos2.1));
149+
let bottom_right = (self.pos1.0.max(self.pos2.0), self.pos1.1.max(self.pos2.1));
144150

151+
self.root_pos_current = (0.5 * (top_left.0 + bottom_right.0), 0.5 * (top_left.1 + bottom_right.1));
152+
153+
ctx.request_paint();
154+
}
155+
MouseMode::Panning => {
156+
self.pos2 = (e.pos.x, e.pos.y);
157+
158+
ctx.request_paint();
159+
},
160+
MouseMode::None => {},
161+
}
145162

146163
// if data.rendering_stage == 0 {
147164
// let size = ctx.size().to_rect();
@@ -174,90 +191,151 @@ impl<'a> Widget<FractalData> for FractalWidget<'a> {
174191
return;
175192
}
176193

177-
// Zoom in, use the mouse position
178194
if e.button == MouseButton::Left {
179195
self.pos1 = (e.pos.x, e.pos.y);
180196
self.pos2 = (e.pos.x, e.pos.y);
181197

182-
if data.mouse_mode == 2 {
183-
self.show_selecting_box = true;
198+
self.mouse_mode = if data.mouse_mode == 2 {
199+
MouseMode::RootFinding
184200
} else {
185-
data.mouse_mode = 1;
201+
MouseMode::Panning
186202
}
187203
}
188-
189-
if e.button == MouseButton::Right {
190-
ctx.submit_command(MULTIPLY_ZOOM.with(1.0 / data.zoom_scale_factor));
191-
}
192204
},
193205
Event::MouseUp(e) => {
206+
// If the rendering / root finding has not completed, stop
207+
if data.rendering_stage != 0 || data.root_stage == 1 {
208+
return;
209+
}
210+
194211
if e.button == MouseButton::Left {
195-
if data.mouse_mode == 2 {
196-
self.pos2 = (e.pos.x, e.pos.y);
212+
match self.mouse_mode {
213+
MouseMode::RootFinding => {
214+
self.pos2 = (e.pos.x, e.pos.y);
197215

198-
ctx.submit_command(CALCULATE_ROOT);
199-
} else {
200-
data.mouse_mode = 0;
216+
ctx.submit_command(CALCULATE_ROOT);
217+
},
218+
MouseMode::Panning => {
219+
self.pos2 = (e.pos.x, e.pos.y);
201220

202-
let mut settings = data.settings.lock();
203-
let mut renderer = data.renderer.lock();
204-
205-
let size = ctx.size().to_rect();
206-
207-
let i = renderer.image_width as f64 / 2.0 - (self.pos2.0 - self.pos1.0) * renderer.image_width as f64 / size.width();
208-
let j = renderer.image_height as f64 / 2.0 - (self.pos2.1 - self.pos1.1) * renderer.image_height as f64 / size.height();
209-
210-
let cos_rotate = renderer.rotate.cos();
211-
let sin_rotate = renderer.rotate.sin();
221+
let mut settings = data.settings.lock();
222+
let renderer = data.renderer.lock();
223+
224+
let size = ctx.size().to_rect();
225+
226+
let i = renderer.image_width as f64 / 2.0 - (self.pos2.0 - self.pos1.0) * renderer.image_width as f64 / size.width();
227+
let j = renderer.image_height as f64 / 2.0 - (self.pos2.1 - self.pos1.1) * renderer.image_height as f64 / size.height();
228+
229+
let cos_rotate = renderer.rotate.cos();
230+
let sin_rotate = renderer.rotate.sin();
231+
232+
let delta_pixel = 4.0 / ((renderer.image_height - 1) as f64 * renderer.zoom.mantissa);
233+
let delta_top_left = get_delta_top_left(delta_pixel, renderer.image_width, renderer.image_height, cos_rotate, sin_rotate);
234+
235+
let element = ComplexFixed::new(
236+
i * delta_pixel * cos_rotate - j * delta_pixel * sin_rotate + delta_top_left.re,
237+
i * delta_pixel * sin_rotate + j * delta_pixel * cos_rotate + delta_top_left.im
238+
);
239+
240+
let element = ComplexExtended::new(element, -renderer.zoom.exponent);
212241

213-
let delta_pixel = 4.0 / ((renderer.image_height - 1) as f64 * renderer.zoom.mantissa);
214-
let delta_top_left = get_delta_top_left(delta_pixel, renderer.image_width, renderer.image_height, cos_rotate, sin_rotate);
242+
let mut location = renderer.center_reference.c.clone();
215243

216-
let element = ComplexFixed::new(
217-
i * delta_pixel * cos_rotate - j * delta_pixel * sin_rotate + delta_top_left.re,
218-
i * delta_pixel * sin_rotate + j * delta_pixel * cos_rotate + delta_top_left.im
219-
);
244+
let precision = location.real().prec();
245+
246+
let temp = FloatArbitrary::with_val(precision, element.exponent).exp2();
247+
let temp2 = FloatArbitrary::with_val(precision, element.mantissa.re);
248+
let temp3 = FloatArbitrary::with_val(precision, element.mantissa.im);
249+
250+
*location.mut_real() += &temp2 * &temp;
251+
*location.mut_imag() += &temp3 * &temp;
252+
253+
// Set the overrides for the current location
254+
settings.set("real", location.real().to_string()).unwrap();
255+
settings.set("imag", location.imag().to_string()).unwrap();
256+
257+
data.real = settings.get_str("real").unwrap();
258+
data.imag = settings.get_str("imag").unwrap();
259+
260+
self.mouse_mode = MouseMode::None;
220261

221-
let element = ComplexExtended::new(element, -renderer.zoom.exponent);
222-
let mut zoom = renderer.zoom;
262+
ctx.submit_command(RESET_RENDERER_FULL);
263+
},
264+
MouseMode::None => {},
265+
}
266+
}
267+
}
268+
Event::Wheel(e) => {
269+
if e.wheel_delta.y > 0.0 {
270+
ctx.submit_command(MULTIPLY_ZOOM.with(1.0 / data.zoom_scale_factor));
271+
} else {
272+
// If the rendering / root finding has not completed, stop
273+
if data.rendering_stage != 0 || data.root_stage == 1 {
274+
return;
275+
}
223276

224-
zoom.mantissa *= data.zoom_scale_factor;
225-
zoom.reduce();
277+
let mut settings = data.settings.lock();
278+
let mut renderer = data.renderer.lock();
226279

227-
let mut location = renderer.center_reference.c.clone();
280+
let size = ctx.size().to_rect();
228281

229-
let precision = location.real().prec();
230-
231-
let temp = FloatArbitrary::with_val(precision, element.exponent).exp2();
232-
let temp2 = FloatArbitrary::with_val(precision, element.mantissa.re);
233-
let temp3 = FloatArbitrary::with_val(precision, element.mantissa.im);
234-
235-
*location.mut_real() += &temp2 * &temp;
236-
*location.mut_imag() += &temp3 * &temp;
237-
238-
data.zoom = extended_to_string_long(zoom);
239-
240-
// Set the overrides for the current location
241-
settings.set("real", location.real().to_string()).unwrap();
242-
settings.set("imag", location.imag().to_string()).unwrap();
243-
settings.set("zoom", data.zoom.clone()).unwrap();
244-
245-
data.real = settings.get_str("real").unwrap();
246-
data.imag = settings.get_str("imag").unwrap();
247-
248-
renderer.adjust_iterations();
249-
250-
settings.set("iterations", renderer.maximum_iteration as i64).unwrap();
251-
data.iteration_limit = renderer.maximum_iteration as i64;
252-
253-
self.pos1 = (0.0, 0.0);
254-
self.pos2 = (0.0, 0.0);
282+
let i = e.pos.x * renderer.image_width as f64 / size.width();
283+
let j = e.pos.y * renderer.image_height as f64 / size.height();
255284

256-
ctx.submit_command(RESET_RENDERER_FULL);
257-
}
285+
let cos_rotate = renderer.rotate.cos();
286+
let sin_rotate = renderer.rotate.sin();
287+
288+
let delta_pixel = 4.0 / ((renderer.image_height - 1) as f64 * renderer.zoom.mantissa);
289+
let delta_top_left = get_delta_top_left(delta_pixel, renderer.image_width, renderer.image_height, cos_rotate, sin_rotate);
290+
291+
let element = ComplexFixed::new(
292+
i * delta_pixel * cos_rotate - j * delta_pixel * sin_rotate + delta_top_left.re,
293+
i * delta_pixel * sin_rotate + j * delta_pixel * cos_rotate + delta_top_left.im
294+
);
295+
296+
let element = ComplexExtended::new(element, -renderer.zoom.exponent);
297+
let mut zoom = renderer.zoom;
298+
299+
zoom.mantissa *= data.zoom_scale_factor;
300+
zoom.reduce();
301+
302+
let mut location = renderer.center_reference.c.clone();
303+
304+
let precision = location.real().prec();
305+
306+
let temp = FloatArbitrary::with_val(precision, element.exponent).exp2();
307+
let temp2 = FloatArbitrary::with_val(precision, element.mantissa.re);
308+
let temp3 = FloatArbitrary::with_val(precision, element.mantissa.im);
309+
310+
*location.mut_real() += &temp2 * &temp;
311+
*location.mut_imag() += &temp3 * &temp;
312+
313+
data.zoom = extended_to_string_long(zoom);
314+
315+
// Set the overrides for the current location
316+
settings.set("real", location.real().to_string()).unwrap();
317+
settings.set("imag", location.imag().to_string()).unwrap();
318+
settings.set("zoom", data.zoom.clone()).unwrap();
319+
320+
data.real = settings.get_str("real").unwrap();
321+
data.imag = settings.get_str("imag").unwrap();
322+
323+
renderer.adjust_iterations();
324+
325+
settings.set("iterations", renderer.maximum_iteration as i64).unwrap();
326+
data.iteration_limit = renderer.maximum_iteration as i64;
327+
328+
self.mouse_mode = MouseMode::None;
329+
330+
ctx.submit_command(RESET_RENDERER_FULL);
258331
}
259332
}
260333
Event::KeyUp(e) => {
334+
// If the rendering / root finding has not completed, stop
335+
if data.rendering_stage != 0 || data.root_stage == 1 {
336+
return;
337+
}
338+
261339
// Shortcut keys
262340
if e.key == KbKey::Character("Z".to_string()) || e.key == KbKey::Character("z".to_string()) {
263341
ctx.submit_command(MULTIPLY_ZOOM.with(2.0));
@@ -304,7 +382,7 @@ impl<'a> Widget<FractalData> for FractalWidget<'a> {
304382
}
305383

306384
if let Some(root_zoom) = command.get(ROOT_FINDING_COMPLETE) {
307-
self.show_selecting_box = false;
385+
self.mouse_mode = MouseMode::None;
308386

309387
data.root_progress = 1.0;
310388
data.root_iteration = 64;
@@ -1310,6 +1388,8 @@ impl<'a> Widget<FractalData> for FractalWidget<'a> {
13101388
.make_image(self.image_width, self.image_height, &temporary_image, ImageFormat::Rgb)
13111389
.unwrap());
13121390

1391+
self.pos1 = self.pos2;
1392+
13131393
self.needs_buffer_refresh = false;
13141394
}
13151395

@@ -1323,16 +1403,16 @@ impl<'a> Widget<FractalData> for FractalWidget<'a> {
13231403

13241404
let mut image_position = Rect::new(0.0, 0.0, self.image_width as f64, self.image_height as f64);
13251405

1326-
if !self.show_selecting_box {
1406+
if self.mouse_mode != MouseMode::RootFinding {
13271407
let x_delta = self.pos2.0 - self.pos1.0;
13281408
let y_delta = self.pos2.1 - self.pos1.1;
1329-
1409+
13301410
image_position.x0 -= x_delta.min(0.0) * self.image_width as f64 / size.x1;
13311411
image_position.y0 -= y_delta.min(0.0) * self.image_height as f64 / size.y1;
13321412

13331413
image_position.x1 -= x_delta.max(0.0) * self.image_width as f64 / size.x1;
13341414
image_position.y1 -= y_delta.max(0.0) * self.image_height as f64 / size.y1;
1335-
1415+
13361416
size.x0 += x_delta.max(0.0);
13371417
size.y0 += y_delta.max(0.0);
13381418

@@ -1342,7 +1422,7 @@ impl<'a> Widget<FractalData> for FractalWidget<'a> {
13421422

13431423
ctx.draw_image_area(self.cached_image.as_ref().unwrap(), image_position, size, interpolation_mode);
13441424

1345-
if self.show_selecting_box {
1425+
if self.mouse_mode == MouseMode::RootFinding {
13461426
let rect = Rect::from_origin_size(self.pos1, (self.pos2.0 - self.pos1.0, self.pos2.1 - self.pos1.1));
13471427
let fill_color = Color::rgba8(0, 0, 0, 150);
13481428
ctx.fill(rect, &fill_color);

src/widgets/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ mod fractal;
44

55
pub use no_update_label::NoUpdateLabel;
66
pub use either::Either;
7-
pub use fractal::{FractalData, FractalWidget};
7+
pub use fractal::{FractalData, FractalWidget, MouseMode};

0 commit comments

Comments
 (0)