mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 18:38:13 +08:00
Add tests to the Ellipse, Artboard, and Fill tools (#2181)
* Add ellipse tests * Add tests for fill tool and re-enable some other tests * Code review * Fix Rust crate advisory --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
1190e82322
commit
b171eeba84
@@ -5622,6 +5622,34 @@ impl NodeNetworkInterface {
|
||||
self.force_set_upstream_to_chain(node_id, network_path);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_recursive(&self) -> NodesRecursiveIter<'_> {
|
||||
NodesRecursiveIter {
|
||||
stack: vec![&self.network],
|
||||
current_slice: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NodesRecursiveIter<'a> {
|
||||
stack: Vec<&'a NodeNetwork>,
|
||||
current_slice: Option<std::collections::hash_map::Iter<'a, NodeId, DocumentNode>>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for NodesRecursiveIter<'a> {
|
||||
type Item = (NodeId, &'a DocumentNode);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if let Some((id, node)) = self.current_slice.as_mut().and_then(|iter| iter.next()) {
|
||||
if let DocumentNodeImplementation::Network(network) = &node.implementation {
|
||||
self.stack.push(network);
|
||||
}
|
||||
return Some((*id, node));
|
||||
}
|
||||
let network = self.stack.pop()?;
|
||||
self.current_slice = Some(network.nodes.iter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
|
||||
@@ -1160,7 +1160,11 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
}
|
||||
|
||||
impl PortfolioMessageHandler {
|
||||
pub async fn introspect_node(&self, node_path: &[NodeId]) -> Result<Arc<dyn std::any::Any>, IntrospectError> {
|
||||
pub fn with_executor(executor: crate::node_graph_executor::NodeGraphExecutor) -> Self {
|
||||
Self { executor, ..Default::default() }
|
||||
}
|
||||
|
||||
pub async fn introspect_node(&self, node_path: &[NodeId]) -> Result<Arc<dyn std::any::Any + Send + Sync>, IntrospectError> {
|
||||
self.executor.introspect_node(node_path).await
|
||||
}
|
||||
|
||||
|
||||
@@ -394,6 +394,15 @@ impl<'a> NodeGraphLayer<'a> {
|
||||
.find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|reference| *reference == Some(node_name.to_string())))
|
||||
}
|
||||
|
||||
/// Node id of a protonode if it exists in the layer's primary flow
|
||||
pub fn upstream_node_id_from_protonode(&self, protonode_identifier: &'static str) -> Option<NodeId> {
|
||||
self.horizontal_layer_flow().find(move |node_id| {
|
||||
self.network_interface
|
||||
.implementation(node_id, &[])
|
||||
.is_some_and(move |implementation| *implementation == graph_craft::document::DocumentNodeImplementation::proto(protonode_identifier))
|
||||
})
|
||||
}
|
||||
|
||||
/// Find all of the inputs of a specific node within the layer's primary flow, up until the next layer is reached.
|
||||
pub fn find_node_inputs(&self, node_name: &str) -> Option<&'a Vec<NodeInput>> {
|
||||
self.horizontal_layer_flow()
|
||||
|
||||
@@ -3,18 +3,16 @@ use crate::messages::portfolio::document::graph_operation::utility_types::Transf
|
||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
use crate::messages::tool::common_functionality::snapping;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapCandidatePoint;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapData;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapTypeConfiguration;
|
||||
use crate::messages::tool::common_functionality::transformation_cage::*;
|
||||
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::renderer::Quad;
|
||||
|
||||
use glam::{IVec2, Vec2Swizzles};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ArtboardTool {
|
||||
fsm_state: ArtboardToolFsmState,
|
||||
@@ -112,7 +110,8 @@ struct ArtboardToolData {
|
||||
drag_current: DVec2,
|
||||
auto_panning: AutoPanning,
|
||||
snap_candidates: Vec<SnapCandidatePoint>,
|
||||
dragging_current_artboard_location: IVec2,
|
||||
dragging_current_artboard_location: glam::IVec2,
|
||||
draw: Resize,
|
||||
}
|
||||
|
||||
impl ArtboardToolData {
|
||||
@@ -256,14 +255,7 @@ impl Fsm for ArtboardToolFsmState {
|
||||
tool_data.get_snap_candidates(document, input);
|
||||
ArtboardToolFsmState::Dragging
|
||||
} else {
|
||||
tool_data.get_snap_candidates(document, input);
|
||||
|
||||
let point = SnapCandidatePoint::handle(to_document.transform_point2(input.mouse.position));
|
||||
|
||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
||||
|
||||
tool_data.drag_start = snapped.snapped_point_document;
|
||||
tool_data.drag_current = snapped.snapped_point_document;
|
||||
tool_data.draw.start(document, input);
|
||||
|
||||
ArtboardToolFsmState::Drawing
|
||||
};
|
||||
@@ -324,46 +316,15 @@ impl Fsm for ArtboardToolFsmState {
|
||||
ArtboardToolFsmState::Dragging
|
||||
}
|
||||
(ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => {
|
||||
let to_viewport = document.metadata().document_to_viewport;
|
||||
let ignore = if let Some(layer) = tool_data.selected_artboard { vec![layer] } else { vec![] };
|
||||
let snap_data = SnapData::ignore(document, input, &ignore);
|
||||
|
||||
let document_mouse = to_viewport.inverse().transform_point2(input.mouse.position);
|
||||
|
||||
let config = SnapTypeConfiguration::default();
|
||||
let snapped = tool_data.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), config);
|
||||
let snapped_mouse_position = to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
|
||||
tool_data.snap_manager.update_indicator(snapped);
|
||||
|
||||
let mut start = to_viewport.transform_point2(tool_data.drag_start);
|
||||
let mut size = snapped_mouse_position - start;
|
||||
|
||||
// Constrain axis
|
||||
if input.keyboard.get(constrain_axis_or_aspect as usize) {
|
||||
size = size.abs().max(size.abs().yx()) * size.signum();
|
||||
}
|
||||
|
||||
// From center
|
||||
if input.keyboard.get(center as usize) {
|
||||
start -= size;
|
||||
size *= 2.;
|
||||
}
|
||||
|
||||
let start = to_viewport.inverse().transform_point2(start);
|
||||
let size = to_viewport.inverse().transform_vector2(size);
|
||||
let end = start + size;
|
||||
|
||||
let [start, end] = tool_data.draw.calculate_points_ignore_layer(document, input, center, constrain_axis_or_aspect);
|
||||
if let Some(artboard) = tool_data.selected_artboard {
|
||||
if artboard == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("Selected artboard cannot be ROOT_PARENT");
|
||||
} else {
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
layer: artboard,
|
||||
location: start.min(end).round().as_ivec2(),
|
||||
dimensions: (start.round() - end.round()).abs().as_ivec2(),
|
||||
});
|
||||
}
|
||||
assert_ne!(artboard, LayerNodeIdentifier::ROOT_PARENT, "Selected artboard cannot be ROOT_PARENT");
|
||||
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
layer: artboard,
|
||||
location: start.min(end).round().as_ivec2(),
|
||||
dimensions: (start.round() - end.round()).abs().as_ivec2(),
|
||||
});
|
||||
} else {
|
||||
let id = NodeId::new();
|
||||
|
||||
@@ -374,8 +335,8 @@ impl Fsm for ArtboardToolFsmState {
|
||||
artboard: graphene_core::Artboard {
|
||||
graphic_group: graphene_core::GraphicGroupTable::default(),
|
||||
label: String::from("Artboard"),
|
||||
location: start.round().as_ivec2(),
|
||||
dimensions: IVec2::splat(1),
|
||||
location: start.min(end).round().as_ivec2(),
|
||||
dimensions: (start.round() - end.round()).abs().as_ivec2(),
|
||||
background: graphene_core::Color::WHITE,
|
||||
clip: false,
|
||||
},
|
||||
@@ -594,3 +555,102 @@ impl Fsm for ArtboardToolFsmState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_artboard {
|
||||
pub use crate::test_utils::test_prelude::*;
|
||||
|
||||
async fn get_artboards(editor: &mut EditorTestUtils) -> Vec<graphene_core::Artboard> {
|
||||
let instrumented = editor.eval_graph().await;
|
||||
instrumented.grab_all_input::<graphene_core::append_artboard::ArtboardInput>(&editor.runtime).collect()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn artboard_draw_simple() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.drag_tool(ToolType::Artboard, 10.1, 10.8, 19.9, 0.2, ModifierKeys::empty()).await;
|
||||
|
||||
let artboards = get_artboards(&mut editor).await;
|
||||
|
||||
assert_eq!(artboards.len(), 1);
|
||||
assert_eq!(artboards[0].location, IVec2::new(10, 0));
|
||||
assert_eq!(artboards[0].dimensions, IVec2::new(10, 11));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn artboard_draw_square() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.drag_tool(ToolType::Artboard, 10., 10., -10., 11., ModifierKeys::SHIFT).await;
|
||||
|
||||
let artboards = get_artboards(&mut editor).await;
|
||||
assert_eq!(artboards.len(), 1);
|
||||
assert_eq!(artboards[0].location, IVec2::new(-10, 10));
|
||||
assert_eq!(artboards[0].dimensions, IVec2::new(20, 20));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn artboard_draw_square_rotated() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor
|
||||
.handle_message(NavigationMessage::CanvasTiltSet {
|
||||
// 45 degree rotation of content clockwise
|
||||
angle_radians: f64::consts::FRAC_PI_4,
|
||||
})
|
||||
.await;
|
||||
// Viewport coordinates
|
||||
editor.drag_tool(ToolType::Artboard, 0., 0., 0., 10., ModifierKeys::SHIFT).await;
|
||||
|
||||
let artboards = get_artboards(&mut editor).await;
|
||||
assert_eq!(artboards.len(), 1);
|
||||
assert_eq!(artboards[0].location, IVec2::new(0, 0));
|
||||
let desired_size = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * 10.);
|
||||
assert_eq!(artboards[0].dimensions, desired_size.round().as_ivec2());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn artboard_draw_center_square_rotated() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
|
||||
editor.new_document().await;
|
||||
editor
|
||||
.handle_message(NavigationMessage::CanvasTiltSet {
|
||||
// 45 degree rotation of content clockwise
|
||||
angle_radians: f64::consts::FRAC_PI_4,
|
||||
})
|
||||
.await;
|
||||
// Viewport coordinates
|
||||
editor.drag_tool(ToolType::Artboard, 0., 0., 0., 10., ModifierKeys::SHIFT | ModifierKeys::ALT).await;
|
||||
|
||||
let artboards = get_artboards(&mut editor).await;
|
||||
assert_eq!(artboards.len(), 1);
|
||||
assert_eq!(artboards[0].location, DVec2::splat(f64::consts::FRAC_1_SQRT_2 * -10.).as_ivec2());
|
||||
let desired_size = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * 20.);
|
||||
assert_eq!(artboards[0].dimensions, desired_size.round().as_ivec2());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn artboard_delete() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
|
||||
editor.new_document().await;
|
||||
editor.drag_tool(ToolType::Artboard, 10.1, 10.8, 19.9, 0.2, ModifierKeys::default()).await;
|
||||
editor.press(Key::Delete, ModifierKeys::default()).await;
|
||||
|
||||
let artboards = get_artboards(&mut editor).await;
|
||||
assert_eq!(artboards.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn artboard_cancel() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
|
||||
editor.new_document().await;
|
||||
|
||||
editor.drag_tool_cancel_rmb(ToolType::Artboard).await;
|
||||
let artboards = get_artboards(&mut editor).await;
|
||||
assert_eq!(artboards.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,3 +315,134 @@ impl Fsm for EllipseToolFsmState {
|
||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Crosshair });
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_ellipse {
|
||||
pub use crate::test_utils::test_prelude::*;
|
||||
use glam::DAffine2;
|
||||
use graphene_core::vector::generator_nodes::ellipse;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct ResolvedEllipse {
|
||||
radius_x: f64,
|
||||
radius_y: f64,
|
||||
transform: DAffine2,
|
||||
}
|
||||
|
||||
async fn get_ellipse(editor: &mut EditorTestUtils) -> Vec<ResolvedEllipse> {
|
||||
let instrumented = editor.eval_graph().await;
|
||||
|
||||
let document = editor.active_document();
|
||||
let layers = document.metadata().all_layers();
|
||||
layers
|
||||
.filter_map(|layer| {
|
||||
let node_graph_layer = NodeGraphLayer::new(layer, &document.network_interface);
|
||||
let ellipse_node = node_graph_layer.upstream_node_id_from_protonode(ellipse::protonode_identifier())?;
|
||||
Some(ResolvedEllipse {
|
||||
radius_x: instrumented.grab_protonode_input::<ellipse::RadiusXInput>(&vec![ellipse_node], &editor.runtime).unwrap(),
|
||||
radius_y: instrumented.grab_protonode_input::<ellipse::RadiusYInput>(&vec![ellipse_node], &editor.runtime).unwrap(),
|
||||
transform: document.metadata().transform_to_document(layer),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn ellipse_draw_simple() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.drag_tool(ToolType::Ellipse, 10., 10., 19., 0., ModifierKeys::empty()).await;
|
||||
|
||||
assert_eq!(editor.active_document().metadata().all_layers().count(), 1);
|
||||
|
||||
let ellipse = get_ellipse(&mut editor).await;
|
||||
assert_eq!(ellipse.len(), 1);
|
||||
assert_eq!(
|
||||
ellipse[0],
|
||||
ResolvedEllipse {
|
||||
radius_x: 4.5,
|
||||
radius_y: 5.,
|
||||
transform: DAffine2::from_translation(DVec2::new(14.5, 5.)) // Uses center
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn ellipse_draw_circle() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.drag_tool(ToolType::Ellipse, 10., 10., -10., 11., ModifierKeys::SHIFT).await;
|
||||
|
||||
let ellipse = get_ellipse(&mut editor).await;
|
||||
assert_eq!(ellipse.len(), 1);
|
||||
assert_eq!(
|
||||
ellipse[0],
|
||||
ResolvedEllipse {
|
||||
radius_x: 10.,
|
||||
radius_y: 10.,
|
||||
transform: DAffine2::from_translation(DVec2::new(0., 20.)) // Uses center
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn ellipse_draw_square_rotated() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor
|
||||
.handle_message(NavigationMessage::CanvasTiltSet {
|
||||
// 45 degree rotation of content clockwise
|
||||
angle_radians: f64::consts::FRAC_PI_4,
|
||||
})
|
||||
.await;
|
||||
editor.drag_tool(ToolType::Ellipse, 0., 0., 1., 10., ModifierKeys::SHIFT).await; // Viewport coordinates
|
||||
|
||||
let ellipse = get_ellipse(&mut editor).await;
|
||||
assert_eq!(ellipse.len(), 1);
|
||||
println!("{ellipse:?}");
|
||||
// TODO: re-enable after https://github.com/GraphiteEditor/Graphite/issues/2370
|
||||
// assert_eq!(ellipse[0].radius_x, 5.);
|
||||
// assert_eq!(ellipse[0].radius_y, 5.);
|
||||
|
||||
// assert!(ellipse[0]
|
||||
// .transform
|
||||
// .abs_diff_eq(DAffine2::from_angle_translation(-f64::consts::FRAC_PI_4, DVec2::X * f64::consts::FRAC_1_SQRT_2 * 10.), 0.001));
|
||||
|
||||
float_eq!(ellipse[0].radius_x, 11. / core::f64::consts::SQRT_2 / 2.);
|
||||
float_eq!(ellipse[0].radius_y, 11. / core::f64::consts::SQRT_2 / 2.);
|
||||
assert!(ellipse[0].transform.abs_diff_eq(DAffine2::from_translation(DVec2::splat(11. / core::f64::consts::SQRT_2 / 2.)), 0.001));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn ellipse_draw_center_square_rotated() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor
|
||||
.handle_message(NavigationMessage::CanvasTiltSet {
|
||||
// 45 degree rotation of content clockwise
|
||||
angle_radians: f64::consts::FRAC_PI_4,
|
||||
})
|
||||
.await;
|
||||
editor.drag_tool(ToolType::Ellipse, 0., 0., 1., 10., ModifierKeys::SHIFT | ModifierKeys::ALT).await; // Viewport coordinates
|
||||
|
||||
let ellipse = get_ellipse(&mut editor).await;
|
||||
assert_eq!(ellipse.len(), 1);
|
||||
// TODO: re-enable after https://github.com/GraphiteEditor/Graphite/issues/2370
|
||||
// assert_eq!(ellipse[0].radius_x, 10.);
|
||||
// assert_eq!(ellipse[0].radius_y, 10.);
|
||||
// assert!(ellipse[0].transform.abs_diff_eq(DAffine2::from_angle(-f64::consts::FRAC_PI_4), 0.001));
|
||||
float_eq!(ellipse[0].radius_x, 11. / core::f64::consts::SQRT_2);
|
||||
float_eq!(ellipse[0].radius_y, 11. / core::f64::consts::SQRT_2);
|
||||
assert!(ellipse[0].transform.abs_diff_eq(DAffine2::IDENTITY, 0.001));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn ellipse_cancel() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.drag_tool_cancel_rmb(ToolType::Ellipse).await;
|
||||
|
||||
let ellipse = get_ellipse(&mut editor).await;
|
||||
assert_eq!(ellipse.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,3 +124,61 @@ impl Fsm for FillToolFsmState {
|
||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_fill {
|
||||
pub use crate::test_utils::test_prelude::*;
|
||||
use graphene_core::vector::fill;
|
||||
use graphene_std::vector::style::Fill;
|
||||
|
||||
async fn get_fills(editor: &mut EditorTestUtils) -> Vec<Fill> {
|
||||
let instrumented = editor.eval_graph().await;
|
||||
|
||||
instrumented.grab_all_input::<fill::FillInput<Fill>>(&editor.runtime).collect()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn ignore_artboard() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.drag_tool(ToolType::Artboard, 0., 0., 100., 100., ModifierKeys::empty()).await;
|
||||
editor.click_tool(ToolType::Fill, MouseKeys::LEFT, DVec2::new(2., 2.), ModifierKeys::empty()).await;
|
||||
assert!(get_fills(&mut editor,).await.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
// TODO: fix https://github.com/GraphiteEditor/Graphite/issues/2270
|
||||
#[should_panic]
|
||||
async fn ignore_raster() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.create_raster_image(Image::new(100, 100, Color::WHITE), Some((0., 0.))).await;
|
||||
editor.click_tool(ToolType::Fill, MouseKeys::LEFT, DVec2::new(2., 2.), ModifierKeys::empty()).await;
|
||||
assert!(get_fills(&mut editor,).await.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn primary() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.drag_tool(ToolType::Rectangle, 0., 0., 100., 100., ModifierKeys::empty()).await;
|
||||
editor.select_primary_color(Color::GREEN).await;
|
||||
editor.click_tool(ToolType::Fill, MouseKeys::LEFT, DVec2::new(2., 2.), ModifierKeys::empty()).await;
|
||||
let fills = get_fills(&mut editor).await;
|
||||
assert_eq!(fills.len(), 1);
|
||||
assert_eq!(fills[0], Fill::Solid(Color::GREEN));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn secondary() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.drag_tool(ToolType::Rectangle, 0., 0., 100., 100., ModifierKeys::empty()).await;
|
||||
let color = Color::YELLOW;
|
||||
editor.handle_message(ToolMessage::SelectSecondaryColor { color }).await;
|
||||
editor.click_tool(ToolType::Fill, MouseKeys::LEFT, DVec2::new(2., 2.), ModifierKeys::SHIFT).await;
|
||||
let fills = get_fills(&mut editor).await;
|
||||
assert_eq!(fills.len(), 1);
|
||||
assert_eq!(fills[0], Fill::Solid(color));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user