Skip to content
Open

Guide-1 #3825

Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions editor/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,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 @@ -263,6 +264,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 @@ -4,6 +4,7 @@ use std::sync::Arc;
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 @@ -37,6 +38,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,
Comment thread
jsjgdh marked this conversation as resolved.
Outdated

// ============================================
// Fields that are saved in the document format
Expand Down Expand Up @@ -154,6 +157,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(),
Comment thread
jsjgdh marked this conversation as resolved.
Outdated
// ============================================
// Fields that are saved in the document format
// ============================================
Expand All @@ -179,6 +183,7 @@ impl Default for DocumentMessageHandler {
saved_hash: None,
auto_saved_hash: None,
layer_range_selection_reference: None,

is_loaded: false,
}
}
Expand Down Expand Up @@ -215,6 +220,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 } => {
self.handle_group_selected_layers(group_folder_type, responses);
}
Expand Down Expand Up @@ -1433,6 +1447,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> },
}
108 changes: 108 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,108 @@
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
Comment thread
jsjgdh marked this conversation as resolved.
Outdated
}

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;

let viewport_to_document_point = |mouse_x: f64, mouse_y: f64| -> DVec2 {
let document_to_viewport = navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), document_ptz);
document_to_viewport.inverse().transform_point2(DVec2::new(mouse_x, mouse_y))
};

match message {
GuideMessage::CreateGuide { id, direction, mouse_x, mouse_y } => {
Comment thread
jsjgdh marked this conversation as resolved.
Outdated
let document_point = viewport_to_document_point(mouse_x, mouse_y);

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_point = viewport_to_document_point(mouse_x, mouse_y);

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,6 @@
use super::utility_types::{OverlayProvider, OverlaysVisibilitySettings};
#[cfg(not(test))]
use crate::messages::portfolio::document::guide_message::GuideMessage;
use crate::messages::prelude::*;

#[derive(ExtractField)]
Expand Down Expand Up @@ -57,6 +59,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 +83,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