Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions editor/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ pub const SELECTION_DRAG_ANGLE: f64 = 90.;
pub const LAYER_ORIGIN_CROSS_DIAMETER: f64 = 10.;
pub const LAYER_ORIGIN_CROSS_THICKNESS: f64 = 1.;

// GUIDES
pub const GUIDE_HIT_TOLERANCE: f64 = 5.;

// PIVOT
pub const PIVOT_CROSSHAIR_THICKNESS: f64 = 1.;
pub const PIVOT_CROSSHAIR_LENGTH: f64 = 9.;
Expand Down
2 changes: 2 additions & 0 deletions editor/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ impl Dispatcher {
menu_bar_message_handler.canvas_tilted = document.document_ptz.tilt() != 0.;
menu_bar_message_handler.canvas_flipped = document.document_ptz.flip;
menu_bar_message_handler.rulers_visible = document.rulers_visible;
menu_bar_message_handler.guides_visible = document.guide_handler.guides_visible;
menu_bar_message_handler.node_graph_open = document.is_graph_overlay_open();
menu_bar_message_handler.has_selected_nodes = selected_nodes.selected_nodes().next().is_some();
menu_bar_message_handler.has_selected_layers = selected_nodes.selected_visible_layers(&document.network_interface).next().is_some();
Expand All @@ -262,6 +263,7 @@ impl Dispatcher {
menu_bar_message_handler.canvas_tilted = false;
menu_bar_message_handler.canvas_flipped = false;
menu_bar_message_handler.rulers_visible = false;
menu_bar_message_handler.guides_visible = false;
menu_bar_message_handler.node_graph_open = false;
menu_bar_message_handler.has_selected_nodes = false;
menu_bar_message_handler.has_selected_layers = false;
Expand Down
7 changes: 7 additions & 0 deletions editor/src/messages/menu_bar/menu_bar_message_handler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
use crate::messages::input_mapper::utility_types::macros::action_shortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::guide_message::GuideMessage;
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType};
use crate::messages::prelude::*;
use graphene_std::path_bool::BooleanOperation;
Expand All @@ -11,6 +12,7 @@ pub struct MenuBarMessageHandler {
pub canvas_tilted: bool,
pub canvas_flipped: bool,
pub rulers_visible: bool,
pub guides_visible: bool,
pub node_graph_open: bool,
pub has_selected_nodes: bool,
pub has_selected_layers: bool,
Expand Down Expand Up @@ -616,6 +618,11 @@ impl LayoutHolder for MenuBarMessageHandler {
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::ToggleRulers))
.on_commit(|_| PortfolioMessage::ToggleRulers.into())
.disabled(no_active_document),
MenuListEntry::new("Guides")
.label("Guides")
.icon(if self.guides_visible { "CheckboxChecked" } else { "CheckboxUnchecked" })
.on_commit(|_| GuideMessage::ToggleGuidesVisibility.into())
.disabled(no_active_document),
],
])
.widget_instance(),
Expand Down
3 changes: 3 additions & 0 deletions editor/src/messages/portfolio/document/document_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::path::PathBuf;
use super::utility_types::misc::{GroupFolderType, SnappingState};
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
use crate::messages::portfolio::document::data_panel::DataPanelMessage;
use crate::messages::portfolio::document::guide_message::GuideMessage;
use crate::messages::portfolio::document::overlays::utility_types::{OverlayContext, OverlaysType};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GridSnapping};
Expand Down Expand Up @@ -35,6 +36,8 @@ pub enum DocumentMessage {
PropertiesPanel(PropertiesPanelMessage),
#[child]
DataPanel(DataPanelMessage),
#[child]
Guide(GuideMessage),

// Messages
AlignSelectedLayers {
Expand Down
15 changes: 15 additions & 0 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::messages::input_mapper::utility_types::macros::action_shortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::data_panel::{DataPanelMessageContext, DataPanelMessageHandler};
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::guide_message_handler::{GuideMessageContext, GuideMessageHandler};
use crate::messages::portfolio::document::node_graph::NodeGraphMessageContext;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
Expand Down Expand Up @@ -76,6 +77,8 @@ pub struct DocumentMessageHandler {
pub properties_panel_message_handler: PropertiesPanelMessageHandler,
#[serde(skip)]
pub data_panel_message_handler: DataPanelMessageHandler,
#[serde(flatten)]
pub guide_handler: GuideMessageHandler,

// ============================================
// Fields that are saved in the document format
Expand Down Expand Up @@ -155,6 +158,7 @@ impl Default for DocumentMessageHandler {
overlays_message_handler: OverlaysMessageHandler::default(),
properties_panel_message_handler: PropertiesPanelMessageHandler::default(),
data_panel_message_handler: DataPanelMessageHandler::default(),
guide_handler: GuideMessageHandler::default(),
// ============================================
// Fields that are saved in the document format
// ============================================
Expand All @@ -180,6 +184,7 @@ impl Default for DocumentMessageHandler {
saved_hash: None,
auto_saved_hash: None,
layer_range_selection_reference: None,

is_loaded: false,
}
}
Expand Down Expand Up @@ -216,6 +221,14 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes

self.navigation_handler.process_message(message, responses, context);
}
DocumentMessage::Guide(message) => {
let context = GuideMessageContext {
navigation_handler: &self.navigation_handler,
document_ptz: &self.document_ptz,
viewport,
};
self.guide_handler.process_message(message, responses, context);
}
DocumentMessage::Overlays(message) => {
let visibility_settings = self.overlays_visibility_settings;

Expand Down Expand Up @@ -622,6 +635,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
self.snapping_state.grid_snapping = visible;
responses.add(OverlaysMessage::Draw);
}
// Guide messages
DocumentMessage::GroupSelectedLayers { group_folder_type } => {
responses.add(DocumentMessage::AddTransaction);

Expand Down Expand Up @@ -1628,6 +1642,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
ZoomCanvasTo200Percent,
ZoomCanvasToFitAll,
);
common.extend(self.guide_handler.actions());

// Additional actions available on desktop
#[cfg(not(target_family = "wasm"))]
Expand Down
14 changes: 14 additions & 0 deletions editor/src/messages/portfolio/document/guide_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::guide::{GuideDirection, GuideId};
use crate::messages::prelude::*;

#[impl_message(Message, DocumentMessage, Guide)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum GuideMessage {
CreateGuide { id: GuideId, direction: GuideDirection, mouse_x: f64, mouse_y: f64 },
MoveGuide { id: GuideId, mouse_x: f64, mouse_y: f64 },
DeleteGuide { id: GuideId },
GuideOverlays { context: OverlayContext },
ToggleGuidesVisibility,
SetHoveredGuide { id: Option<GuideId> },
}
111 changes: 111 additions & 0 deletions editor/src/messages/portfolio/document/guide_message_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use super::utility_types::guide::{Guide, GuideDirection, GuideId};
use crate::messages::portfolio::document::guide_message::{GuideMessage, GuideMessageDiscriminant};
use crate::messages::portfolio::document::overlays::guide_overlays::guide_overlay;
use crate::messages::portfolio::document::utility_types::misc::PTZ;
use crate::messages::prelude::*;
use glam::DVec2;

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ExtractField)]
#[serde(default)]
pub struct GuideMessageHandler {
#[serde(default)]
pub guides: Vec<Guide>,
#[serde(default = "default_guides_visible")]
pub guides_visible: bool,
#[serde(skip)]
pub hovered_guide_id: Option<GuideId>,
}

fn default_guides_visible() -> bool {
true
}

impl Default for GuideMessageHandler {
fn default() -> Self {
Self {
guides: Vec::new(),
guides_visible: true,
hovered_guide_id: None,
}
}
}

#[derive(ExtractField)]
pub struct GuideMessageContext<'a> {
pub navigation_handler: &'a NavigationMessageHandler,
pub document_ptz: &'a PTZ,
pub viewport: &'a ViewportMessageHandler,
}

#[message_handler_data]
impl MessageHandler<GuideMessage, GuideMessageContext<'_>> for GuideMessageHandler {
fn actions(&self) -> ActionList {
actions!(GuideMessageDiscriminant; ToggleGuidesVisibility)
}

fn process_message(&mut self, message: GuideMessage, responses: &mut VecDeque<Message>, context: GuideMessageContext) {
let GuideMessageContext {
navigation_handler,
document_ptz,
viewport,
} = context;

match message {
GuideMessage::CreateGuide { id, direction, mouse_x, mouse_y } => {
let document_to_viewport = navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), document_ptz);
let viewport_to_document = document_to_viewport.inverse();

let viewport_point = DVec2::new(mouse_x, mouse_y);
let document_point = viewport_to_document.transform_point2(viewport_point);

let document_position = match direction {
GuideDirection::Horizontal => document_point.y,
GuideDirection::Vertical => document_point.x,
};

let guide = Guide::with_id(id, direction, document_position);
self.guides.push(guide);
responses.add(OverlaysMessage::Draw);
responses.add(PortfolioMessage::UpdateDocumentWidgets);
}
GuideMessage::MoveGuide { id, mouse_x, mouse_y } => {
let document_to_viewport = navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), document_ptz);
let viewport_to_document = document_to_viewport.inverse();

let viewport_point = DVec2::new(mouse_x, mouse_y);
let document_point = viewport_to_document.transform_point2(viewport_point);

if let Some(guide) = self.guides.iter_mut().find(|guide| guide.id == id) {
guide.position = match guide.direction {
GuideDirection::Horizontal => document_point.y,
GuideDirection::Vertical => document_point.x,
};
}
responses.add(OverlaysMessage::Draw);
}
GuideMessage::DeleteGuide { id } => {
self.guides.retain(|g| g.id != id);
responses.add(OverlaysMessage::Draw);
responses.add(PortfolioMessage::UpdateDocumentWidgets);
}
GuideMessage::GuideOverlays { context: mut overlay_context } => {
if self.guides_visible {
let document_to_viewport = navigation_handler.calculate_offset_transform(overlay_context.viewport.center_in_viewport_space().into(), document_ptz);
guide_overlay(self, &mut overlay_context, document_to_viewport);
}
}
GuideMessage::ToggleGuidesVisibility => {
self.guides_visible = !self.guides_visible;
responses.add(OverlaysMessage::Draw);
responses.add(PortfolioMessage::UpdateDocumentWidgets);
responses.add(MenuBarMessage::SendLayout);
}
GuideMessage::SetHoveredGuide { id } => {
if self.hovered_guide_id != id {
self.hovered_guide_id = id;
responses.add(OverlaysMessage::Draw);
}
}
}
}
}
2 changes: 2 additions & 0 deletions editor/src/messages/portfolio/document/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ mod document_message_handler;

pub mod data_panel;
pub mod graph_operation;
pub mod guide_message;
pub mod guide_message_handler;
pub mod navigation;
pub mod node_graph;
pub mod overlays;
Expand Down
57 changes: 57 additions & 0 deletions editor/src/messages/portfolio/document/overlays/guide_overlays.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::consts::{COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50};
use crate::messages::portfolio::document::guide_message_handler::GuideMessageHandler;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::guide::GuideDirection;
use glam::{DAffine2, DVec2};

fn extend_line_to_viewport(point: DVec2, direction: DVec2, viewport_size: DVec2) -> Option<(DVec2, DVec2)> {
let dir = direction.try_normalize()?;

// Calculates t values for intersections with viewport edges
let mut t_values = Vec::new();

let edges = graphene_std::renderer::Quad::from_box([DVec2::ZERO, viewport_size]).all_edges();
for [start, end] in edges {
let t_along_viewport = (point - start).perp_dot(dir) / (end - start).perp_dot(dir);
let t_along_direction = (point - start).perp_dot(end - start) / (end - start).perp_dot(dir);
if 0. <= t_along_viewport && t_along_viewport <= 1. && t_along_direction.is_finite() {
t_values.push(t_along_direction);
}
}

if t_values.len() < 2 {
return None;
}

let t_min = t_values.iter().cloned().fold(f64::INFINITY, f64::min);
let t_max = t_values.iter().cloned().fold(f64::NEG_INFINITY, f64::max);

let start = point + dir * t_min;
let end = point + dir * t_max;

Some((start, end))
}

pub fn guide_overlay(guide_handler: &GuideMessageHandler, overlay_context: &mut OverlayContext, document_to_viewport: DAffine2) {
let viewport_size: DVec2 = overlay_context.viewport.size().into();

for guide in &guide_handler.guides {
let (doc_point, doc_direction) = match guide.direction {
GuideDirection::Horizontal => (DVec2::new(0.0, guide.position), DVec2::X),
GuideDirection::Vertical => (DVec2::new(guide.position, 0.0), DVec2::Y),
};

let viewport_point = document_to_viewport.transform_point2(doc_point);
let viewport_direction = document_to_viewport.transform_vector2(doc_direction);

let color = if guide_handler.hovered_guide_id == Some(guide.id) {
COLOR_OVERLAY_BLUE_50
} else {
COLOR_OVERLAY_BLUE
};

if let Some((start, end)) = extend_line_to_viewport(viewport_point, viewport_direction, viewport_size) {
overlay_context.line(start, end, Some(color), None);
}
}
}
1 change: 1 addition & 0 deletions editor/src/messages/portfolio/document/overlays/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod grid_overlays;
pub mod guide_overlays;
mod overlays_message;
mod overlays_message_handler;
pub mod utility_functions;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::utility_types::{OverlayProvider, OverlaysVisibilitySettings};
use crate::messages::portfolio::document::guide_message::GuideMessage;
use crate::messages::prelude::*;

#[derive(ExtractField)]
Expand Down Expand Up @@ -57,6 +58,13 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMes
viewport: *viewport,
},
});
responses.add(GuideMessage::GuideOverlays {
context: OverlayContext {
render_context: canvas_context.clone(),
visibility_settings: visibility_settings.clone(),
viewport: *viewport,
},
});
for provider in &self.overlay_providers {
responses.add(provider(OverlayContext {
render_context: canvas_context.clone(),
Expand All @@ -74,6 +82,7 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMes

if visibility_settings.all() {
responses.add(DocumentMessage::GridOverlays { context: overlay_context.clone() });
responses.add(GuideMessage::GuideOverlays { context: overlay_context.clone() });

for provider in &self.overlay_providers {
responses.add(provider(overlay_context.clone()));
Expand Down
Loading
Loading