| ... | @@ -123,6 +123,13 @@ enum Drag { | ... | @@ -123,6 +123,13 @@ enum Drag { |
| 123 | grab: [f32; 2], | 123 | grab: [f32; 2], |
| 124 | pending_press: Option<[f32; 2]>, | 124 | pending_press: Option<[f32; 2]>, |
| 125 | }, | 125 | }, |
| | 126 | Image { |
| | 127 | id: onestore::ExGuid, |
| | 128 | handle: [i8; 2], |
| | 129 | /// Document point of the press. |
| | 130 | press: [f32; 2], |
| | 131 | pending_press: Option<[f32; 2]>, |
| | 132 | }, |
| 126 | } | 133 | } |
| 127 | | 134 | |
| 128 | #[derive(Clone, Copy)] | 135 | #[derive(Clone, Copy)] |
| ... | @@ -130,6 +137,24 @@ enum PointerFeedback<'a> { | ... | @@ -130,6 +137,24 @@ enum PointerFeedback<'a> { |
| 130 | Hover(onestore::ExGuid), | 137 | Hover(onestore::ExGuid), |
| 131 | Move(onestore::ExGuid, [f32; 2]), | 138 | Move(onestore::ExGuid, [f32; 2]), |
| 132 | Resize(&'a TextOutline), | 139 | Resize(&'a TextOutline), |
| | 140 | /// A picture being moved or resized, drawn at this origin and size. |
| | 141 | Image(onestore::ExGuid, [f32; 2], [f32; 2]), |
| | 142 | } |
| | 143 | |
| | 144 | /// A non-text object holding focus, which hides the text caret and suspends typing. |
| | 145 | #[derive(Clone, Copy, Debug, PartialEq)] |
| | 146 | enum ObjectFocus { |
| | 147 | ReadOnly(usize), |
| | 148 | Image(onestore::ExGuid), |
| | 149 | } |
| | 150 | |
| | 151 | impl ObjectFocus { |
| | 152 | fn read_only(self) -> Option<usize> { |
| | 153 | match self { |
| | 154 | Self::ReadOnly(index) => Some(index), |
| | 155 | Self::Image(_) => None, |
| | 156 | } |
| | 157 | } |
| 133 | } | 158 | } |
| 134 | | 159 | |
| 135 | struct State { | 160 | struct State { |
| ... | @@ -151,7 +176,7 @@ struct State { | ... | @@ -151,7 +176,7 @@ struct State { |
| 151 | pointer_inside: bool, | 176 | pointer_inside: bool, |
| 152 | last_click: Option<(Instant, [f32; 2], u8)>, | 177 | last_click: Option<(Instant, [f32; 2], u8)>, |
| 153 | drag: Option<Drag>, | 178 | drag: Option<Drag>, |
| 154 | read_only_focus: Option<usize>, | 179 | object_focus: Option<ObjectFocus>, |
| 155 | modifiers: ModifiersState, | 180 | modifiers: ModifiersState, |
| 156 | focused: bool, | 181 | focused: bool, |
| 157 | occluded: bool, | 182 | occluded: bool, |
| ... | @@ -310,7 +335,7 @@ impl State { | ... | @@ -310,7 +335,7 @@ impl State { |
| 310 | pointer_inside: false, | 335 | pointer_inside: false, |
| 311 | last_click: None, | 336 | last_click: None, |
| 312 | drag: None, | 337 | drag: None, |
| 313 | read_only_focus: None, | 338 | object_focus: None, |
| 314 | modifiers: ModifiersState::empty(), | 339 | modifiers: ModifiersState::empty(), |
| 315 | focused: true, | 340 | focused: true, |
| 316 | occluded: false, | 341 | occluded: false, |
| ... | @@ -352,15 +377,75 @@ impl State { | ... | @@ -352,15 +377,75 @@ impl State { |
| 352 | )) | 377 | )) |
| 353 | } | 378 | } |
| 354 | | 379 | |
| 355 | fn set_read_only_focus(&mut self, index: Option<usize>) { | 380 | fn image_preview(&self) -> Option<(onestore::ExGuid, [f32; 2], [f32; 2])> { |
| 356 | if self.read_only_focus != index { | 381 | let Drag::Image { |
| | 382 | id, |
| | 383 | handle, |
| | 384 | press, |
| | 385 | pending_press: None, |
| | 386 | } = self.drag.as_ref()? |
| | 387 | else { |
| | 388 | return None; |
| | 389 | }; |
| | 390 | let (origin, size) = self.editor.image_placement(*id)?; |
| | 391 | let point = self.viewport.document_point(self.pointer); |
| | 392 | let delta = [point[0] - press[0], point[1] - press[1]]; |
| | 393 | if *handle != [0, 0] { |
| | 394 | let (origin, size) = resize_image(origin, size, *handle, delta); |
| | 395 | return Some((*id, origin, size)); |
| | 396 | } |
| | 397 | let origin = [origin[0] + delta[0], origin[1] + delta[1]]; |
| | 398 | let origin = if self.modifiers.alt_key() { |
| | 399 | origin |
| | 400 | } else { |
| | 401 | snap_to_grid(origin) |
| | 402 | }; |
| | 403 | Some((*id, origin, size)) |
| | 404 | } |
| | 405 | |
| | 406 | /// The selected picture's handles lie above the page; its body keeps its paint order. |
| | 407 | fn hit_test(&self, point: [f32; 2]) -> Option<Hit> { |
| | 408 | let pixel = self.display_scale / self.viewport.scale; |
| | 409 | if let Some(ObjectFocus::Image(id)) = self.object_focus |
| | 410 | && let Some((origin, size)) = self.editor.image_placement(id) |
| | 411 | && let Some(handle) = image_handle_at(image_rect(origin, size), pixel, point) |
| | 412 | { |
| | 413 | return Some(Hit::Image { id, handle }); |
| | 414 | } |
| | 415 | page_hit_test(&self.editor, self.scene.as_ref(), point, pixel) |
| | 416 | } |
| | 417 | |
| | 418 | fn set_object_focus(&mut self, focus: Option<ObjectFocus>) { |
| | 419 | if self.object_focus != focus { |
| 357 | self.editor.finish_composition(); | 420 | self.editor.finish_composition(); |
| 358 | self.drag = None; | 421 | self.drag = None; |
| 359 | self.read_only_focus = index; | 422 | self.object_focus = focus; |
| 360 | self.window.set_ime_allowed(index.is_none()); | 423 | self.window.set_ime_allowed(focus.is_none()); |
| 361 | } | 424 | } |
| 362 | } | 425 | } |
| 363 | | 426 | |
| | 427 | /// Page coordinates of a focused object. |
| | 428 | fn object_rect(&self, focus: ObjectFocus) -> [f32; 4] { |
| | 429 | let (scene, offset) = self.scene.as_ref().unwrap(); |
| | 430 | let [x0, y0, x1, y1] = match focus { |
| | 431 | ObjectFocus::ReadOnly(index) => scene |
| | 432 | .read_only(Some(&self.editor)) |
| | 433 | .nth(index) |
| | 434 | .unwrap() |
| | 435 | .rect(), |
| | 436 | ObjectFocus::Image(id) => { |
| | 437 | let (origin, size) = self.editor.image_placement(id).unwrap(); |
| | 438 | image_rect(origin, size) |
| | 439 | } |
| | 440 | }; |
| | 441 | [ |
| | 442 | x0 + offset[0], |
| | 443 | y0 + offset[1], |
| | 444 | x1 + offset[0], |
| | 445 | y1 + offset[1], |
| | 446 | ] |
| | 447 | } |
| | 448 | |
| 364 | fn changed(&mut self) -> Result<(), Box<dyn Error>> { | 449 | fn changed(&mut self) -> Result<(), Box<dyn Error>> { |
| 365 | self.scroll().clamp(&mut self.viewport); | 450 | self.scroll().clamp(&mut self.viewport); |
| 366 | self.caret = true; | 451 | self.caret = true; |
| ... | @@ -446,7 +531,7 @@ impl State { | ... | @@ -446,7 +531,7 @@ impl State { |
| 446 | self.editor = editor; | 531 | self.editor = editor; |
| 447 | self.scene = Some((scene, [0.0; 2])); | 532 | self.scene = Some((scene, [0.0; 2])); |
| 448 | self.drag = None; | 533 | self.drag = None; |
| 449 | self.read_only_focus = None; | 534 | self.object_focus = None; |
| 450 | self.window.set_title(&session.window_title()); | 535 | self.window.set_title(&session.window_title()); |
| 451 | self.update_accessibility()?; | 536 | self.update_accessibility()?; |
| 452 | self.window.request_redraw(); | 537 | self.window.request_redraw(); |
| ... | @@ -535,7 +620,7 @@ impl State { | ... | @@ -535,7 +620,7 @@ impl State { |
| 535 | self.scene.as_ref(), | 620 | self.scene.as_ref(), |
| 536 | &self.editor, | 621 | &self.editor, |
| 537 | self.viewport, | 622 | self.viewport, |
| 538 | self.read_only_focus, | 623 | self.object_focus.and_then(ObjectFocus::read_only), |
| 539 | ); | 624 | ); |
| 540 | update | 625 | update |
| 541 | } | 626 | } |
| ... | @@ -573,7 +658,7 @@ impl State { | ... | @@ -573,7 +658,7 @@ impl State { |
| 573 | } | 658 | } |
| 574 | if let Some(index) = self.accessibility.read_only_for_node(request.target_node) { | 659 | if let Some(index) = self.accessibility.read_only_for_node(request.target_node) { |
| 575 | if request.action == Action::Focus { | 660 | if request.action == Action::Focus { |
| 576 | self.set_read_only_focus(Some(index)); | 661 | self.set_object_focus(Some(ObjectFocus::ReadOnly(index))); |
| 577 | self.window.focus_window(); | 662 | self.window.focus_window(); |
| 578 | self.reveal_focus()?; | 663 | self.reveal_focus()?; |
| 579 | self.changed()?; | 664 | self.changed()?; |
| ... | @@ -605,7 +690,7 @@ impl State { | ... | @@ -605,7 +690,7 @@ impl State { |
| 605 | } | 690 | } |
| 606 | _ => return Ok(()), | 691 | _ => return Ok(()), |
| 607 | } | 692 | } |
| 608 | self.set_read_only_focus(None); | 693 | self.set_object_focus(None); |
| 609 | self.reveal_focus()?; | 694 | self.reveal_focus()?; |
| 610 | self.changed() | 695 | self.changed() |
| 611 | } | 696 | } |
| ... | @@ -614,19 +699,9 @@ impl State { | ... | @@ -614,19 +699,9 @@ impl State { |
| 614 | if self.viewport.size.contains(&0) { | 699 | if self.viewport.size.contains(&0) { |
| 615 | return Ok(()); | 700 | return Ok(()); |
| 616 | } | 701 | } |
| 617 | let rect = if let Some(index) = self.read_only_focus { | 702 | let rect = if let Some(focus) = self.object_focus { |
| 618 | let (scene, offset) = self.scene.as_ref().unwrap(); | 703 | let [x0, y0, x1, y1] = self.object_rect(focus).map(f64::from); |
| 619 | let [x0, y0, x1, y1] = scene | 704 | parley::BoundingBox { x0, y0, x1, y1 } |
| 620 | .read_only(Some(&self.editor)) | | |
| 621 | .nth(index) | | |
| 622 | .unwrap() | | |
| 623 | .rect(); | | |
| 624 | parley::BoundingBox { | | |
| 625 | x0: f64::from(x0 + offset[0]), | | |
| 626 | y0: f64::from(y0 + offset[1]), | | |
| 627 | x1: f64::from(x1 + offset[0]), | | |
| 628 | y1: f64::from(y1 + offset[1]), | | |
| 629 | } | | |
| 630 | } else { | 705 | } else { |
| 631 | let mut rect = self.editor.caret(1.0)?; | 706 | let mut rect = self.editor.caret(1.0)?; |
| 632 | let origin = self.editor.active_outline().origin(); | 707 | let origin = self.editor.active_outline().origin(); |
| ... | @@ -647,7 +722,7 @@ impl State { | ... | @@ -647,7 +722,7 @@ impl State { |
| 647 | [(outline.x0, outline.x1), (outline.y0, outline.y1)][axis]; | 722 | [(outline.x0, outline.x1), (outline.y0, outline.y1)][axis]; |
| 648 | let outline_start = outline_start.min(start); | 723 | let outline_start = outline_start.min(start); |
| 649 | let outline_end = outline_end.max(end); | 724 | let outline_end = outline_end.max(end); |
| 650 | if self.read_only_focus.is_none() | 725 | if self.object_focus.is_none() |
| 651 | && (outline_end - outline_start) * f64::from(self.viewport.scale) | 726 | && (outline_end - outline_start) * f64::from(self.viewport.scale) |
| 652 | <= size - margin * 2.0 | 727 | <= size - margin * 2.0 |
| 653 | { | 728 | { |
| ... | @@ -759,6 +834,10 @@ impl State { | ... | @@ -759,6 +834,10 @@ impl State { |
| 759 | _ => self | 834 | _ => self |
| 760 | .preview() | 835 | .preview() |
| 761 | .map(|(id, origin)| PointerFeedback::Move(id, origin)) | 836 | .map(|(id, origin)| PointerFeedback::Move(id, origin)) |
| | 837 | .or_else(|| { |
| | 838 | self.image_preview() |
| | 839 | .map(|(id, origin, size)| PointerFeedback::Image(id, origin, size)) |
| | 840 | }) |
| 762 | .or_else(|| { | 841 | .or_else(|| { |
| 763 | if !self.pointer_inside { | 842 | if !self.pointer_inside { |
| 764 | return None; | 843 | return None; |
| ... | @@ -778,10 +857,13 @@ impl State { | ... | @@ -778,10 +857,13 @@ impl State { |
| 778 | } | 857 | } |
| 779 | }), | 858 | }), |
| 780 | }, | 859 | }, |
| 781 | self.read_only_focus, | 860 | self.object_focus, |
| 782 | self.caret | 861 | self.caret |
| 783 | && self.focused | 862 | && self.focused |
| 784 | && !matches!(self.drag, Some(Drag::Outline { .. } | Drag::Resize { .. })), | 863 | && !matches!( |
| | 864 | self.drag, |
| | 865 | Some(Drag::Outline { .. } | Drag::Resize { .. } | Drag::Image { .. }) |
| | 866 | ), |
| 785 | self.viewport.scale, | 867 | self.viewport.scale, |
| 786 | self.display_scale, | 868 | self.display_scale, |
| 787 | )?; | 869 | )?; |
| ... | @@ -807,12 +889,15 @@ impl State { | ... | @@ -807,12 +889,15 @@ impl State { |
| 807 | let shift = self.modifiers.shift_key(); | 889 | let shift = self.modifiers.shift_key(); |
| 808 | let command = self.modifiers.super_key(); | 890 | let command = self.modifiers.super_key(); |
| 809 | let option = self.modifiers.alt_key(); | 891 | let option = self.modifiers.alt_key(); |
| 810 | if self.read_only_focus.is_some() { | 892 | if let Some(focus) = self.object_focus { |
| 811 | if !read_only_shortcut(key, self.modifiers) { | 893 | let undo = matches!(focus, ObjectFocus::Image(_)) |
| | 894 | && command |
| | 895 | && matches!(key, Key::Character(value) if value.eq_ignore_ascii_case("z")); |
| | 896 | if !undo && !read_only_shortcut(key, self.modifiers) { |
| 812 | return Ok(()); | 897 | return Ok(()); |
| 813 | } | 898 | } |
| 814 | if key == &Key::Named(NamedKey::Escape) { | 899 | if key == &Key::Named(NamedKey::Escape) { |
| 815 | self.set_read_only_focus(None); | 900 | self.set_object_focus(None); |
| 816 | self.reveal_focus()?; | 901 | self.reveal_focus()?; |
| 817 | return self.changed(); | 902 | return self.changed(); |
| 818 | } | 903 | } |
| ... | @@ -832,7 +917,10 @@ impl State { | ... | @@ -832,7 +917,10 @@ impl State { |
| 832 | _ => {} | 917 | _ => {} |
| 833 | } | 918 | } |
| 834 | } | 919 | } |
| 835 | if matches!(self.drag, Some(Drag::Outline { .. } | Drag::Resize { .. })) { | 920 | if matches!( |
| | 921 | self.drag, |
| | 922 | Some(Drag::Outline { .. } | Drag::Resize { .. } | Drag::Image { .. }) |
| | 923 | ) { |
| 836 | if matches!( | 924 | if matches!( |
| 837 | key, | 925 | key, |
| 838 | Key::Named( | 926 | Key::Named( |
| ... | @@ -881,14 +969,17 @@ impl State { | ... | @@ -881,14 +969,17 @@ impl State { |
| 881 | if count == 0 { | 969 | if count == 0 { |
| 882 | return self.changed(); | 970 | return self.changed(); |
| 883 | } | 971 | } |
| 884 | let index = self.read_only_focus.map_or_else( | 972 | let index = self |
| 885 | || { | 973 | .object_focus |
| 886 | outlines | 974 | .and_then(ObjectFocus::read_only) |
| 887 | .iter() | 975 | .map_or_else( |
| 888 | .position(|outline| outline.id == self.editor.active_outline().id) | 976 | || { |
| 889 | }, | 977 | outlines |
| 890 | |index| Some(outlines.len() + index), | 978 | .iter() |
| 891 | ); | 979 | .position(|outline| outline.id == self.editor.active_outline().id) |
| | 980 | }, |
| | 981 | |index| Some(outlines.len() + index), |
| | 982 | ); |
| 892 | let next = index.map_or(if shift { count - 1 } else { 0 }, |index| { | 983 | let next = index.map_or(if shift { count - 1 } else { 0 }, |index| { |
| 893 | if shift { | 984 | if shift { |
| 894 | (index + count - 1) % count | 985 | (index + count - 1) % count |
| ... | @@ -898,9 +989,9 @@ impl State { | ... | @@ -898,9 +989,9 @@ impl State { |
| 898 | }); | 989 | }); |
| 899 | if next < outlines.len() { | 990 | if next < outlines.len() { |
| 900 | self.editor.focus_outline(outlines[next].id)?; | 991 | self.editor.focus_outline(outlines[next].id)?; |
| 901 | self.set_read_only_focus(None); | 992 | self.set_object_focus(None); |
| 902 | } else { | 993 | } else { |
| 903 | self.set_read_only_focus(Some(next - outlines.len())); | 994 | self.set_object_focus(Some(ObjectFocus::ReadOnly(next - outlines.len()))); |
| 904 | } | 995 | } |
| 905 | self.reveal_focus()?; | 996 | self.reveal_focus()?; |
| 906 | return self.changed(); | 997 | return self.changed(); |
| ... | @@ -908,14 +999,9 @@ impl State { | ... | @@ -908,14 +999,9 @@ impl State { |
| 908 | if command && let Key::Character(key) = key { | 999 | if command && let Key::Character(key) = key { |
| 909 | match key.to_lowercase().as_str() { | 1000 | match key.to_lowercase().as_str() { |
| 910 | "n" if shift => { | 1001 | "n" if shift => { |
| 911 | let position = if let Some(index) = self.read_only_focus { | 1002 | let position = if let Some(focus) = self.object_focus { |
| 912 | let (scene, offset) = self.scene.as_ref().unwrap(); | 1003 | let rect = self.object_rect(focus); |
| 913 | let rect = scene | 1004 | [rect[2] + 24.0, rect[1]] |
| 914 | .read_only(Some(&self.editor)) | | |
| 915 | .nth(index) | | |
| 916 | .unwrap() | | |
| 917 | .rect(); | | |
| 918 | [rect[2] + offset[0] + 24.0, rect[1] + offset[1]] | | |
| 919 | } else { | 1005 | } else { |
| 920 | let bounds = self.editor.active_outline().bounds(); | 1006 | let bounds = self.editor.active_outline().bounds(); |
| 921 | [bounds.x1 as f32 + 24.0, bounds.y0 as f32] | 1007 | [bounds.x1 as f32 + 24.0, bounds.y0 as f32] |
| ... | @@ -925,7 +1011,7 @@ impl State { | ... | @@ -925,7 +1011,7 @@ impl State { |
| 925 | snap_to_grid(position), | 1011 | snap_to_grid(position), |
| 926 | DEFAULT_OUTLINE_WIDTH, | 1012 | DEFAULT_OUTLINE_WIDTH, |
| 927 | )?; | 1013 | )?; |
| 928 | self.set_read_only_focus(None); | 1014 | self.set_object_focus(None); |
| 929 | } | 1015 | } |
| 930 | "a" => self.editor.select_all()?, | 1016 | "a" => self.editor.select_all()?, |
| 931 | "z" => { | 1017 | "z" => { |
| ... | @@ -1137,7 +1223,7 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1137,7 +1223,7 @@ impl ApplicationHandler<UserEvent> for App { |
| 1137 | let event = match event { | 1223 | let event = match event { |
| 1138 | UserEvent::InsertText(text) => { | 1224 | UserEvent::InsertText(text) => { |
| 1139 | if let Some(state) = &mut self.state | 1225 | if let Some(state) = &mut self.state |
| 1140 | && state.read_only_focus.is_none() | 1226 | && state.object_focus.is_none() |
| 1141 | && !text.is_empty() | 1227 | && !text.is_empty() |
| 1142 | && !text.chars().any(char::is_control) | 1228 | && !text.chars().any(char::is_control) |
| 1143 | { | 1229 | { |
| ... | @@ -1282,12 +1368,7 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1282,12 +1368,7 @@ impl ApplicationHandler<UserEvent> for App { |
| 1282 | WindowEvent::CursorMoved { position, .. } => { | 1368 | WindowEvent::CursorMoved { position, .. } => { |
| 1283 | state.pointer_inside = true; | 1369 | state.pointer_inside = true; |
| 1284 | state.pointer = [position.x as f32, position.y as f32]; | 1370 | state.pointer = [position.x as f32, position.y as f32]; |
| 1285 | let hit = page_hit_test( | 1371 | let hit = state.hit_test(state.viewport.document_point(state.pointer)); |
| 1286 | &state.editor, | | |
| 1287 | state.scene.as_ref(), | | |
| 1288 | state.viewport.document_point(state.pointer), | | |
| 1289 | state.display_scale / state.viewport.scale, | | |
| 1290 | ); | | |
| 1291 | let scrollbar = state | 1372 | let scrollbar = state |
| 1292 | .scroll() | 1373 | .scroll() |
| 1293 | .hit_test(state.viewport, state.display_scale, state.pointer) | 1374 | .hit_test(state.viewport, state.display_scale, state.pointer) |
| ... | @@ -1295,6 +1376,8 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1295,6 +1376,8 @@ impl ApplicationHandler<UserEvent> for App { |
| 1295 | state.window.set_cursor(match (&state.drag, hit) { | 1376 | state.window.set_cursor(match (&state.drag, hit) { |
| 1296 | (Some(Drag::Scrollbar { .. }), _) => winit::window::CursorIcon::Default, | 1377 | (Some(Drag::Scrollbar { .. }), _) => winit::window::CursorIcon::Default, |
| 1297 | (None, _) if scrollbar => winit::window::CursorIcon::Default, | 1378 | (None, _) if scrollbar => winit::window::CursorIcon::Default, |
| | 1379 | (Some(Drag::Image { handle, .. }), _) => handle_cursor(*handle), |
| | 1380 | (None, Some(Hit::Image { handle, .. })) => handle_cursor(handle), |
| 1298 | (Some(Drag::Resize { .. }), _) | (None, Some(Hit::Resize { .. })) => { | 1381 | (Some(Drag::Resize { .. }), _) | (None, Some(Hit::Resize { .. })) => { |
| 1299 | winit::window::CursorIcon::EwResize | 1382 | winit::window::CursorIcon::EwResize |
| 1300 | } | 1383 | } |
| ... | @@ -1330,7 +1413,9 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1330,7 +1413,9 @@ impl ApplicationHandler<UserEvent> for App { |
| 1330 | .select(drag_selection(*anchor, target, *unit))?; | 1413 | .select(drag_selection(*anchor, target, *unit))?; |
| 1331 | state.changed()?; | 1414 | state.changed()?; |
| 1332 | } | 1415 | } |
| 1333 | Some(Drag::Outline { pending_press, .. }) => { | 1416 | Some( |
| | 1417 | Drag::Outline { pending_press, .. } | Drag::Image { pending_press, .. }, |
| | 1418 | ) => { |
| 1334 | if pending_press.is_some_and(|press| { | 1419 | if pending_press.is_some_and(|press| { |
| 1335 | (0..2).any(|axis| { | 1420 | (0..2).any(|axis| { |
| 1336 | (state.pointer[axis] - press[axis]).abs() | 1421 | (state.pointer[axis] - press[axis]).abs() |
| ... | @@ -1387,18 +1472,22 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1387,18 +1472,22 @@ impl ApplicationHandler<UserEvent> for App { |
| 1387 | if scrollbar.is_some() { | 1472 | if scrollbar.is_some() { |
| 1388 | state.drag = scrollbar; | 1473 | state.drag = scrollbar; |
| 1389 | } else { | 1474 | } else { |
| 1390 | match page_hit_test( | 1475 | match state.hit_test(point) { |
| 1391 | &state.editor, | | |
| 1392 | state.scene.as_ref(), | | |
| 1393 | point, | | |
| 1394 | state.display_scale / state.viewport.scale, | | |
| 1395 | ) { | | |
| 1396 | Some(Hit::Date(field)) => state.edit_date(field)?, | 1476 | Some(Hit::Date(field)) => state.edit_date(field)?, |
| 1397 | Some(Hit::ReadOnly(index)) => { | 1477 | Some(Hit::ReadOnly(index)) => { |
| 1398 | state.set_read_only_focus(Some(index)) | 1478 | state.set_object_focus(Some(ObjectFocus::ReadOnly(index))) |
| | 1479 | } |
| | 1480 | Some(Hit::Image { id, handle }) => { |
| | 1481 | state.set_object_focus(Some(ObjectFocus::Image(id))); |
| | 1482 | state.drag = Some(Drag::Image { |
| | 1483 | id, |
| | 1484 | handle, |
| | 1485 | press: point, |
| | 1486 | pending_press: Some(state.pointer), |
| | 1487 | }); |
| 1399 | } | 1488 | } |
| 1400 | Some(Hit::Handle { id, grab }) => { | 1489 | Some(Hit::Handle { id, grab }) => { |
| 1401 | state.set_read_only_focus(None); | 1490 | state.set_object_focus(None); |
| 1402 | state.editor.focus_outline(id)?; | 1491 | state.editor.focus_outline(id)?; |
| 1403 | state.drag = Some(Drag::Outline { | 1492 | state.drag = Some(Drag::Outline { |
| 1404 | id, | 1493 | id, |
| ... | @@ -1407,7 +1496,7 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1407,7 +1496,7 @@ impl ApplicationHandler<UserEvent> for App { |
| 1407 | }); | 1496 | }); |
| 1408 | } | 1497 | } |
| 1409 | Some(Hit::Resize { id, grab }) => { | 1498 | Some(Hit::Resize { id, grab }) => { |
| 1410 | state.set_read_only_focus(None); | 1499 | state.set_object_focus(None); |
| 1411 | state.editor.focus_outline(id)?; | 1500 | state.editor.focus_outline(id)?; |
| 1412 | state.drag = Some(Drag::Resize { | 1501 | state.drag = Some(Drag::Resize { |
| 1413 | outline: None, | 1502 | outline: None, |
| ... | @@ -1416,9 +1505,9 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1416,9 +1505,9 @@ impl ApplicationHandler<UserEvent> for App { |
| 1416 | } | 1505 | } |
| 1417 | Some(Hit::Text { id, point }) => { | 1506 | Some(Hit::Text { id, point }) => { |
| 1418 | let extend = state.modifiers.shift_key() | 1507 | let extend = state.modifiers.shift_key() |
| 1419 | && state.read_only_focus.is_none() | 1508 | && state.object_focus.is_none() |
| 1420 | && id == state.editor.active_outline().id; | 1509 | && id == state.editor.active_outline().id; |
| 1421 | state.set_read_only_focus(None); | 1510 | state.set_object_focus(None); |
| 1422 | state.editor.focus_outline(id)?; | 1511 | state.editor.focus_outline(id)?; |
| 1423 | let previous = state.editor.selection(); | 1512 | let previous = state.editor.selection(); |
| 1424 | state.editor.select_below(&mut state.engine, id, point)?; | 1513 | state.editor.select_below(&mut state.engine, id, point)?; |
| ... | @@ -1464,7 +1553,7 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1464,7 +1553,7 @@ impl ApplicationHandler<UserEvent> for App { |
| 1464 | position, | 1553 | position, |
| 1465 | DEFAULT_OUTLINE_WIDTH, | 1554 | DEFAULT_OUTLINE_WIDTH, |
| 1466 | )?; | 1555 | )?; |
| 1467 | state.set_read_only_focus(None); | 1556 | state.set_object_focus(None); |
| 1468 | state.drag = Some(Drag::Text { | 1557 | state.drag = Some(Drag::Text { |
| 1469 | anchor: state.editor.selection(), | 1558 | anchor: state.editor.selection(), |
| 1470 | unit: SelectionUnit::Grapheme, | 1559 | unit: SelectionUnit::Grapheme, |
| ... | @@ -1474,6 +1563,9 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1474,6 +1563,9 @@ impl ApplicationHandler<UserEvent> for App { |
| 1474 | } | 1563 | } |
| 1475 | } else { | 1564 | } else { |
| 1476 | let preview = state.preview(); | 1565 | let preview = state.preview(); |
| | 1566 | if let Some((id, origin, size)) = state.image_preview() { |
| | 1567 | state.editor.place_image(id, origin, size)?; |
| | 1568 | } |
| 1477 | if let Some(Drag::Resize { | 1569 | if let Some(Drag::Resize { |
| 1478 | outline: Some(outline), | 1570 | outline: Some(outline), |
| 1479 | .. | 1571 | .. |
| ... | @@ -1508,7 +1600,7 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1508,7 +1600,7 @@ impl ApplicationHandler<UserEvent> for App { |
| 1508 | { | 1600 | { |
| 1509 | state.key(&event.logical_key, event.text.as_deref())?; | 1601 | state.key(&event.logical_key, event.text.as_deref())?; |
| 1510 | } | 1602 | } |
| 1511 | WindowEvent::Ime(_) if state.read_only_focus.is_some() => {} | 1603 | WindowEvent::Ime(_) if state.object_focus.is_some() => {} |
| 1512 | WindowEvent::Ime(Ime::Preedit(text, cursor)) => { | 1604 | WindowEvent::Ime(Ime::Preedit(text, cursor)) => { |
| 1513 | if text.is_empty() { | 1605 | if text.is_empty() { |
| 1514 | state.editor.cancel_composition(&mut state.engine)?; | 1606 | state.editor.cancel_composition(&mut state.engine)?; |
| ... | @@ -1558,7 +1650,7 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -1558,7 +1650,7 @@ impl ApplicationHandler<UserEvent> for App { |
| 1558 | return; | 1650 | return; |
| 1559 | }; | 1651 | }; |
| 1560 | let [anchor, focus] = state.editor.selection().positions; | 1652 | let [anchor, focus] = state.editor.selection().positions; |
| 1561 | if state.focused && !state.occluded && state.read_only_focus.is_none() && anchor == focus { | 1653 | if state.focused && !state.occluded && state.object_focus.is_none() && anchor == focus { |
| 1562 | let now = Instant::now(); | 1654 | let now = Instant::now(); |
| 1563 | if now >= state.blink_at { | 1655 | if now >= state.blink_at { |
| 1564 | state.caret = !state.caret; | 1656 | state.caret = !state.caret; |
| ... | @@ -1588,6 +1680,11 @@ enum Hit { | ... | @@ -1588,6 +1680,11 @@ enum Hit { |
| 1588 | grab: [f32; 2], | 1680 | grab: [f32; 2], |
| 1589 | }, | 1681 | }, |
| 1590 | ReadOnly(usize), | 1682 | ReadOnly(usize), |
| | 1683 | /// `handle` is [0, 0] on the picture and a direction on the selected picture's handles. |
| | 1684 | Image { |
| | 1685 | id: onestore::ExGuid, |
| | 1686 | handle: [i8; 2], |
| | 1687 | }, |
| 1591 | } | 1688 | } |
| 1592 | | 1689 | |
| 1593 | fn page_hit_test( | 1690 | fn page_hit_test( |
| ... | @@ -1696,6 +1793,7 @@ fn page_hit_test( | ... | @@ -1696,6 +1793,7 @@ fn page_hit_test( |
| 1696 | canvas::gpu::page::SceneHit::Outline(hit) => Some(hit), | 1793 | canvas::gpu::page::SceneHit::Outline(hit) => Some(hit), |
| 1697 | canvas::gpu::page::SceneHit::Date(field) => Some(Hit::Date(field)), | 1794 | canvas::gpu::page::SceneHit::Date(field) => Some(Hit::Date(field)), |
| 1698 | canvas::gpu::page::SceneHit::ReadOnly(index) => Some(Hit::ReadOnly(index)), | 1795 | canvas::gpu::page::SceneHit::ReadOnly(index) => Some(Hit::ReadOnly(index)), |
| | 1796 | canvas::gpu::page::SceneHit::Image(id) => Some(Hit::Image { id, handle: [0, 0] }), |
| 1699 | } | 1797 | } |
| 1700 | }; | 1798 | }; |
| 1701 | [Layer::Grips, Layer::Body, Layer::Below] | 1799 | [Layer::Grips, Layer::Body, Layer::Below] |
| ... | @@ -1707,7 +1805,7 @@ fn page_primitives<'a>( | ... | @@ -1707,7 +1805,7 @@ fn page_primitives<'a>( |
| 1707 | editor: &'a CanvasEditor, | 1805 | editor: &'a CanvasEditor, |
| 1708 | scene: Option<&'a (PageScene, [f32; 2])>, | 1806 | scene: Option<&'a (PageScene, [f32; 2])>, |
| 1709 | preview: Option<PointerFeedback<'a>>, | 1807 | preview: Option<PointerFeedback<'a>>, |
| 1710 | read_only_focus: Option<usize>, | 1808 | object_focus: Option<ObjectFocus>, |
| 1711 | show_caret: bool, | 1809 | show_caret: bool, |
| 1712 | scale: f32, | 1810 | scale: f32, |
| 1713 | display_scale: f32, | 1811 | display_scale: f32, |
| ... | @@ -1726,7 +1824,7 @@ fn page_primitives<'a>( | ... | @@ -1726,7 +1824,7 @@ fn page_primitives<'a>( |
| 1726 | Some(PointerFeedback::Move(id, origin)) if id == outline.id => origin, | 1824 | Some(PointerFeedback::Move(id, origin)) if id == outline.id => origin, |
| 1727 | _ => outline.origin(), | 1825 | _ => outline.origin(), |
| 1728 | }; | 1826 | }; |
| 1729 | if (read_only_focus.is_none() && outline.id == editor.active_outline().id) | 1827 | if (object_focus.is_none() && outline.id == editor.active_outline().id) |
| 1730 | || matches!(preview, Some(PointerFeedback::Hover(id) | PointerFeedback::Move(id, _)) if id == outline.id) | 1828 | || matches!(preview, Some(PointerFeedback::Hover(id) | PointerFeedback::Move(id, _)) if id == outline.id) |
| 1731 | || matches!(preview, Some(PointerFeedback::Resize(resized)) if resized.id == outline.id) | 1829 | || matches!(preview, Some(PointerFeedback::Resize(resized)) if resized.id == outline.id) |
| 1732 | { | 1830 | { |
| ... | @@ -1738,7 +1836,7 @@ fn page_primitives<'a>( | ... | @@ -1738,7 +1836,7 @@ fn page_primitives<'a>( |
| 1738 | ); | 1836 | ); |
| 1739 | } | 1837 | } |
| 1740 | append_outline( | 1838 | append_outline( |
| 1741 | (read_only_focus.is_none() | 1839 | (object_focus.is_none() |
| 1742 | && outline.id == editor.active_outline().id | 1840 | && outline.id == editor.active_outline().id |
| 1743 | && !matches!(preview, Some(PointerFeedback::Resize(_)))) | 1841 | && !matches!(preview, Some(PointerFeedback::Resize(_)))) |
| 1744 | .then_some(editor), | 1842 | .then_some(editor), |
| ... | @@ -1751,7 +1849,11 @@ fn page_primitives<'a>( | ... | @@ -1751,7 +1849,11 @@ fn page_primitives<'a>( |
| 1751 | ) | 1849 | ) |
| 1752 | }; | 1850 | }; |
| 1753 | if let Some((scene, origin)) = scene { | 1851 | if let Some((scene, origin)) = scene { |
| 1754 | scene.append_primitives_with(&mut primitives, *origin, Some(editor), &paint)?; | 1852 | let moving = match preview { |
| | 1853 | Some(PointerFeedback::Image(id, origin, size)) => Some((id, image_rect(origin, size))), |
| | 1854 | _ => None, |
| | 1855 | }; |
| | 1856 | scene.append_primitives_with(&mut primitives, *origin, Some(editor), moving, &paint)?; |
| 1755 | } | 1857 | } |
| 1756 | for outline in editor | 1858 | for outline in editor |
| 1757 | .visible_outlines() | 1859 | .visible_outlines() |
| ... | @@ -1759,7 +1861,7 @@ fn page_primitives<'a>( | ... | @@ -1759,7 +1861,7 @@ fn page_primitives<'a>( |
| 1759 | { | 1861 | { |
| 1760 | paint(outline.id, [0.0; 2], &mut primitives)?; | 1862 | paint(outline.id, [0.0; 2], &mut primitives)?; |
| 1761 | } | 1863 | } |
| 1762 | if read_only_focus.is_none() | 1864 | if object_focus.is_none() |
| 1763 | && let Some(outline) = editor.caret_outline() | 1865 | && let Some(outline) = editor.caret_outline() |
| 1764 | { | 1866 | { |
| 1765 | append_outline( | 1867 | append_outline( |
| ... | @@ -1772,7 +1874,20 @@ fn page_primitives<'a>( | ... | @@ -1772,7 +1874,20 @@ fn page_primitives<'a>( |
| 1772 | &mut primitives, | 1874 | &mut primitives, |
| 1773 | )?; | 1875 | )?; |
| 1774 | } | 1876 | } |
| 1775 | if let Some(index) = read_only_focus { | 1877 | if let Some(ObjectFocus::Image(id)) = object_focus { |
| | 1878 | let (origin, size) = match preview { |
| | 1879 | Some(PointerFeedback::Image(moving, origin, size)) if moving == id => (origin, size), |
| | 1880 | _ => editor |
| | 1881 | .image_placement(id) |
| | 1882 | .ok_or("The selected picture is missing.")?, |
| | 1883 | }; |
| | 1884 | append_image_chrome( |
| | 1885 | image_rect(origin, size), |
| | 1886 | display_scale / scale, |
| | 1887 | &mut primitives, |
| | 1888 | ); |
| | 1889 | } |
| | 1890 | if let Some(ObjectFocus::ReadOnly(index)) = object_focus { |
| 1776 | let (scene, offset) = scene.unwrap(); | 1891 | let (scene, offset) = scene.unwrap(); |
| 1777 | let [x0, y0, x1, y1] = scene.read_only(Some(editor)).nth(index).unwrap().rect(); | 1892 | let [x0, y0, x1, y1] = scene.read_only(Some(editor)).nth(index).unwrap().rect(); |
| 1778 | let [x0, y0, x1, y1] = [ | 1893 | let [x0, y0, x1, y1] = [ |
| ... | @@ -1797,6 +1912,124 @@ fn page_primitives<'a>( | ... | @@ -1797,6 +1912,124 @@ fn page_primitives<'a>( |
| 1797 | Ok(primitives) | 1912 | Ok(primitives) |
| 1798 | } | 1913 | } |
| 1799 | | 1914 | |
| | 1915 | /// Native picture handles sit on a selection border drawn 5 px outside the picture, named |
| | 1916 | /// by their direction from its center. |
| | 1917 | fn image_handles(rect: [f32; 4], pixel: f32) -> impl Iterator<Item = ([i8; 2], [f32; 2])> { |
| | 1918 | let border = [ |
| | 1919 | rect[0] - 5.0 * pixel, |
| | 1920 | rect[1] - 5.0 * pixel, |
| | 1921 | rect[2] + 5.0 * pixel, |
| | 1922 | rect[3] + 5.0 * pixel, |
| | 1923 | ]; |
| | 1924 | [-1, 0, 1] |
| | 1925 | .into_iter() |
| | 1926 | .flat_map(|y| [-1, 0, 1].map(|x| [x, y])) |
| | 1927 | .filter(|handle| *handle != [0, 0]) |
| | 1928 | .map(move |handle| { |
| | 1929 | ( |
| | 1930 | handle, |
| | 1931 | std::array::from_fn(|axis| { |
| | 1932 | let [start, end] = [border[axis], border[axis + 2]]; |
| | 1933 | start + (end - start) * f32::from(handle[axis] + 1) * 0.5 |
| | 1934 | }), |
| | 1935 | ) |
| | 1936 | }) |
| | 1937 | } |
| | 1938 | |
| | 1939 | fn image_handle_at(rect: [f32; 4], pixel: f32, point: [f32; 2]) -> Option<[i8; 2]> { |
| | 1940 | image_handles(rect, pixel) |
| | 1941 | .find(|(_, center)| (0..2).all(|axis| (point[axis] - center[axis]).abs() <= 5.0 * pixel)) |
| | 1942 | .map(|(handle, _)| handle) |
| | 1943 | } |
| | 1944 | |
| | 1945 | fn handle_cursor(handle: [i8; 2]) -> winit::window::CursorIcon { |
| | 1946 | use winit::window::CursorIcon; |
| | 1947 | match handle { |
| | 1948 | [0, 0] => CursorIcon::Move, |
| | 1949 | [_, 0] => CursorIcon::EwResize, |
| | 1950 | [0, _] => CursorIcon::NsResize, |
| | 1951 | [x, y] if x == y => CursorIcon::NwseResize, |
| | 1952 | _ => CursorIcon::NeswResize, |
| | 1953 | } |
| | 1954 | } |
| | 1955 | |
| | 1956 | fn image_rect(origin: [f32; 2], size: [f32; 2]) -> [f32; 4] { |
| | 1957 | [ |
| | 1958 | origin[0], |
| | 1959 | origin[1], |
| | 1960 | origin[0] + size[0], |
| | 1961 | origin[1] + size[1], |
| | 1962 | ] |
| | 1963 | } |
| | 1964 | |
| | 1965 | /// Drags a picture's `handle` by `delta`: edges stretch one axis, corners keep the aspect |
| | 1966 | /// ratio at the larger of the two scales, and the opposite side stays fixed. |
| | 1967 | fn resize_image( |
| | 1968 | origin: [f32; 2], |
| | 1969 | size: [f32; 2], |
| | 1970 | handle: [i8; 2], |
| | 1971 | delta: [f32; 2], |
| | 1972 | ) -> ([f32; 2], [f32; 2]) { |
| | 1973 | let mut scale: [f32; 2] = std::array::from_fn(|axis| { |
| | 1974 | (size[axis] + f32::from(handle[axis]) * delta[axis]) / size[axis] |
| | 1975 | }); |
| | 1976 | if !handle.contains(&0) { |
| | 1977 | scale = [scale[0].max(scale[1]); 2]; |
| | 1978 | } |
| | 1979 | let resized: [f32; 2] = std::array::from_fn(|axis| (size[axis] * scale[axis]).max(1.0)); |
| | 1980 | let origin = std::array::from_fn(|axis| { |
| | 1981 | if handle[axis] < 0 { |
| | 1982 | origin[axis] + size[axis] - resized[axis] |
| | 1983 | } else { |
| | 1984 | origin[axis] |
| | 1985 | } |
| | 1986 | }); |
| | 1987 | (origin, resized) |
| | 1988 | } |
| | 1989 | |
| | 1990 | fn append_image_chrome(rect: [f32; 4], pixel: f32, primitives: &mut Vec<Primitive<'_>>) { |
| | 1991 | let mut tint = canvas::gpu::colorref(0x00e0d2e6); |
| | 1992 | // Native pictures take this tint at 25% in sRGB; 10% in linear light matches it. |
| | 1993 | tint[3] = 0.1; |
| | 1994 | primitives.push(Primitive::Rect { rect, color: tint }); |
| | 1995 | primitives.push(Primitive::RoundedRect { |
| | 1996 | rect: [ |
| | 1997 | rect[0] - 5.0 * pixel, |
| | 1998 | rect[1] - 5.0 * pixel, |
| | 1999 | rect[2] + 5.0 * pixel, |
| | 2000 | rect[3] + 5.0 * pixel, |
| | 2001 | ], |
| | 2002 | radius: [0.0; 2], |
| | 2003 | stroke: Some(Stroke::Dashed(pixel)), |
| | 2004 | color: canvas::gpu::colorref(0x00ff9a31), |
| | 2005 | }); |
| | 2006 | for (handle, [x, y]) in image_handles(rect, pixel) { |
| | 2007 | let (half, radius) = if handle.contains(&0) { |
| | 2008 | (3.5, 0.0) |
| | 2009 | } else { |
| | 2010 | (4.0, 4.0) |
| | 2011 | }; |
| | 2012 | let rect = [ |
| | 2013 | x - half * pixel, |
| | 2014 | y - half * pixel, |
| | 2015 | x + half * pixel, |
| | 2016 | y + half * pixel, |
| | 2017 | ]; |
| | 2018 | primitives.push(Primitive::RoundedRect { |
| | 2019 | rect, |
| | 2020 | radius: [radius * pixel; 2], |
| | 2021 | stroke: None, |
| | 2022 | color: canvas::gpu::colorref(0x00ffefe7), |
| | 2023 | }); |
| | 2024 | primitives.push(Primitive::RoundedRect { |
| | 2025 | rect, |
| | 2026 | radius: [radius * pixel; 2], |
| | 2027 | stroke: Some(Stroke::Solid(pixel)), |
| | 2028 | color: canvas::gpu::colorref(0x00dea67b), |
| | 2029 | }); |
| | 2030 | } |
| | 2031 | } |
| | 2032 | |
| 1800 | fn drag_selection(anchor: Selection, target: Selection, unit: SelectionUnit) -> Selection { | 2033 | fn drag_selection(anchor: Selection, target: Selection, unit: SelectionUnit) -> Selection { |
| 1801 | if unit == SelectionUnit::Grapheme { | 2034 | if unit == SelectionUnit::Grapheme { |
| 1802 | return Selection { | 2035 | return Selection { |
| ... | @@ -2500,6 +2733,33 @@ mod tests { | ... | @@ -2500,6 +2733,33 @@ mod tests { |
| 2500 | ); | 2733 | ); |
| 2501 | } | 2734 | } |
| 2502 | | 2735 | |
| | 2736 | #[test] |
| | 2737 | fn picture_handles_resize_as_onenote_does() { |
| | 2738 | // Native drags of the 333 x 200.1 pt mockup on "av: casual night in the trees". |
| | 2739 | let (origin, size) = ([468.0, 86.4], [333.0, 200.1]); |
| | 2740 | let (corner_origin, corner) = resize_image(origin, size, [1, -1], [-75.0, 30.75]); |
| | 2741 | assert!((corner[0] - 281.827).abs() < 0.01 && (corner[1] - 169.351).abs() < 0.01); |
| | 2742 | assert!((corner_origin[1] - 117.15).abs() < 0.01 && corner_origin[0] == 468.0); |
| | 2743 | assert_eq!( |
| | 2744 | resize_image(origin, size, [1, 0], [46.5, 10.0]), |
| | 2745 | (origin, [379.5, 200.1]) |
| | 2746 | ); |
| | 2747 | let (left_origin, left) = resize_image(origin, size, [-1, 0], [400.0, 0.0]); |
| | 2748 | assert_eq!(left, [1.0, 200.1]); |
| | 2749 | assert_eq!(left_origin[0], 468.0 + 333.0 - 1.0); |
| | 2750 | let pixel = 0.75; |
| | 2751 | let rect = image_rect(origin, size); |
| | 2752 | assert_eq!( |
| | 2753 | image_handle_at(rect, pixel, [468.0 - 3.75, 86.4 - 3.75]), |
| | 2754 | Some([-1, -1]) |
| | 2755 | ); |
| | 2756 | assert_eq!( |
| | 2757 | image_handle_at(rect, pixel, [468.0 + 166.5, 286.5 + 3.75]), |
| | 2758 | Some([0, 1]) |
| | 2759 | ); |
| | 2760 | assert_eq!(image_handle_at(rect, pixel, [600.0, 150.0]), None); |
| | 2761 | } |
| | 2762 | |
| 2503 | #[test] | 2763 | #[test] |
| 2504 | fn grouped_drag_keeps_the_initial_word_when_reversing_direction() { | 2764 | fn grouped_drag_keeps_the_initial_word_when_reversing_direction() { |
| 2505 | let selection = |start, end| { | 2765 | let selection = |start, end| { |
| ... | @@ -3257,7 +3517,16 @@ mod tests { | ... | @@ -3257,7 +3517,16 @@ mod tests { |
| 3257 | .any(|(_, color)| *color == macos::text_colors()[0]) | 3517 | .any(|(_, color)| *color == macos::text_colors()[0]) |
| 3258 | ); | 3518 | ); |
| 3259 | let selected = rectangles( | 3519 | let selected = rectangles( |
| 3260 | page_primitives(&editor, Some(&scene), None, Some(0), true, scale, 1.0).unwrap(), | 3520 | page_primitives( |
| | 3521 | &editor, |
| | 3522 | Some(&scene), |
| | 3523 | None, |
| | 3524 | Some(ObjectFocus::ReadOnly(0)), |
| | 3525 | true, |
| | 3526 | scale, |
| | 3527 | 1.0, |
| | 3528 | ) |
| | 3529 | .unwrap(), |
| 3261 | ); | 3530 | ); |
| 3262 | assert!( | 3531 | assert!( |
| 3263 | !selected | 3532 | !selected |
| ... | @@ -3267,8 +3536,16 @@ mod tests { | ... | @@ -3267,8 +3536,16 @@ mod tests { |
| 3267 | assert_eq!( | 3536 | assert_eq!( |
| 3268 | selected, | 3537 | selected, |
| 3269 | rectangles( | 3538 | rectangles( |
| 3270 | page_primitives(&editor, Some(&scene), None, Some(0), false, scale, 1.0) | 3539 | page_primitives( |
| 3271 | .unwrap() | 3540 | &editor, |
| | 3541 | Some(&scene), |
| | 3542 | None, |
| | 3543 | Some(ObjectFocus::ReadOnly(0)), |
| | 3544 | false, |
| | 3545 | scale, |
| | 3546 | 1.0 |
| | 3547 | ) |
| | 3548 | .unwrap() |
| 3272 | ) | 3549 | ) |
| 3273 | ); | 3550 | ); |
| 3274 | assert_eq!( | 3551 | assert_eq!( |
| ... | @@ -3285,7 +3562,16 @@ mod tests { | ... | @@ -3285,7 +3562,16 @@ mod tests { |
| 3285 | .any(|(_, color)| *color == macos::text_colors()[1]) | 3562 | .any(|(_, color)| *color == macos::text_colors()[1]) |
| 3286 | ); | 3563 | ); |
| 3287 | let selected = rectangles( | 3564 | let selected = rectangles( |
| 3288 | page_primitives(&editor, Some(&scene), None, Some(0), true, 1.0, 1.0).unwrap(), | 3565 | page_primitives( |
| | 3566 | &editor, |
| | 3567 | Some(&scene), |
| | 3568 | None, |
| | 3569 | Some(ObjectFocus::ReadOnly(0)), |
| | 3570 | true, |
| | 3571 | 1.0, |
| | 3572 | 1.0, |
| | 3573 | ) |
| | 3574 | .unwrap(), |
| 3289 | ); | 3575 | ); |
| 3290 | assert!( | 3576 | assert!( |
| 3291 | !selected | 3577 | !selected |