Fix viewport bounds getting out of sync at times, like when toggling rulers
This commit is contained in:
parent
a4a513911d
commit
06177597ae
|
|
@ -119,7 +119,7 @@ impl Dispatcher {
|
||||||
}
|
}
|
||||||
Message::Frontend(message) => {
|
Message::Frontend(message) => {
|
||||||
// Handle these messages immediately by returning early
|
// Handle these messages immediately by returning early
|
||||||
if let FrontendMessage::TriggerFontLoad { .. } | FrontendMessage::TriggerRefreshBoundsOfViewports = message {
|
if let FrontendMessage::TriggerFontLoad { .. } = message {
|
||||||
self.responses.push(message);
|
self.responses.push(message);
|
||||||
self.cleanup_queues(false);
|
self.cleanup_queues(false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::messages::prelude::*;
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
use graphene_core::uuid::generate_uuid;
|
use graphene_core::uuid::generate_uuid;
|
||||||
|
|
||||||
use glam::{DVec2, IVec2, UVec2};
|
use glam::{IVec2, UVec2};
|
||||||
|
|
||||||
/// A dialog to allow users to set some initial options about a new document.
|
/// A dialog to allow users to set some initial options about a new document.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
|
@ -26,16 +26,13 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
|
||||||
|
|
||||||
let create_artboard = !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0;
|
let create_artboard = !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0;
|
||||||
if create_artboard {
|
if create_artboard {
|
||||||
let id = NodeId(generate_uuid());
|
|
||||||
responses.add(GraphOperationMessage::NewArtboard {
|
responses.add(GraphOperationMessage::NewArtboard {
|
||||||
id,
|
id: NodeId(generate_uuid()),
|
||||||
artboard: graphene_core::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
|
artboard: graphene_core::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
|
||||||
});
|
});
|
||||||
responses.add(NavigationMessage::FitViewportToBounds {
|
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
|
||||||
bounds: [DVec2::ZERO, self.dimensions.as_dvec2()],
|
|
||||||
prevent_zoom_past_100: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ pub enum FrontendMessage {
|
||||||
#[serde(rename = "blobUrl")]
|
#[serde(rename = "blobUrl")]
|
||||||
blob_url: String,
|
blob_url: String,
|
||||||
},
|
},
|
||||||
|
TriggerDelayedZoomCanvasToFitAll,
|
||||||
TriggerDownloadBlobUrl {
|
TriggerDownloadBlobUrl {
|
||||||
#[serde(rename = "layerName")]
|
#[serde(rename = "layerName")]
|
||||||
layer_name: String,
|
layer_name: String,
|
||||||
|
|
@ -87,7 +88,6 @@ pub enum FrontendMessage {
|
||||||
TriggerLoadPreferences,
|
TriggerLoadPreferences,
|
||||||
TriggerOpenDocument,
|
TriggerOpenDocument,
|
||||||
TriggerPaste,
|
TriggerPaste,
|
||||||
TriggerRefreshBoundsOfViewports,
|
|
||||||
TriggerRevokeBlobUrl {
|
TriggerRevokeBlobUrl {
|
||||||
url: String,
|
url: String,
|
||||||
},
|
},
|
||||||
|
|
@ -112,7 +112,6 @@ pub enum FrontendMessage {
|
||||||
#[serde(rename = "documentSerializedContent")]
|
#[serde(rename = "documentSerializedContent")]
|
||||||
document_serialized_content: String,
|
document_serialized_content: String,
|
||||||
},
|
},
|
||||||
TriggerViewportResize,
|
|
||||||
TriggerVisitLink {
|
TriggerVisitLink {
|
||||||
url: String,
|
url: String,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageData> for
|
||||||
self.viewport_bounds = bounds;
|
self.viewport_bounds = bounds;
|
||||||
|
|
||||||
responses.add(NavigationMessage::CanvasPan { delta: DVec2::ZERO });
|
responses.add(NavigationMessage::CanvasPan { delta: DVec2::ZERO });
|
||||||
responses.add(FrontendMessage::TriggerViewportResize);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {
|
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {
|
||||||
|
|
|
||||||
|
|
@ -429,7 +429,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(FrontendMessage::TriggerGraphViewOverlay { open });
|
responses.add(FrontendMessage::TriggerGraphViewOverlay { open });
|
||||||
responses.add(FrontendMessage::TriggerRefreshBoundsOfViewports);
|
|
||||||
// Update the tilt menu bar buttons to be disabled when the graph is open
|
// Update the tilt menu bar buttons to be disabled when the graph is open
|
||||||
responses.add(MenuBarMessage::SendLayout);
|
responses.add(MenuBarMessage::SendLayout);
|
||||||
if open {
|
if open {
|
||||||
|
|
@ -780,7 +779,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||||
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
||||||
}
|
}
|
||||||
DocumentMessage::RenderRulers => {
|
DocumentMessage::RenderRulers => {
|
||||||
let document_transform_scale = self.navigation_handler.snapped_zoom(self.document_ptz.zoom);
|
let document_transform_scale = self.navigation_handler.snapped_zoom(self.document_ptz.zoom());
|
||||||
|
|
||||||
let ruler_origin = if !self.graph_view_overlay_open {
|
let ruler_origin = if !self.graph_view_overlay_open {
|
||||||
self.metadata().document_to_viewport.transform_point2(DVec2::ZERO)
|
self.metadata().document_to_viewport.transform_point2(DVec2::ZERO)
|
||||||
|
|
@ -802,7 +801,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
DocumentMessage::RenderScrollbars => {
|
DocumentMessage::RenderScrollbars => {
|
||||||
let document_transform_scale = self.navigation_handler.snapped_zoom(self.document_ptz.zoom);
|
let document_transform_scale = self.navigation_handler.snapped_zoom(self.document_ptz.zoom());
|
||||||
|
|
||||||
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
||||||
|
|
||||||
|
|
@ -1871,7 +1870,7 @@ impl DocumentMessageHandler {
|
||||||
.tooltip("Reset Tilt and Zoom to 100%")
|
.tooltip("Reset Tilt and Zoom to 100%")
|
||||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
|
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
|
||||||
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
|
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
|
||||||
.disabled(self.document_ptz.tilt.abs() < 1e-4 && (self.document_ptz.zoom - 1.).abs() < 1e-4)
|
.disabled(self.document_ptz.tilt.abs() < 1e-4 && (self.document_ptz.zoom() - 1.).abs() < 1e-4)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
PopoverButton::new()
|
PopoverButton::new()
|
||||||
.popover_layout(vec![
|
.popover_layout(vec![
|
||||||
|
|
@ -1902,7 +1901,7 @@ impl DocumentMessageHandler {
|
||||||
])
|
])
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
NumberInput::new(Some(self.navigation_handler.snapped_zoom(self.document_ptz.zoom) * 100.))
|
NumberInput::new(Some(self.navigation_handler.snapped_zoom(self.document_ptz.zoom()) * 100.))
|
||||||
.unit("%")
|
.unit("%")
|
||||||
.min(0.000001)
|
.min(0.000001)
|
||||||
.max(1000000.)
|
.max(1000000.)
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
} else {
|
} else {
|
||||||
node_graph_ptz.entry(node_graph_handler.network.clone()).or_insert(PTZ::default())
|
node_graph_ptz.entry(node_graph_handler.network.clone()).or_insert(PTZ::default())
|
||||||
};
|
};
|
||||||
let old_zoom = ptz.zoom;
|
let old_zoom = ptz.zoom();
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
NavigationMessage::BeginCanvasPan => {
|
NavigationMessage::BeginCanvasPan => {
|
||||||
|
|
@ -110,8 +110,8 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
});
|
});
|
||||||
|
|
||||||
self.navigation_operation = NavigationOperation::Zoom {
|
self.navigation_operation = NavigationOperation::Zoom {
|
||||||
zoom_raw_not_snapped: ptz.zoom,
|
zoom_raw_not_snapped: ptz.zoom(),
|
||||||
zoom_original_for_abort: ptz.zoom,
|
zoom_original_for_abort: ptz.zoom(),
|
||||||
snap: false,
|
snap: false,
|
||||||
};
|
};
|
||||||
self.mouse_position = ipp.mouse.position;
|
self.mouse_position = ipp.mouse.position;
|
||||||
|
|
@ -145,7 +145,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
}
|
}
|
||||||
NavigationMessage::CanvasTiltResetAndZoomTo100Percent => {
|
NavigationMessage::CanvasTiltResetAndZoomTo100Percent => {
|
||||||
ptz.tilt = 0.;
|
ptz.tilt = 0.;
|
||||||
ptz.zoom = 1.;
|
ptz.set_zoom(1.);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||||
}
|
}
|
||||||
|
|
@ -154,16 +154,16 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||||
}
|
}
|
||||||
NavigationMessage::CanvasZoomDecrease { center_on_mouse } => {
|
NavigationMessage::CanvasZoomDecrease { center_on_mouse } => {
|
||||||
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom).unwrap_or(&ptz.zoom);
|
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom()).unwrap_or(&ptz.zoom());
|
||||||
if center_on_mouse {
|
if center_on_mouse {
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
|
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom(), ipp.mouse.position));
|
||||||
}
|
}
|
||||||
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
||||||
}
|
}
|
||||||
NavigationMessage::CanvasZoomIncrease { center_on_mouse } => {
|
NavigationMessage::CanvasZoomIncrease { center_on_mouse } => {
|
||||||
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom).unwrap_or(&ptz.zoom);
|
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom()).unwrap_or(&ptz.zoom());
|
||||||
if center_on_mouse {
|
if center_on_mouse {
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
|
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom(), ipp.mouse.position));
|
||||||
}
|
}
|
||||||
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
||||||
}
|
}
|
||||||
|
|
@ -179,10 +179,12 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
} else {
|
} else {
|
||||||
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
|
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
|
||||||
};
|
};
|
||||||
zoom_factor *= Self::clamp_zoom(ptz.zoom * zoom_factor, document_bounds, old_zoom, ipp);
|
zoom_factor *= Self::clamp_zoom(ptz.zoom() * zoom_factor, document_bounds, old_zoom, ipp);
|
||||||
|
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position));
|
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position));
|
||||||
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom * zoom_factor });
|
responses.add(NavigationMessage::CanvasZoomSet {
|
||||||
|
zoom_factor: ptz.zoom() * zoom_factor,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
NavigationMessage::CanvasZoomSet { zoom_factor } => {
|
NavigationMessage::CanvasZoomSet { zoom_factor } => {
|
||||||
let document_bounds = if !graph_view_overlay_open {
|
let document_bounds = if !graph_view_overlay_open {
|
||||||
|
|
@ -191,8 +193,9 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
} else {
|
} else {
|
||||||
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
|
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
|
||||||
};
|
};
|
||||||
ptz.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
let zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
||||||
ptz.zoom *= Self::clamp_zoom(ptz.zoom, document_bounds, old_zoom, ipp);
|
let zoom = zoom * Self::clamp_zoom(zoom, document_bounds, old_zoom, ipp);
|
||||||
|
ptz.set_zoom(zoom);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +211,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
ptz.pan = pan_original_for_abort;
|
ptz.pan = pan_original_for_abort;
|
||||||
}
|
}
|
||||||
NavigationOperation::Zoom { zoom_original_for_abort, .. } => {
|
NavigationOperation::Zoom { zoom_original_for_abort, .. } => {
|
||||||
ptz.zoom = zoom_original_for_abort;
|
ptz.set_zoom(zoom_original_for_abort);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,7 +220,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
|
|
||||||
// Final chance to apply snapping if the key was pressed during this final frame
|
// Final chance to apply snapping if the key was pressed during this final frame
|
||||||
ptz.tilt = self.snapped_tilt(ptz.tilt);
|
ptz.tilt = self.snapped_tilt(ptz.tilt);
|
||||||
ptz.zoom = self.snapped_zoom(ptz.zoom);
|
ptz.set_zoom(self.snapped_zoom(ptz.zoom()));
|
||||||
|
|
||||||
// Reset the navigation operation now that it's done
|
// Reset the navigation operation now that it's done
|
||||||
self.navigation_operation = NavigationOperation::None;
|
self.navigation_operation = NavigationOperation::None;
|
||||||
|
|
@ -238,19 +241,18 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
bounds: [pos1, pos2],
|
bounds: [pos1, pos2],
|
||||||
prevent_zoom_past_100,
|
prevent_zoom_past_100,
|
||||||
} => {
|
} => {
|
||||||
let v1 = if !graph_view_overlay_open {
|
let (pos1, pos2) = (pos1.min(pos2), pos1.max(pos2));
|
||||||
metadata.document_to_viewport.inverse().transform_point2(DVec2::ZERO)
|
let diagonal = pos2 - pos1;
|
||||||
} else {
|
|
||||||
node_graph_to_viewport.inverse().transform_point2(DVec2::ZERO)
|
|
||||||
};
|
|
||||||
let v2 = if !graph_view_overlay_open {
|
|
||||||
metadata.document_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size())
|
|
||||||
} else {
|
|
||||||
node_graph_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size())
|
|
||||||
};
|
|
||||||
|
|
||||||
let center = ((v1 + v2) - (pos1 + pos2)) / 2.;
|
if diagonal.length() < f64::EPSILON * 1000. || ipp.viewport_bounds.size() == DVec2::ZERO {
|
||||||
let size = 1. / ((pos2 - pos1) / (v2 - v1));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let transform = (if graph_view_overlay_open { *node_graph_to_viewport } else { metadata.document_to_viewport }).inverse();
|
||||||
|
let (v1, v2) = (transform.transform_point2(DVec2::ZERO), transform.transform_point2(ipp.viewport_bounds.size()));
|
||||||
|
|
||||||
|
let center = ((v2 + v1) - (pos2 + pos1)) / 2.;
|
||||||
|
let size = (v2 - v1) / diagonal;
|
||||||
let new_scale = size.min_element();
|
let new_scale = size.min_element();
|
||||||
|
|
||||||
let viewport_change = if !graph_view_overlay_open {
|
let viewport_change = if !graph_view_overlay_open {
|
||||||
|
|
@ -264,12 +266,12 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
ptz.pan += center;
|
ptz.pan += center;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptz.zoom *= new_scale * VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR;
|
ptz.set_zoom(ptz.zoom() * new_scale * VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR);
|
||||||
|
|
||||||
// Keep the canvas filling less than the full available viewport bounds if requested.
|
// Keep the canvas filling less than the full available viewport bounds if requested.
|
||||||
// And if the zoom is close to the full viewport bounds, we ignore the padding because 100% is preferrable if it still fits.
|
// And if the zoom is close to the full viewport bounds, we ignore the padding because 100% is preferrable if it still fits.
|
||||||
if prevent_zoom_past_100 && ptz.zoom > VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR {
|
if prevent_zoom_past_100 && ptz.zoom() > VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR {
|
||||||
ptz.zoom = 1.;
|
ptz.set_zoom(1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
|
|
@ -339,7 +341,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
|
|
||||||
updated_zoom * Self::clamp_zoom(updated_zoom, document_bounds, old_zoom, ipp)
|
updated_zoom * Self::clamp_zoom(updated_zoom, document_bounds, old_zoom, ipp)
|
||||||
};
|
};
|
||||||
ptz.zoom = self.snapped_zoom(zoom_raw_not_snapped);
|
ptz.set_zoom(self.snapped_zoom(zoom_raw_not_snapped));
|
||||||
|
|
||||||
let snap = ipp.keyboard.get(snap as usize);
|
let snap = ipp.keyboard.get(snap as usize);
|
||||||
|
|
||||||
|
|
@ -349,7 +351,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
snap,
|
snap,
|
||||||
};
|
};
|
||||||
|
|
||||||
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom });
|
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -427,7 +429,7 @@ impl NavigationMessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_document_transform(&self, viewport_center: DVec2, ptz: &PTZ, responses: &mut VecDeque<Message>) {
|
fn create_document_transform(&self, viewport_center: DVec2, ptz: &PTZ, responses: &mut VecDeque<Message>) {
|
||||||
let transform = self.calculate_offset_transform(viewport_center, ptz.pan, ptz.tilt, ptz.zoom);
|
let transform = self.calculate_offset_transform(viewport_center, ptz.pan, ptz.tilt, ptz.zoom());
|
||||||
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,13 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessag
|
||||||
use super::utility_types::OverlayContext;
|
use super::utility_types::OverlayContext;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
let canvas = self.canvas.get_or_insert_with(|| overlay_canvas_element().expect("Failed to get canvas element"));
|
let canvas = match &self.canvas {
|
||||||
|
Some(canvas) => canvas,
|
||||||
|
None => {
|
||||||
|
let Some(new_canvas) = overlay_canvas_element() else { return };
|
||||||
|
self.canvas.get_or_insert(new_canvas)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let context = self.context.get_or_insert_with(|| {
|
let context = self.context.get_or_insert_with(|| {
|
||||||
let context = canvas.get_context("2d").ok().flatten().expect("Failed to get canvas context");
|
let context = canvas.get_context("2d").ok().flatten().expect("Failed to get canvas context");
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ impl GridSnapping {
|
||||||
pub fn compute_rectangle_spacing(mut size: DVec2, navigation: &PTZ) -> Option<DVec2> {
|
pub fn compute_rectangle_spacing(mut size: DVec2, navigation: &PTZ) -> Option<DVec2> {
|
||||||
let mut iterations = 0;
|
let mut iterations = 0;
|
||||||
size = size.abs();
|
size = size.abs();
|
||||||
while (size * navigation.zoom).cmplt(DVec2::splat(10.)).any() {
|
while (size * navigation.zoom()).cmplt(DVec2::splat(10.)).any() {
|
||||||
if iterations > 100 {
|
if iterations > 100 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -261,7 +261,7 @@ impl GridSnapping {
|
||||||
let length = length.abs();
|
let length = length.abs();
|
||||||
let mut iterations = 0;
|
let mut iterations = 0;
|
||||||
let mut multiplier = 1.;
|
let mut multiplier = 1.;
|
||||||
while (length / divisor.abs().max(1.)) * multiplier * navigation.zoom < 10. {
|
while (length / divisor.abs().max(1.)) * multiplier * navigation.zoom() < 10. {
|
||||||
if iterations > 100 {
|
if iterations > 100 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -409,8 +409,7 @@ impl fmt::Display for SnappingOptions {
|
||||||
pub struct PTZ {
|
pub struct PTZ {
|
||||||
pub pan: DVec2,
|
pub pan: DVec2,
|
||||||
pub tilt: f64,
|
pub tilt: f64,
|
||||||
// TODO: Make this private and add getter/setter methods which ensure zoom is always positive and greater than the smallest zoom level in `VIEWPORT_ZOOM_LEVELS`.
|
zoom: f64,
|
||||||
pub zoom: f64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PTZ {
|
impl Default for PTZ {
|
||||||
|
|
@ -418,3 +417,13 @@ impl Default for PTZ {
|
||||||
Self { pan: DVec2::ZERO, tilt: 0., zoom: 1. }
|
Self { pan: DVec2::ZERO, tilt: 0., zoom: 1. }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PTZ {
|
||||||
|
pub fn zoom(&self) -> f64 {
|
||||||
|
self.zoom
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_zoom(&mut self, zoom: f64) {
|
||||||
|
self.zoom = zoom.clamp(crate::consts::VIEWPORT_ZOOM_SCALE_MIN, crate::consts::VIEWPORT_ZOOM_SCALE_MAX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -618,7 +618,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||||
|
|
||||||
responses.add(DocumentMessage::RenderRulers);
|
responses.add(DocumentMessage::RenderRulers);
|
||||||
responses.add(MenuBarMessage::SendLayout);
|
responses.add(MenuBarMessage::SendLayout);
|
||||||
responses.add(FrontendMessage::TriggerRefreshBoundsOfViewports);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PortfolioMessage::UpdateDocumentWidgets => {
|
PortfolioMessage::UpdateDocumentWidgets => {
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ impl SnapConstraint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn snap_tolerance(document: &DocumentMessageHandler) -> f64 {
|
pub fn snap_tolerance(document: &DocumentMessageHandler) -> f64 {
|
||||||
document.snapping_state.tolerance / document.document_ptz.zoom
|
document.snapping_state.tolerance / document.document_ptz.zoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_points(a: &&SnappedPoint, b: &&SnappedPoint) -> Ordering {
|
fn compare_points(a: &&SnappedPoint, b: &&SnappedPoint) -> Ordering {
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,6 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
||||||
|
|
||||||
// Notify the frontend about the initial working colors
|
// Notify the frontend about the initial working colors
|
||||||
document_data.update_working_colors(responses);
|
document_data.update_working_colors(responses);
|
||||||
responses.add(FrontendMessage::TriggerRefreshBoundsOfViewports);
|
|
||||||
|
|
||||||
let mut data = ToolActionHandlerData {
|
let mut data = ToolActionHandlerData {
|
||||||
document,
|
document,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
import type { DocumentState } from "@graphite/state-providers/document";
|
import type { DocumentState } from "@graphite/state-providers/document";
|
||||||
import { textInputCleanup } from "@graphite/utility-functions/keyboard-entry";
|
import { textInputCleanup } from "@graphite/utility-functions/keyboard-entry";
|
||||||
import { extractPixelData, rasterizeSVGCanvas } from "@graphite/utility-functions/rasterization";
|
import { extractPixelData, rasterizeSVGCanvas } from "@graphite/utility-functions/rasterization";
|
||||||
|
import { updateBoundsOfViewports } from "@graphite/utility-functions/viewports";
|
||||||
import type { Editor } from "@graphite/wasm-communication/editor";
|
import type { Editor } from "@graphite/wasm-communication/editor";
|
||||||
import {
|
import {
|
||||||
type MouseCursorIcon,
|
type MouseCursorIcon,
|
||||||
|
|
@ -12,7 +13,6 @@
|
||||||
DisplayEditableTextboxTransform,
|
DisplayEditableTextboxTransform,
|
||||||
DisplayRemoveEditableTextbox,
|
DisplayRemoveEditableTextbox,
|
||||||
TriggerTextCommit,
|
TriggerTextCommit,
|
||||||
TriggerViewportResize,
|
|
||||||
UpdateDocumentArtwork,
|
UpdateDocumentArtwork,
|
||||||
UpdateDocumentRulers,
|
UpdateDocumentRulers,
|
||||||
UpdateDocumentScrollbars,
|
UpdateDocumentScrollbars,
|
||||||
|
|
@ -344,19 +344,6 @@
|
||||||
showTextInput = false;
|
showTextInput = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize elements to render the new viewport size
|
|
||||||
export function viewportResize() {
|
|
||||||
if (!viewport) return;
|
|
||||||
|
|
||||||
// Resize the canvas
|
|
||||||
canvasSvgWidth = Math.ceil(parseFloat(getComputedStyle(viewport).width));
|
|
||||||
canvasSvgHeight = Math.ceil(parseFloat(getComputedStyle(viewport).height));
|
|
||||||
|
|
||||||
// Resize the rulers
|
|
||||||
rulerHorizontal?.resize();
|
|
||||||
rulerVertical?.resize();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// Update rendered SVGs
|
// Update rendered SVGs
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateDocumentArtwork, async (data) => {
|
editor.subscriptions.subscribeJsMessage(UpdateDocumentArtwork, async (data) => {
|
||||||
|
|
@ -418,15 +405,24 @@
|
||||||
displayRemoveEditableTextbox();
|
displayRemoveEditableTextbox();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Resize elements to render the new viewport size
|
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerViewportResize, async () => {
|
|
||||||
await tick();
|
|
||||||
|
|
||||||
viewportResize();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Once this component is mounted, we want to resend the document bounds to the backend via the resize event handler which does that
|
// Once this component is mounted, we want to resend the document bounds to the backend via the resize event handler which does that
|
||||||
window.dispatchEvent(new Event("resize"));
|
window.dispatchEvent(new Event("resize"));
|
||||||
|
|
||||||
|
const viewportResizeObserver = new ResizeObserver(() => {
|
||||||
|
if (!viewport) return;
|
||||||
|
|
||||||
|
// Resize the canvas
|
||||||
|
canvasSvgWidth = Math.ceil(parseFloat(getComputedStyle(viewport).width));
|
||||||
|
canvasSvgHeight = Math.ceil(parseFloat(getComputedStyle(viewport).height));
|
||||||
|
|
||||||
|
// Resize the rulers
|
||||||
|
rulerHorizontal?.resize();
|
||||||
|
rulerVertical?.resize();
|
||||||
|
|
||||||
|
// Send the new bounds of the viewports to the backend
|
||||||
|
if (viewport.parentElement) updateBoundsOfViewports(editor, viewport.parentElement);
|
||||||
|
});
|
||||||
|
if (viewport) viewportResizeObserver.observe(viewport);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,6 @@
|
||||||
|
|
||||||
panelSizes[nextSiblingName] = ((nextSiblingSize + mouseDelta) / totalResizingSpaceOccupied) * proportionBeingResized * 100;
|
panelSizes[nextSiblingName] = ((nextSiblingSize + mouseDelta) / totalResizingSpaceOccupied) * proportionBeingResized * 100;
|
||||||
panelSizes[prevSiblingName] = ((prevSiblingSize - mouseDelta) / totalResizingSpaceOccupied) * proportionBeingResized * 100;
|
panelSizes[prevSiblingName] = ((prevSiblingSize - mouseDelta) / totalResizingSpaceOccupied) * proportionBeingResized * 100;
|
||||||
|
|
||||||
window.dispatchEvent(new CustomEvent("resize"));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const cleanup = (e: PointerEvent) => {
|
const cleanup = (e: PointerEvent) => {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { makeKeyboardModifiersBitfield, textInputCleanup, getLocalizedScanCode }
|
||||||
import { platformIsMac } from "@graphite/utility-functions/platform";
|
import { platformIsMac } from "@graphite/utility-functions/platform";
|
||||||
import { extractPixelData } from "@graphite/utility-functions/rasterization";
|
import { extractPixelData } from "@graphite/utility-functions/rasterization";
|
||||||
import { stripIndents } from "@graphite/utility-functions/strip-indents";
|
import { stripIndents } from "@graphite/utility-functions/strip-indents";
|
||||||
|
import { updateBoundsOfViewports } from "@graphite/utility-functions/viewports";
|
||||||
import { type Editor } from "@graphite/wasm-communication/editor";
|
import { type Editor } from "@graphite/wasm-communication/editor";
|
||||||
import { TriggerPaste } from "@graphite/wasm-communication/messages";
|
import { TriggerPaste } from "@graphite/wasm-communication/messages";
|
||||||
|
|
||||||
|
|
@ -29,7 +30,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const listeners: { target: EventListenerTarget; eventName: EventName; action: (event: any) => void; options?: AddEventListenerOptions }[] = [
|
const listeners: { target: EventListenerTarget; eventName: EventName; action: (event: any) => void; options?: AddEventListenerOptions }[] = [
|
||||||
{ target: window, eventName: "resize", action: () => onWindowResize(window.document.body) },
|
{ target: window, eventName: "resize", action: () => updateBoundsOfViewports(editor, window.document.body) },
|
||||||
{ target: window, eventName: "beforeunload", action: (e: BeforeUnloadEvent) => onBeforeUnload(e) },
|
{ target: window, eventName: "beforeunload", action: (e: BeforeUnloadEvent) => onBeforeUnload(e) },
|
||||||
{ target: window, eventName: "keyup", action: (e: KeyboardEvent) => onKeyUp(e) },
|
{ target: window, eventName: "keyup", action: (e: KeyboardEvent) => onKeyUp(e) },
|
||||||
{ target: window, eventName: "keydown", action: (e: KeyboardEvent) => onKeyDown(e) },
|
{ target: window, eventName: "keydown", action: (e: KeyboardEvent) => onKeyDown(e) },
|
||||||
|
|
@ -240,19 +241,6 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
|
|
||||||
// Window events
|
// Window events
|
||||||
|
|
||||||
function onWindowResize(container: HTMLElement) {
|
|
||||||
const viewports = Array.from(container.querySelectorAll("[data-viewport]"));
|
|
||||||
const boundsOfViewports = viewports.map((canvas) => {
|
|
||||||
const bounds = canvas.getBoundingClientRect();
|
|
||||||
return [bounds.left, bounds.top, bounds.right, bounds.bottom];
|
|
||||||
});
|
|
||||||
|
|
||||||
const flattened = boundsOfViewports.flat();
|
|
||||||
const data = Float64Array.from(flattened);
|
|
||||||
|
|
||||||
if (boundsOfViewports.length > 0) editor.handle.boundsOfViewports(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onBeforeUnload(e: BeforeUnloadEvent) {
|
async function onBeforeUnload(e: BeforeUnloadEvent) {
|
||||||
const activeDocument = get(portfolio).documents[get(portfolio).activeDocumentIndex];
|
const activeDocument = get(portfolio).documents[get(portfolio).activeDocumentIndex];
|
||||||
if (activeDocument && !activeDocument.isAutoSaved) editor.handle.triggerAutoSave(activeDocument.id);
|
if (activeDocument && !activeDocument.isAutoSaved) editor.handle.triggerAutoSave(activeDocument.id);
|
||||||
|
|
@ -390,7 +378,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
// Bind the event listeners
|
// Bind the event listeners
|
||||||
bindListeners();
|
bindListeners();
|
||||||
// Resize on creation
|
// Resize on creation
|
||||||
onWindowResize(window.document.body);
|
updateBoundsOfViewports(editor, window.document.body);
|
||||||
|
|
||||||
// Return the destructor
|
// Return the destructor
|
||||||
return unbindListeners;
|
return unbindListeners;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { type Editor } from "@graphite/wasm-communication/editor";
|
||||||
import {
|
import {
|
||||||
defaultWidgetLayout,
|
defaultWidgetLayout,
|
||||||
patchWidgetLayout,
|
patchWidgetLayout,
|
||||||
TriggerRefreshBoundsOfViewports,
|
|
||||||
UpdateDocumentBarLayout,
|
UpdateDocumentBarLayout,
|
||||||
UpdateDocumentModeLayout,
|
UpdateDocumentModeLayout,
|
||||||
UpdateToolOptionsLayout,
|
UpdateToolOptionsLayout,
|
||||||
|
|
@ -13,6 +12,7 @@ import {
|
||||||
UpdateWorkingColorsLayout,
|
UpdateWorkingColorsLayout,
|
||||||
UpdateNodeGraphBarLayout,
|
UpdateNodeGraphBarLayout,
|
||||||
TriggerGraphViewOverlay,
|
TriggerGraphViewOverlay,
|
||||||
|
TriggerDelayedZoomCanvasToFitAll,
|
||||||
} from "@graphite/wasm-communication/messages";
|
} from "@graphite/wasm-communication/messages";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
|
|
@ -83,16 +83,6 @@ export function createDocumentState(editor: Editor) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Other
|
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerRefreshBoundsOfViewports, async () => {
|
|
||||||
// Wait to display the unpopulated document panel (missing: tools, options bar content, scrollbar positioning, and canvas)
|
|
||||||
await tick();
|
|
||||||
// Wait to display the populated document panel
|
|
||||||
await tick();
|
|
||||||
|
|
||||||
// Request a resize event so the viewport gets measured now that the canvas is populated and positioned correctly
|
|
||||||
window.dispatchEvent(new CustomEvent("resize"));
|
|
||||||
});
|
|
||||||
// Show or hide the graph view overlay
|
// Show or hide the graph view overlay
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerGraphViewOverlay, (triggerGraphViewOverlay) => {
|
editor.subscriptions.subscribeJsMessage(TriggerGraphViewOverlay, (triggerGraphViewOverlay) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
|
|
@ -100,6 +90,9 @@ export function createDocumentState(editor: Editor) {
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
editor.subscriptions.subscribeJsMessage(TriggerDelayedZoomCanvasToFitAll, () => {
|
||||||
|
setTimeout(() => editor.handle.zoomCanvasToFitAll(), 0);
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { type Editor } from "@graphite/wasm-communication/editor";
|
||||||
|
|
||||||
|
export function updateBoundsOfViewports(editor: Editor, container: HTMLElement) {
|
||||||
|
const viewports = Array.from(container.querySelectorAll("[data-viewport]"));
|
||||||
|
const boundsOfViewports = viewports.map((canvas) => {
|
||||||
|
const bounds = canvas.getBoundingClientRect();
|
||||||
|
return [bounds.left, bounds.top, bounds.right, bounds.bottom];
|
||||||
|
});
|
||||||
|
|
||||||
|
const flattened = boundsOfViewports.flat();
|
||||||
|
const data = Float64Array.from(flattened);
|
||||||
|
|
||||||
|
if (boundsOfViewports.length > 0) editor.handle.boundsOfViewports(data);
|
||||||
|
}
|
||||||
|
|
@ -649,6 +649,8 @@ export class TriggerCopyToClipboardBlobUrl extends JsMessage {
|
||||||
readonly blobUrl!: string;
|
readonly blobUrl!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TriggerDelayedZoomCanvasToFitAll extends JsMessage {}
|
||||||
|
|
||||||
export class TriggerDownloadBlobUrl extends JsMessage {
|
export class TriggerDownloadBlobUrl extends JsMessage {
|
||||||
readonly layerName!: string;
|
readonly layerName!: string;
|
||||||
|
|
||||||
|
|
@ -672,8 +674,6 @@ export class TriggerDownloadTextFile extends JsMessage {
|
||||||
readonly name!: string;
|
readonly name!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerRefreshBoundsOfViewports extends JsMessage {}
|
|
||||||
|
|
||||||
export class TriggerRevokeBlobUrl extends JsMessage {
|
export class TriggerRevokeBlobUrl extends JsMessage {
|
||||||
readonly url!: string;
|
readonly url!: string;
|
||||||
}
|
}
|
||||||
|
|
@ -782,8 +782,6 @@ export class TriggerAboutGraphiteLocalizedCommitDate extends JsMessage {
|
||||||
readonly commitDate!: string;
|
readonly commitDate!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerViewportResize extends JsMessage {}
|
|
||||||
|
|
||||||
// TODO: Eventually remove this (probably starting late 2024)
|
// TODO: Eventually remove this (probably starting late 2024)
|
||||||
export class TriggerUpgradeDocumentToVectorManipulationFormat extends JsMessage {
|
export class TriggerUpgradeDocumentToVectorManipulationFormat extends JsMessage {
|
||||||
readonly documentId!: bigint;
|
readonly documentId!: bigint;
|
||||||
|
|
@ -1428,6 +1426,7 @@ export const messageMakers: Record<string, MessageMaker> = {
|
||||||
DisplayRemoveEditableTextbox,
|
DisplayRemoveEditableTextbox,
|
||||||
TriggerAboutGraphiteLocalizedCommitDate,
|
TriggerAboutGraphiteLocalizedCommitDate,
|
||||||
TriggerCopyToClipboardBlobUrl,
|
TriggerCopyToClipboardBlobUrl,
|
||||||
|
TriggerDelayedZoomCanvasToFitAll,
|
||||||
TriggerFetchAndOpenDocument,
|
TriggerFetchAndOpenDocument,
|
||||||
TriggerDownloadBlobUrl,
|
TriggerDownloadBlobUrl,
|
||||||
TriggerDownloadImage,
|
TriggerDownloadImage,
|
||||||
|
|
@ -1441,13 +1440,11 @@ export const messageMakers: Record<string, MessageMaker> = {
|
||||||
TriggerLoadPreferences,
|
TriggerLoadPreferences,
|
||||||
TriggerOpenDocument,
|
TriggerOpenDocument,
|
||||||
TriggerPaste,
|
TriggerPaste,
|
||||||
TriggerRefreshBoundsOfViewports,
|
|
||||||
TriggerRevokeBlobUrl,
|
TriggerRevokeBlobUrl,
|
||||||
TriggerSavePreferences,
|
TriggerSavePreferences,
|
||||||
TriggerTextCommit,
|
TriggerTextCommit,
|
||||||
TriggerTextCopy,
|
TriggerTextCopy,
|
||||||
TriggerUpgradeDocumentToVectorManipulationFormat,
|
TriggerUpgradeDocumentToVectorManipulationFormat,
|
||||||
TriggerViewportResize,
|
|
||||||
TriggerVisitLink,
|
TriggerVisitLink,
|
||||||
UpdateActiveDocument,
|
UpdateActiveDocument,
|
||||||
UpdateBox,
|
UpdateBox,
|
||||||
|
|
|
||||||
|
|
@ -342,6 +342,13 @@ impl EditorHandle {
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Zoom the canvas to fit all content
|
||||||
|
#[wasm_bindgen(js_name = zoomCanvasToFitAll)]
|
||||||
|
pub fn zoom_canvas_to_fit_all(&self) {
|
||||||
|
let message = DocumentMessage::ZoomCanvasToFitAll;
|
||||||
|
self.dispatch(message);
|
||||||
|
}
|
||||||
|
|
||||||
/// Mouse movement within the screenspace bounds of the viewport
|
/// Mouse movement within the screenspace bounds of the viewport
|
||||||
#[wasm_bindgen(js_name = onMouseMove)]
|
#[wasm_bindgen(js_name = onMouseMove)]
|
||||||
pub fn on_mouse_move(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
pub fn on_mouse_move(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
||||||
|
|
|
||||||
|
|
@ -93,11 +93,17 @@ impl log::Log for WasmLog {
|
||||||
log::Level::Error => (error, "error", "color:red"),
|
log::Level::Error => (error, "error", "color:red"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let file = record.file().unwrap_or_else(|| record.target());
|
// The %c is replaced by the message color
|
||||||
let line = record.line().map_or_else(|| "[Unknown]".to_string(), |line| line.to_string());
|
if record.level() == log::Level::Info {
|
||||||
let args = record.args();
|
// We don't print the file name and line number for info-level logs because it's used for printing the message system logs
|
||||||
let msg = &format!("%c{name}\t{file}:{line}\n{args}"); // The %c is replaced by the message color
|
log(&format!("%c{}\t{}", name, record.args()), color);
|
||||||
log(msg, color)
|
} else {
|
||||||
|
let file = record.file().unwrap_or_else(|| record.target());
|
||||||
|
let line = record.line().map_or_else(|| "[Unknown]".to_string(), |line| line.to_string());
|
||||||
|
let args = record.args();
|
||||||
|
|
||||||
|
log(&format!("%c{name}\t{file}:{line}\n{args}"), color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&self) {}
|
fn flush(&self) {}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue