Skip to content
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
14 changes: 9 additions & 5 deletions editor/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct Dispatcher {

#[derive(Debug, Default)]
pub struct DispatcherMessageHandlers {
animation_message_handler: AnimationMessageHandler,
broadcast_message_handler: BroadcastMessageHandler,
debug_message_handler: DebugMessageHandler,
dialog_message_handler: DialogMessageHandler,
Expand Down Expand Up @@ -50,12 +51,9 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerStructure),
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
];
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::AnimationFrame)),
MessageDiscriminant::InputPreprocessor(InputPreprocessorMessageDiscriminant::FrameTimeAdvance),
];
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::AnimationFrame))];
// TODO: Find a way to combine these with the list above. We use strings for now since these are the standard variant names used by multiple messages. But having these also type-checked would be best.
const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport", "Overlays", "Draw"];
const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport", "Overlays", "Draw", "CurrentTime", "Time"];

impl Dispatcher {
pub fn new() -> Self {
Expand Down Expand Up @@ -177,6 +175,9 @@ impl Dispatcher {
// Finish loading persistent data from the browser database
queue.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments);
}
Message::Animation(message) => {
self.message_handlers.animation_message_handler.process_message(message, &mut queue, ());
}
Message::Batched(messages) => {
messages.iter().for_each(|message| self.handle_message(message.to_owned(), false));
}
Expand Down Expand Up @@ -232,6 +233,7 @@ impl Dispatcher {
let preferences = &self.message_handlers.preferences_message_handler;
let current_tool = &self.message_handlers.tool_message_handler.tool_state.tool_data.active_tool_type;
let message_logging_verbosity = self.message_handlers.debug_message_handler.message_logging_verbosity;
let timing_information = self.message_handlers.animation_message_handler.timing_information();

self.message_handlers.portfolio_message_handler.process_message(
message,
Expand All @@ -241,6 +243,7 @@ impl Dispatcher {
preferences,
current_tool,
message_logging_verbosity,
timing_information,
},
);
}
Expand Down Expand Up @@ -283,6 +286,7 @@ impl Dispatcher {
// TODO: Reduce the number of heap allocations
let mut list = Vec::new();
list.extend(self.message_handlers.dialog_message_handler.actions());
list.extend(self.message_handlers.animation_message_handler.actions());
list.extend(self.message_handlers.input_preprocessor_message_handler.actions());
list.extend(self.message_handlers.key_mapping_message_handler.actions());
list.extend(self.message_handlers.debug_message_handler.actions());
Expand Down
17 changes: 17 additions & 0 deletions editor/src/messages/animation/animation_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use crate::messages::prelude::*;

use super::animation_message_handler::AnimationTimeMode;

#[impl_message(Message, Animation)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum AnimationMessage {
ToggleLivePreview,
EnableLivePreview,
DisableLivePreview,
ResetAnimation,
SetFrameIndex(f64),
SetTime(f64),
UpdateTime,
IncrementFrameCounter,
SetAnimationTimeMode(AnimationTimeMode),
}
84 changes: 84 additions & 0 deletions editor/src/messages/animation/animation_message_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::time::Duration;

use crate::messages::prelude::*;

use super::TimingInformation;

#[derive(PartialEq, Clone, Default, Debug, serde::Serialize, serde::Deserialize)]
pub enum AnimationTimeMode {
#[default]
TimeBased,
FrameBased,
}

#[derive(Debug, Default)]
pub struct AnimationMessageHandler {
live_preview: bool,
timestamp: f64,
frame_index: f64,
animation_start: Option<f64>,
fps: f64,
animation_time_mode: AnimationTimeMode,
}
impl AnimationMessageHandler {
pub(crate) fn timing_information(&self) -> TimingInformation {
let animation_time = self.timestamp - self.animation_start.unwrap_or(self.timestamp);
let animation_time = match self.animation_time_mode {
AnimationTimeMode::TimeBased => Duration::from_millis(animation_time as u64),
AnimationTimeMode::FrameBased => Duration::from_secs((self.frame_index / self.fps) as u64),
};
TimingInformation { time: self.timestamp, animation_time }
}
}

impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
fn process_message(&mut self, message: AnimationMessage, responses: &mut VecDeque<Message>, _data: ()) {
match message {
AnimationMessage::ToggleLivePreview => {
if self.animation_start.is_none() {
self.animation_start = Some(self.timestamp);
}
self.live_preview = !self.live_preview
}
AnimationMessage::EnableLivePreview => {
if self.animation_start.is_none() {
self.animation_start = Some(self.timestamp);
}
self.live_preview = true
}
AnimationMessage::DisableLivePreview => self.live_preview = false,
AnimationMessage::SetFrameIndex(frame) => {
self.frame_index = frame;
log::debug!("set frame index to {}", frame);
responses.add(PortfolioMessage::SubmitActiveGraphRender)
}
AnimationMessage::SetTime(time) => {
self.timestamp = time;
responses.add(AnimationMessage::UpdateTime);
}
AnimationMessage::IncrementFrameCounter => {
if self.live_preview {
self.frame_index += 1.;
responses.add(AnimationMessage::UpdateTime);
}
}
AnimationMessage::UpdateTime => {
if self.live_preview {
responses.add(PortfolioMessage::SubmitActiveGraphRender)
}
}
AnimationMessage::ResetAnimation => {
self.frame_index = 0.;
self.animation_start = None;
responses.add(PortfolioMessage::SubmitActiveGraphRender)
}
AnimationMessage::SetAnimationTimeMode(animation_time_mode) => self.animation_time_mode = animation_time_mode,
}
}

advertise_actions!(AnimationMessageDiscriminant;
ToggleLivePreview,
SetFrameIndex,
ResetAnimation,
);
}
9 changes: 9 additions & 0 deletions editor/src/messages/animation/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mod animation_message;
mod animation_message_handler;

#[doc(inline)]
pub use animation_message::{AnimationMessage, AnimationMessageDiscriminant};
#[doc(inline)]
pub use animation_message_handler::AnimationMessageHandler;

pub use graphene_core::application_io::TimingInformation;
3 changes: 3 additions & 0 deletions editor/src/messages/input_mapper/input_mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,9 @@ pub fn input_mappings() -> Mapping {
entry!(KeyDown(Digit0); modifiers=[Alt], action_dispatch=DebugMessage::MessageOff),
entry!(KeyDown(Digit1); modifiers=[Alt], action_dispatch=DebugMessage::MessageNames),
entry!(KeyDown(Digit2); modifiers=[Alt], action_dispatch=DebugMessage::MessageContents),
// AnimationMessage
entry!(KeyDown(Space); modifiers=[Shift], action_dispatch=AnimationMessage::ToggleLivePreview),
entry!(KeyDown(ArrowLeft); modifiers=[Control], action_dispatch=AnimationMessage::ResetAnimation),
];
let (mut key_up, mut key_down, mut key_up_no_repeat, mut key_down_no_repeat, mut double_click, mut wheel_scroll, mut pointer_move) = mappings;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys};
use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ViewportBounds};
use crate::messages::prelude::*;
use core::time::Duration;

#[impl_message(Message, InputPreprocessor)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
Expand All @@ -13,6 +12,6 @@ pub enum InputPreprocessorMessage {
PointerDown { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
PointerMove { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
PointerUp { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
FrameTimeAdvance { timestamp: Duration },
CurrentTime { timestamp: u64 },
WheelScroll { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct InputPreprocessorMessageData {
#[derive(Debug, Default)]
pub struct InputPreprocessorMessageHandler {
pub frame_time: FrameTimeInfo,
pub time: u64,
pub keyboard: KeyStates,
pub mouse: MouseState,
pub viewport_bounds: ViewportBounds,
Expand Down Expand Up @@ -93,8 +94,9 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageData> for

self.translate_mouse_event(mouse_state, false, responses);
}
InputPreprocessorMessage::FrameTimeAdvance { timestamp } => {
self.frame_time.advance_timestamp(timestamp);
InputPreprocessorMessage::CurrentTime { timestamp } => {
responses.add(AnimationMessage::SetTime(timestamp as f64));
self.time = timestamp;
}
InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => {
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
Expand Down
2 changes: 2 additions & 0 deletions editor/src/messages/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub enum Message {
StartBuffer,
EndBuffer(graphene_std::renderer::RenderMetadata),

#[child]
Animation(AnimationMessage),
#[child]
Broadcast(BroadcastMessage),
#[child]
Expand Down
1 change: 1 addition & 0 deletions editor/src/messages/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! The root-level messages forming the first layer of the message system architecture.

pub mod animation;
pub mod broadcast;
pub mod debug;
pub mod dialog;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2086,7 +2086,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeDefinition {
identifier: "Text",
category: "Vector",
category: "Text",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::proto("graphene_std::text::TextNode"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use graphene_core::raster::{
use graphene_core::text::Font;
use graphene_core::vector::misc::CentroidType;
use graphene_core::vector::style::{GradientType, LineCap, LineJoin};
use graphene_std::animation::RealTimeMode;
use graphene_std::application_io::TextureFrameTable;
use graphene_std::transform::Footprint;
use graphene_std::vector::VectorDataTable;
Expand Down Expand Up @@ -165,6 +166,7 @@ pub(crate) fn property_from_type(
last.clone()
}
Some(x) if x == TypeId::of::<BlendMode>() => blend_mode(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<RealTimeMode>() => real_time_mode(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<RedGreenBlue>() => color_channel(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<RedGreenBlueAlpha>() => rgba_channel(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<NoiseType>() => noise_type(document_node, node_id, index, name, true),
Expand Down Expand Up @@ -778,6 +780,40 @@ pub fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize
LayoutGroup::Row { widgets }.with_tooltip("Color Channel")
}

pub fn real_time_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
};
if let Some(&TaggedValue::RealTimeMode(mode)) = input.as_non_exposed_value() {
let calculation_modes = [
RealTimeMode::Utc,
RealTimeMode::Year,
RealTimeMode::Hour,
RealTimeMode::Minute,
RealTimeMode::Second,
RealTimeMode::Millisecond,
];
let mut entries = Vec::with_capacity(calculation_modes.len());
for method in calculation_modes {
entries.push(
MenuListEntry::new(format!("{method:?}"))
.label(method.to_string())
.on_update(update_value(move |_| TaggedValue::RealTimeMode(method), node_id, index))
.on_commit(commit_value),
);
}
let entries = vec![entries];

widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
DropdownInput::new(entries).selected_index(Some(mode as u32)).widget_holder(),
]);
}
LayoutGroup::Row { widgets }.with_tooltip("Real Time Mode")
}

pub fn rgba_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
Expand Down
1 change: 1 addition & 0 deletions editor/src/messages/portfolio/portfolio_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ pub enum PortfolioMessage {
bounds: ExportBounds,
transparent_background: bool,
},
SubmitActiveGraphRender,
SubmitGraphRender {
document_id: DocumentId,
ignore_hash: bool,
Expand Down
10 changes: 10 additions & 0 deletions editor/src/messages/portfolio/portfolio_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use super::spreadsheet::SpreadsheetMessageHandler;
use super::utility_types::{PanelType, PersistentData};
use crate::application::generate_uuid;
use crate::consts::DEFAULT_DOCUMENT_NAME;
use crate::messages::animation::TimingInformation;
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
use crate::messages::dialog::simple_dialogs;
use crate::messages::frontend::utility_types::FrontendDocumentDetails;
Expand Down Expand Up @@ -32,6 +33,7 @@ pub struct PortfolioMessageData<'a> {
pub preferences: &'a PreferencesMessageHandler,
pub current_tool: &'a ToolType,
pub message_logging_verbosity: MessageLoggingVerbosity,
pub timing_information: TimingInformation,
}

#[derive(Debug, Default)]
Expand All @@ -56,6 +58,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
preferences,
current_tool,
message_logging_verbosity,
timing_information,
} = data;

match message {
Expand Down Expand Up @@ -306,6 +309,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
let _ = self.executor.submit_node_graph_evaluation(
self.documents.get_mut(document_id).expect("Tried to render non-existent document"),
ipp.viewport_bounds.size().as_uvec2(),
timing_information,
inspect_node,
true,
);
Expand Down Expand Up @@ -1072,11 +1076,17 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
});
}
}
PortfolioMessage::SubmitActiveGraphRender => {
if let Some(document_id) = self.active_document_id {
responses.add(PortfolioMessage::SubmitGraphRender { document_id, ignore_hash: false });
}
}
PortfolioMessage::SubmitGraphRender { document_id, ignore_hash } => {
let inspect_node = self.inspect_node_id();
let result = self.executor.submit_node_graph_evaluation(
self.documents.get_mut(&document_id).expect("Tried to render non-existent document"),
ipp.viewport_bounds.size().as_uvec2(),
timing_information,
inspect_node,
ignore_hash,
);
Expand Down
1 change: 1 addition & 0 deletions editor/src/messages/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pub use crate::utility_traits::{ActionList, AsMessage, MessageHandler, ToDiscriminant, TransitiveChild};

// Message, MessageData, MessageDiscriminant, MessageHandler
pub use crate::messages::animation::{AnimationMessage, AnimationMessageDiscriminant, AnimationMessageHandler};
pub use crate::messages::broadcast::{BroadcastMessage, BroadcastMessageDiscriminant, BroadcastMessageHandler};
pub use crate::messages::debug::{DebugMessage, DebugMessageDiscriminant, DebugMessageHandler};
pub use crate::messages::dialog::export_dialog::{ExportDialogMessage, ExportDialogMessageData, ExportDialogMessageDiscriminant, ExportDialogMessageHandler};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ where

/// Calculates the bounding box of the layer's text, based on the settings for max width and height specified in the typesetting config.
pub fn text_bounding_box(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, font_cache: &FontCache) -> Quad {
let (text, font, typesetting) = get_text(layer, &document.network_interface).expect("Text layer should have text when interacting with the Text tool");
let Some((text, font, typesetting)) = get_text(layer, &document.network_interface) else {
return Quad::from_box([DVec2::ZERO, DVec2::ZERO]);
};

let buzz_face = font_cache.get(font).map(|data| load_face(data));
let far = graphene_core::text::bounding_box(text, buzz_face.as_ref(), typesetting, false);
Expand Down
Loading