Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an editor preference for touched/enclosed/directional based selection #2156

Merged
merged 22 commits into from
Jan 26, 2025
Merged
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
27 changes: 14 additions & 13 deletions editor/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,20 +221,21 @@ impl Dispatcher {
}
Message::Tool(message) => {
let document_id = self.message_handlers.portfolio_message_handler.active_document_id().unwrap();
if let Some(document) = self.message_handlers.portfolio_message_handler.documents.get_mut(&document_id) {
let data = ToolMessageData {
document_id,
document,
input: &self.message_handlers.input_preprocessor_message_handler,
persistent_data: &self.message_handlers.portfolio_message_handler.persistent_data,
node_graph: &self.message_handlers.portfolio_message_handler.executor,
preferences: &self.message_handlers.preferences_message_handler,
};

self.message_handlers.tool_message_handler.process_message(message, &mut queue, data);
} else {
let Some(document) = self.message_handlers.portfolio_message_handler.documents.get_mut(&document_id) else {
warn!("Called ToolMessage without an active document.\nGot {message:?}");
}
return;
};

let data = ToolMessageData {
document_id,
document,
input: &self.message_handlers.input_preprocessor_message_handler,
persistent_data: &self.message_handlers.portfolio_message_handler.persistent_data,
node_graph: &self.message_handlers.portfolio_message_handler.executor,
preferences: &self.message_handlers.preferences_message_handler,
};

self.message_handlers.tool_message_handler.process_message(message, &mut queue, data);
}
Message::Workspace(message) => {
self.message_handlers.workspace_message_handler.process_message(message, &mut queue, ());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::preferences::SelectionMode;
use crate::messages::prelude::*;

pub struct PreferencesDialogMessageData<'a> {
Expand Down Expand Up @@ -31,6 +32,39 @@ impl PreferencesDialogMessageHandler {
const TITLE: &'static str = "Editor Preferences";

fn layout(&self, preferences: &PreferencesMessageHandler) -> Layout {
let selection_section = vec![TextLabel::new("Selection").italic(true).widget_holder()];
let selection_mode = RadioInput::new(vec![
RadioEntryData::new(SelectionMode::Touched.to_string())
.label(SelectionMode::Touched.to_string())
.tooltip(SelectionMode::Touched.tooltip_description())
.on_update(move |_| {
PreferencesMessage::SelectionMode {
selection_mode: SelectionMode::Touched,
}
.into()
}),
RadioEntryData::new(SelectionMode::Enclosed.to_string())
.label(SelectionMode::Enclosed.to_string())
.tooltip(SelectionMode::Enclosed.tooltip_description())
.on_update(move |_| {
PreferencesMessage::SelectionMode {
selection_mode: SelectionMode::Enclosed,
}
.into()
}),
RadioEntryData::new(SelectionMode::Directional.to_string())
.label(SelectionMode::Directional.to_string())
.tooltip(SelectionMode::Directional.tooltip_description())
.on_update(move |_| {
PreferencesMessage::SelectionMode {
selection_mode: SelectionMode::Directional,
}
.into()
}),
])
.selected_index(Some(preferences.selection_mode as u32))
.widget_holder();

let zoom_with_scroll_tooltip = "Use the scroll wheel for zooming instead of vertically panning (not recommended for trackpads)";
let input_section = vec![TextLabel::new("Input").italic(true).widget_holder()];
let zoom_with_scroll = vec![
Expand All @@ -43,9 +77,9 @@ impl PreferencesDialogMessageHandler {
.into()
})
.widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextLabel::new("Zoom with Scroll").table_align(true).tooltip(zoom_with_scroll_tooltip).widget_holder(),
];

let vello_tooltip = "Use the experimental Vello renderer (your browser must support WebGPU)";
let renderer_section = vec![TextLabel::new("Experimental").italic(true).widget_holder()];
let use_vello = vec![
Expand All @@ -54,19 +88,26 @@ impl PreferencesDialogMessageHandler {
.disabled(!preferences.supports_wgpu())
.on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::UseVello { use_vello: checkbox_input.checked }.into())
.widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextLabel::new("Vello Renderer")
.table_align(true)
.tooltip(vello_tooltip)
.disabled(!preferences.supports_wgpu())
.widget_holder(),
];

let vector_mesh_tooltip = "Allow tools to produce vector meshes, where more than two segments can connect to an anchor point.\n\nCurrently this does not properly handle line joins and fills.";
let vector_meshes = vec![
CheckboxInput::new(preferences.vector_meshes)
.tooltip(vector_mesh_tooltip)
.on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::VectorMeshes { enabled: checkbox_input.checked }.into())
.widget_holder(),
TextLabel::new("Vector Meshes").table_align(true).tooltip(vector_mesh_tooltip).widget_holder(),
];

// TODO: Reenable when Imaginate is restored
// let imaginate_server_hostname = vec![
// TextLabel::new("Imaginate").min_width(60).italic(true).widget_holder(),
// TextLabel::new("Server Hostname").table_align(true).widget_holder(),
// Separator::new(SeparatorType::Unrelated).widget_holder(),
// TextInput::new(&preferences.imaginate_server_hostname)
// .min_width(200)
// .on_update(|text_input: &TextInput| PreferencesMessage::ImaginateServerHostname { hostname: text_input.value.clone() }.into())
Expand All @@ -75,7 +116,6 @@ impl PreferencesDialogMessageHandler {
// let imaginate_refresh_frequency = vec![
// TextLabel::new("").min_width(60).widget_holder(),
// TextLabel::new("Refresh Frequency").table_align(true).widget_holder(),
// Separator::new(SeparatorType::Unrelated).widget_holder(),
// NumberInput::new(Some(preferences.imaginate_refresh_frequency))
// .unit(" seconds")
// .min(0.)
Expand All @@ -85,17 +125,9 @@ impl PreferencesDialogMessageHandler {
// .widget_holder(),
// ];

let vector_mesh_tooltip = "Allow tools to produce vector meshes, where more than two segments can connect to an anchor point.\n\nCurrently this does not properly handle line joins and fills.";
let vector_meshes = vec![
CheckboxInput::new(preferences.vector_meshes)
.tooltip(vector_mesh_tooltip)
.on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::VectorMeshes { enabled: checkbox_input.checked }.into())
.widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextLabel::new("Vector Meshes").table_align(true).tooltip(vector_mesh_tooltip).widget_holder(),
];

Layout::WidgetLayout(WidgetLayout::new(vec![
LayoutGroup::Row { widgets: selection_section },
LayoutGroup::Row { widgets: vec![selection_mode] },
LayoutGroup::Row { widgets: input_section },
LayoutGroup::Row { widgets: zoom_with_scroll },
LayoutGroup::Row { widgets: renderer_section },
Expand Down
23 changes: 23 additions & 0 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1407,6 +1407,29 @@ impl DocumentMessageHandler {
self.intersect_quad(viewport_quad, ipp).filter(|layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
}

pub fn is_layer_fully_inside(&self, layer: &LayerNodeIdentifier, quad: graphene_core::renderer::Quad) -> bool {
// Get the bounding box of the layer in document space
let Some(bounding_box) = self.metadata().bounding_box_viewport(*layer) else { return false };

// Check if the bounding box is fully within the selection quad
let [top_left, bottom_right] = bounding_box;

let quad_bbox = quad.bounding_box();

let quad_left = quad_bbox[0].x;
let quad_right = quad_bbox[1].x;
let quad_top = quad_bbox[0].y.max(quad_bbox[1].y); // Correct top
let quad_bottom = quad_bbox[0].y.min(quad_bbox[1].y); // Correct bottom

// Extract layer's bounding box coordinates
let layer_left = top_left.x;
let layer_right = bottom_right.x;
let layer_top = bottom_right.y;
let layer_bottom = top_left.y;

layer_left >= quad_left && layer_right <= quad_right && layer_top <= quad_top && layer_bottom >= quad_bottom
}

/// Find all of the layers that were clicked on from a viewport space location
pub fn click_xray(&self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ impl DoubleEndedIterator for DescendantsIter<'_> {

#[derive(Debug, Clone, Copy, Default)]
pub struct NodeRelations {
parent: Option<LayerNodeIdentifier>,
pub parent: Option<LayerNodeIdentifier>,
previous_sibling: Option<LayerNodeIdentifier>,
next_sibling: Option<LayerNodeIdentifier>,
first_child: Option<LayerNodeIdentifier>,
Expand Down
8 changes: 4 additions & 4 deletions editor/src/messages/portfolio/portfolio_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,16 @@ pub enum PortfolioMessage {
document_id: DocumentId,
},
DestroyAllDocuments,
EditorPreferences,
FontLoaded {
font_family: String,
font_style: String,
preview_url: String,
data: Vec<u8>,
},
ImaginateCheckServerStatus,
ImaginatePollServerStatus,
EditorPreferences,
ImaginateServerHostname,
// ImaginateCheckServerStatus,
// ImaginatePollServerStatus,
// ImaginateServerHostname,
Import,
LoadDocumentResources {
document_id: DocumentId,
Expand Down
58 changes: 30 additions & 28 deletions editor/src/messages/portfolio/portfolio_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
use crate::messages::portfolio::document::DocumentMessageData;
use crate::messages::preferences::SelectionMode;
use crate::messages::prelude::*;
use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType};
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
Expand Down Expand Up @@ -38,6 +39,7 @@ pub struct PortfolioMessageHandler {
copy_buffer: [Vec<CopyBufferEntry>; INTERNAL_CLIPBOARD_COUNT as usize],
pub persistent_data: PersistentData,
pub executor: NodeGraphExecutor,
pub selection_mode: SelectionMode,
}

impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMessageHandler {
Expand Down Expand Up @@ -295,35 +297,35 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
responses.add(NodeGraphMessage::RunDocumentGraph);
}
}
PortfolioMessage::ImaginateCheckServerStatus => {
let server_status = self.persistent_data.imaginate.server_status().clone();
self.persistent_data.imaginate.poll_server_check();
#[cfg(target_arch = "wasm32")]
if let Some(fut) = self.persistent_data.imaginate.initiate_server_check() {
wasm_bindgen_futures::spawn_local(async move {
let () = fut.await;
use wasm_bindgen::prelude::*;

#[wasm_bindgen(module = "/../frontend/src/editor.ts")]
extern "C" {
#[wasm_bindgen(js_name = injectImaginatePollServerStatus)]
fn inject();
}
inject();
})
}
if &server_status != self.persistent_data.imaginate.server_status() {
responses.add(PropertiesPanelMessage::Refresh);
}
}
PortfolioMessage::ImaginatePollServerStatus => {
self.persistent_data.imaginate.poll_server_check();
responses.add(PropertiesPanelMessage::Refresh);
}
// PortfolioMessage::ImaginateCheckServerStatus => {
// let server_status = self.persistent_data.imaginate.server_status().clone();
// self.persistent_data.imaginate.poll_server_check();
// #[cfg(target_arch = "wasm32")]
// if let Some(fut) = self.persistent_data.imaginate.initiate_server_check() {
// wasm_bindgen_futures::spawn_local(async move {
// let () = fut.await;
// use wasm_bindgen::prelude::*;

// #[wasm_bindgen(module = "/../frontend/src/editor.ts")]
// extern "C" {
// #[wasm_bindgen(js_name = injectImaginatePollServerStatus)]
// fn inject();
// }
// inject();
// })
// }
// if &server_status != self.persistent_data.imaginate.server_status() {
// responses.add(PropertiesPanelMessage::Refresh);
// }
// }
// PortfolioMessage::ImaginatePollServerStatus => {
// self.persistent_data.imaginate.poll_server_check();
// responses.add(PropertiesPanelMessage::Refresh);
// }
PortfolioMessage::EditorPreferences => self.executor.update_editor_preferences(preferences.editor_preferences()),
PortfolioMessage::ImaginateServerHostname => {
self.persistent_data.imaginate.set_host_name(&preferences.imaginate_server_hostname);
}
// PortfolioMessage::ImaginateServerHostname => {
// self.persistent_data.imaginate.set_host_name(&preferences.imaginate_server_hostname);
// }
PortfolioMessage::Import => {
// This portfolio message wraps the frontend message so it can be listed as an action, which isn't possible for frontend messages
responses.add(FrontendMessage::TriggerImport);
Expand Down
3 changes: 2 additions & 1 deletion editor/src/messages/portfolio/utility_types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use graphene_std::{imaginate::ImaginatePersistentData, text::FontCache};
use graphene_std::imaginate::ImaginatePersistentData;
use graphene_std::text::FontCache;

#[derive(Debug, Default)]
pub struct PersistentData {
Expand Down
3 changes: 3 additions & 0 deletions editor/src/messages/preferences/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
mod preferences_message;
mod preferences_message_handler;
pub mod utility_types;

#[doc(inline)]
pub use preferences_message::{PreferencesMessage, PreferencesMessageDiscriminant};
#[doc(inline)]
pub use preferences_message_handler::PreferencesMessageHandler;
#[doc(inline)]
pub use utility_types::SelectionMode;
8 changes: 6 additions & 2 deletions editor/src/messages/preferences/preferences_message.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use crate::messages::preferences::SelectionMode;
use crate::messages::prelude::*;

#[impl_message(Message, Preferences)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum PreferencesMessage {
// Management messages
Load { preferences: String },
ResetToDefaults,

ImaginateRefreshFrequency { seconds: f64 },
// Per-preference messages
UseVello { use_vello: bool },
ImaginateServerHostname { hostname: String },
SelectionMode { selection_mode: SelectionMode },
VectorMeshes { enabled: bool },
ModifyLayout { zoom_with_scroll: bool },
// ImaginateRefreshFrequency { seconds: f64 },
// ImaginateServerHostname { hostname: String },
}
Loading
Loading