Add quick measure overlays with Alt pressed (#1894)
* Measure with alt * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
501b562d0f
commit
1f278799d6
|
|
@ -78,7 +78,9 @@ impl OverlayContext {
|
|||
let position = position.round() - DVec2::splat(0.5);
|
||||
|
||||
self.render_context.begin_path();
|
||||
self.render_context.arc(position.x, position.y, MANIPULATOR_GROUP_MARKER_SIZE / 2., 0., TAU).expect("draw circle");
|
||||
self.render_context
|
||||
.arc(position.x, position.y, MANIPULATOR_GROUP_MARKER_SIZE / 2., 0., TAU)
|
||||
.expect("Failed to draw the circle");
|
||||
|
||||
let fill = if selected { COLOR_OVERLAY_BLUE } else { COLOR_OVERLAY_WHITE };
|
||||
self.render_context.set_fill_style(&wasm_bindgen::JsValue::from_str(fill));
|
||||
|
|
@ -127,7 +129,7 @@ impl OverlayContext {
|
|||
let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);
|
||||
let position = position.round();
|
||||
self.render_context.begin_path();
|
||||
self.render_context.arc(position.x, position.y, radius, 0., TAU).expect("draw circle");
|
||||
self.render_context.arc(position.x, position.y, radius, 0., TAU).expect("Failed to draw the circle");
|
||||
self.render_context.set_fill_style(&wasm_bindgen::JsValue::from_str(color_fill));
|
||||
self.render_context.set_stroke_style(&wasm_bindgen::JsValue::from_str(color_stroke));
|
||||
self.render_context.fill();
|
||||
|
|
@ -139,7 +141,7 @@ impl OverlayContext {
|
|||
// Circle
|
||||
|
||||
self.render_context.begin_path();
|
||||
self.render_context.arc(x, y, PIVOT_DIAMETER / 2., 0., TAU).expect("draw circle");
|
||||
self.render_context.arc(x, y, PIVOT_DIAMETER / 2., 0., TAU).expect("Failed to draw the circle");
|
||||
self.render_context.set_fill_style(&wasm_bindgen::JsValue::from_str(COLOR_OVERLAY_YELLOW));
|
||||
self.render_context.fill();
|
||||
|
||||
|
|
@ -247,7 +249,7 @@ impl OverlayContext {
|
|||
|
||||
pub fn text(&self, text: &str, pos: DVec2, background: &str, padding: f64) {
|
||||
let pos = pos.round();
|
||||
let metrics = self.render_context.measure_text(text).expect("measure text");
|
||||
let metrics = self.render_context.measure_text(text).expect("Failed to measure the text dimensions");
|
||||
self.render_context.set_fill_style(&background.into());
|
||||
self.render_context.fill_rect(
|
||||
pos.x + metrics.actual_bounding_box_left(),
|
||||
|
|
@ -258,6 +260,34 @@ impl OverlayContext {
|
|||
self.render_context.set_fill_style(&"white".into());
|
||||
self.render_context
|
||||
.fill_text(text, pos.x + padding, pos.y - padding - metrics.font_bounding_box_descent())
|
||||
.expect("draw text");
|
||||
.expect("Failed to draw the text on the canvas");
|
||||
}
|
||||
|
||||
pub fn angle_text(&self, text: &str, pos: DVec2, direction: DVec2, padding: f64, pivot: Pivot) {
|
||||
self.render_context.translate(pos.x, pos.y).expect("Failed to translate the render context to the specified position");
|
||||
|
||||
let angle = -direction.angle_to(DVec2::X);
|
||||
self.render_context.rotate(angle).expect("Failed to rotate the render context to the specified angle");
|
||||
|
||||
let metrics = self.render_context.measure_text(text).expect("Failed to measure the text dimensions");
|
||||
|
||||
self.render_context.set_fill_style(&wasm_bindgen::JsValue::from_str(COLOR_OVERLAY_BLUE));
|
||||
|
||||
let local_position = match pivot {
|
||||
Pivot::LeftCentreY => DVec2::new(padding, (metrics.actual_bounding_box_ascent() + metrics.actual_bounding_box_descent()) / 2.),
|
||||
Pivot::TopCentreX => DVec2::new(
|
||||
-(metrics.actual_bounding_box_right() + metrics.actual_bounding_box_left()) / 2.,
|
||||
padding + metrics.font_bounding_box_ascent(),
|
||||
),
|
||||
};
|
||||
self.render_context
|
||||
.fill_text(text, local_position.x, local_position.y)
|
||||
.expect("Failed to draw the text at the calculated position");
|
||||
self.render_context.reset_transform().expect("Failed to reset the render context transform");
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Pivot {
|
||||
LeftCentreY,
|
||||
TopCentreX,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
use crate::messages::portfolio::document::overlays::utility_types::{self, OverlayContext};
|
||||
use crate::messages::tool::tool_messages::tool_prelude::*;
|
||||
|
||||
use graphene_std::renderer::Rect;
|
||||
|
||||
pub fn overlay(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) {
|
||||
let transform_to_document = document_to_viewport.inverse() * transform;
|
||||
if selected_bounds.intersects(hovered_bounds) {
|
||||
// TODO: I'm not sure what to do here?
|
||||
return;
|
||||
}
|
||||
|
||||
// Always do horizontal then vertical from the selected
|
||||
let turn_x = selected_bounds.center().x.clamp(hovered_bounds.min().x, hovered_bounds.max().x);
|
||||
let turn_y = hovered_bounds.center().y.clamp(selected_bounds.min().y, selected_bounds.max().y);
|
||||
|
||||
let selected_x = turn_x.clamp(selected_bounds.min().x, selected_bounds.max().x);
|
||||
let hovered_y = turn_y.clamp(hovered_bounds.min().y, hovered_bounds.max().y);
|
||||
|
||||
if turn_x != selected_x {
|
||||
let min_viewport = transform.transform_point2(DVec2::new(turn_x.min(selected_x), turn_y));
|
||||
let max_viewport = transform.transform_point2(DVec2::new(turn_x.max(selected_x), turn_y));
|
||||
overlay_context.line(min_viewport, max_viewport);
|
||||
let length = format!("{:.2}", transform_to_document.transform_vector2(DVec2::X * (turn_x - selected_x)).length());
|
||||
let direction = -(min_viewport - max_viewport).normalize_or_zero();
|
||||
overlay_context.angle_text(&length, (min_viewport + max_viewport) / 2., direction, 5., utility_types::Pivot::TopCentreX);
|
||||
}
|
||||
if turn_y != hovered_y {
|
||||
let min_viewport = transform.transform_point2(DVec2::new(turn_x, turn_y.min(hovered_y)));
|
||||
let max_viewport = transform.transform_point2(DVec2::new(turn_x, turn_y.max(hovered_y)));
|
||||
overlay_context.line(min_viewport, max_viewport);
|
||||
let length = format!("{:.2}", transform_to_document.transform_vector2(DVec2::Y * (turn_y - hovered_y)).length());
|
||||
let direction = (min_viewport - max_viewport).normalize_or_zero().perp();
|
||||
overlay_context.angle_text(&length, (min_viewport + max_viewport) / 2., direction, 5., utility_types::Pivot::LeftCentreY);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
pub mod auto_panning;
|
||||
pub mod color_selector;
|
||||
pub mod graph_modification_utils;
|
||||
pub mod measure;
|
||||
pub mod pivot;
|
||||
pub mod resize;
|
||||
pub mod shape_editor;
|
||||
|
|
|
|||
|
|
@ -486,6 +486,7 @@ impl Fsm for PathToolFsmState {
|
|||
// `Self::InsertPoint` case:
|
||||
(Self::InsertPoint, PathToolMessage::MouseDown { .. } | PathToolMessage::Enter { .. }) => {
|
||||
tool_data.double_click_handled = true;
|
||||
// TODO: Don't use `Key::Shift` directly, instead take it as a variable from the input mappings list like in all other places
|
||||
let shift = input.keyboard.get(Key::Shift as usize);
|
||||
tool_data.end_insertion(shape_editor, responses, InsertEndKind::Add { shift })
|
||||
}
|
||||
|
|
@ -499,8 +500,11 @@ impl Fsm for PathToolFsmState {
|
|||
(Self::InsertPoint, PathToolMessage::GRS { key: propagate }) => {
|
||||
// MAYBE: use `InputMapperMessage::KeyDown(..)` instead
|
||||
match propagate {
|
||||
// TODO: Don't use `Key::G` directly, instead take it as a variable from the input mappings list like in all other places
|
||||
Key::KeyG => responses.add(TransformLayerMessage::BeginGrab),
|
||||
// TODO: Don't use `Key::R` directly, instead take it as a variable from the input mappings list like in all other places
|
||||
Key::KeyR => responses.add(TransformLayerMessage::BeginRotate),
|
||||
// TODO: Don't use `Key::S` directly, instead take it as a variable from the input mappings list like in all other places
|
||||
Key::KeyS => responses.add(TransformLayerMessage::BeginScale),
|
||||
_ => warn!("Unexpected GRS key"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,14 +10,15 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
|
|||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, NodeNetworkInterface, NodeTemplate};
|
||||
use crate::messages::portfolio::document::utility_types::transformation::Selected;
|
||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::is_layer_fed_by_node_of_name;
|
||||
use crate::messages::tool::common_functionality::pivot::Pivot;
|
||||
use crate::messages::tool::common_functionality::snapping::{self, SnapCandidatePoint, SnapData, SnapManager};
|
||||
use crate::messages::tool::common_functionality::transformation_cage::*;
|
||||
use crate::messages::tool::common_functionality::{auto_panning::AutoPanning, measure};
|
||||
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::renderer::Quad;
|
||||
use graphene_std::renderer::Rect;
|
||||
use graphene_std::vector::misc::BooleanOperation;
|
||||
|
||||
use std::fmt;
|
||||
|
|
@ -471,13 +472,27 @@ impl Fsm for SelectToolFsmState {
|
|||
|
||||
// Update the selection box
|
||||
overlay_context.quad(quad);
|
||||
}
|
||||
// Only highlight layers if the viewport is not being panned (middle mouse button is pressed)
|
||||
} else if !input.keyboard.get(Key::Mmb as usize) {
|
||||
// TODO: Don't use `Key::Mmb` directly, instead take it as a variable from the input mappings list like in all other places
|
||||
else if !input.keyboard.get(Key::Mmb as usize) {
|
||||
// Get the layer the user is hovering over
|
||||
let click = document.click(input);
|
||||
let not_selected_click = click.filter(|&hovered_layer| !document.network_interface.selected_nodes(&[]).unwrap().selected_layers_contains(hovered_layer, document.metadata()));
|
||||
if let Some(layer) = not_selected_click {
|
||||
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
|
||||
|
||||
// Measure with Alt held down
|
||||
// TODO: Don't use `Key::Alt` directly, instead take it as a variable from the input mappings list like in all other places
|
||||
if input.keyboard.get(Key::Alt as usize) {
|
||||
let hovered_bounds = document
|
||||
.metadata()
|
||||
.bounding_box_with_transform(layer, transform.inverse() * document.metadata().transform_to_viewport(layer));
|
||||
|
||||
if let [Some(selected_bounds), Some(hovered_bounds)] = [bounds, hovered_bounds].map(|rect| rect.map(Rect::from_box)) {
|
||||
measure::overlay(selected_bounds, hovered_bounds, transform, document.metadata().document_to_viewport, &mut overlay_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue