Skip to content

Commit 010ba5a

Browse files
mfish33Keavon0HyperCube
authored
Welcome screen, refactor to allow zero documents, and add TS typing to widgets (#702)
* unfinished implementation * Add frontend for the empty panel screen * Add an icon for Folder based on NodeFolder * fixed messages causing peicees of ui not to render on new document * Standardize nextTick syntax * WIP generisization of component subscriptions (not compiling yet) * Fix crash when loading font and there is no active document * Only advertise tool actions with a document * Fix failure to create new document * Initalise the properties panel * Fix highlight tab, canvas jump, warns and layer tree * Fix tests * Possibly fix some things? * Move WorkingColors layout definition to backend * Standardize action macro formatting * Provide typing for widgets in TS/Vue and associated cleanup * Fix viewport positioning initialization * Fix menu bar init at startup not document creation * Fix no viewport bounds bug * Change !=0 to >0 * Simplify the init system Closes #656 Co-authored-by: Keavon Chambers <[email protected]> Co-authored-by: 0hypercube <[email protected]>
1 parent b679351 commit 010ba5a

File tree

77 files changed

+1814
-1091
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+1814
-1091
lines changed

editor/src/communication/dispatcher.rs

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::broadcast_message_handler::BroadcastMessageHandler;
2+
use crate::consts::{DEFAULT_FONT_FAMILY, DEFAULT_FONT_STYLE};
23
use crate::document::PortfolioMessageHandler;
34
use crate::global::GlobalMessageHandler;
45
use crate::input::{InputMapperMessageHandler, InputPreprocessorMessageHandler};
@@ -7,6 +8,8 @@ use crate::message_prelude::*;
78
use crate::viewport_tools::tool_message_handler::ToolMessageHandler;
89
use crate::workspace::WorkspaceMessageHandler;
910

11+
use graphene::layers::text_layer::Font;
12+
1013
use std::collections::VecDeque;
1114

1215
#[derive(Debug, Default)]
@@ -42,8 +45,6 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
4245
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::FolderChanged)),
4346
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::DocumentStructureChanged)),
4447
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerTreeStructure),
45-
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateActiveDocument),
46-
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateOpenDocumentsList),
4748
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
4849
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerSignal(BroadcastSignalDiscriminant::DocumentIsDirty)),
4950
];
@@ -87,21 +88,35 @@ impl Dispatcher {
8788
match message {
8889
#[remain::unsorted]
8990
NoOp => {}
91+
#[remain::unsorted]
92+
Init => {
93+
// Display the menu bar at the top of the window
94+
let message = MenuBarMessage::SendLayout.into();
95+
queue.push_back(message);
96+
97+
// Load the default font
98+
let font = Font::new(DEFAULT_FONT_FAMILY.into(), DEFAULT_FONT_STYLE.into());
99+
let message = FrontendMessage::TriggerFontLoad { font, is_default: true }.into();
100+
queue.push_back(message);
101+
}
102+
90103
Broadcast(message) => self.message_handlers.broadcast_message_handler.process_action(message, (), &mut queue),
91104
Dialog(message) => {
92105
self.message_handlers
93106
.dialog_message_handler
94107
.process_action(message, &self.message_handlers.portfolio_message_handler, &mut queue);
95108
}
96109
Frontend(message) => {
97-
// Image and font loading should be immediately handled
98-
if let FrontendMessage::UpdateImageData { .. } | FrontendMessage::TriggerFontLoad { .. } = message {
110+
// Handle these messages immediately by returning early
111+
if let FrontendMessage::UpdateImageData { .. } | FrontendMessage::TriggerFontLoad { .. } | FrontendMessage::TriggerRefreshBoundsOfViewports = message {
99112
self.responses.push(message);
113+
114+
// Return early to avoid running the code after the match block
100115
return;
116+
} else {
117+
// `FrontendMessage`s are saved and will be sent to the frontend after the message queue is done being processed
118+
self.responses.push(message);
101119
}
102-
103-
// `FrontendMessage`s are saved and will be sent to the frontend after the message queue is done being processed
104-
self.responses.push(message);
105120
}
106121
Global(message) => {
107122
self.message_handlers.global_message_handler.process_action(message, (), &mut queue);
@@ -122,15 +137,19 @@ impl Dispatcher {
122137
.process_action(message, &self.message_handlers.input_preprocessor_message_handler, &mut queue);
123138
}
124139
Tool(message) => {
125-
self.message_handlers.tool_message_handler.process_action(
126-
message,
127-
(
128-
self.message_handlers.portfolio_message_handler.active_document(),
129-
&self.message_handlers.input_preprocessor_message_handler,
130-
self.message_handlers.portfolio_message_handler.font_cache(),
131-
),
132-
&mut queue,
133-
);
140+
if let Some(document) = self.message_handlers.portfolio_message_handler.active_document() {
141+
self.message_handlers.tool_message_handler.process_action(
142+
message,
143+
(
144+
document,
145+
&self.message_handlers.input_preprocessor_message_handler,
146+
self.message_handlers.portfolio_message_handler.font_cache(),
147+
),
148+
&mut queue,
149+
);
150+
} else {
151+
log::warn!("Called ToolMessage without an active document.\nGot {:?}", message);
152+
}
134153
}
135154
Workspace(message) => {
136155
self.message_handlers
@@ -153,7 +172,9 @@ impl Dispatcher {
153172
list.extend(self.message_handlers.input_preprocessor_message_handler.actions());
154173
list.extend(self.message_handlers.input_mapper_message_handler.actions());
155174
list.extend(self.message_handlers.global_message_handler.actions());
156-
list.extend(self.message_handlers.tool_message_handler.actions());
175+
if self.message_handlers.portfolio_message_handler.active_document().is_some() {
176+
list.extend(self.message_handlers.tool_message_handler.actions());
177+
}
157178
list.extend(self.message_handlers.portfolio_message_handler.actions());
158179
list
159180
}
@@ -192,6 +213,7 @@ mod test {
192213
set_uuid_seed(0);
193214
let mut editor = Editor::new();
194215

216+
editor.new_document();
195217
editor.select_primary_color(Color::RED);
196218
editor.draw_rect(100., 200., 300., 400.);
197219
editor.select_primary_color(Color::BLUE);
@@ -211,14 +233,14 @@ mod test {
211233
init_logger();
212234
let mut editor = create_editor_with_three_layers();
213235

214-
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
236+
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
215237
editor.handle_message(PortfolioMessage::Copy { clipboard: Clipboard::Internal });
216238
editor.handle_message(PortfolioMessage::PasteIntoFolder {
217239
clipboard: Clipboard::Internal,
218240
folder_path: vec![],
219241
insert_index: -1,
220242
});
221-
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
243+
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
222244

223245
let layers_before_copy = document_before_copy.root.as_folder().unwrap().layers();
224246
let layers_after_copy = document_after_copy.root.as_folder().unwrap().layers();
@@ -245,7 +267,7 @@ mod test {
245267
init_logger();
246268
let mut editor = create_editor_with_three_layers();
247269

248-
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
270+
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
249271
let shape_id = document_before_copy.root.as_folder().unwrap().layer_ids[1];
250272

251273
editor.handle_message(DocumentMessage::SetSelectedLayers {
@@ -258,7 +280,7 @@ mod test {
258280
insert_index: -1,
259281
});
260282

261-
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
283+
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
262284

263285
let layers_before_copy = document_before_copy.root.as_folder().unwrap().layers();
264286
let layers_after_copy = document_after_copy.root.as_folder().unwrap().layers();
@@ -290,7 +312,7 @@ mod test {
290312

291313
editor.handle_message(DocumentMessage::CreateEmptyFolder { container_path: vec![] });
292314

293-
let document_before_added_shapes = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
315+
let document_before_added_shapes = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
294316
let folder_id = document_before_added_shapes.root.as_folder().unwrap().layer_ids[FOLDER_INDEX];
295317

296318
// TODO: This adding of a Line and Pen should be rewritten using the corresponding functions in EditorTestUtils.
@@ -314,7 +336,7 @@ mod test {
314336
replacement_selected_layers: vec![vec![folder_id]],
315337
});
316338

317-
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
339+
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
318340

319341
editor.handle_message(PortfolioMessage::Copy { clipboard: Clipboard::Internal });
320342
editor.handle_message(DocumentMessage::DeleteSelectedLayers);
@@ -329,7 +351,7 @@ mod test {
329351
insert_index: -1,
330352
});
331353

332-
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
354+
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
333355

334356
let layers_before_copy = document_before_copy.root.as_folder().unwrap().layers();
335357
let layers_after_copy = document_after_copy.root.as_folder().unwrap().layers();
@@ -380,7 +402,7 @@ mod test {
380402
const SHAPE_INDEX: usize = 1;
381403
const RECT_INDEX: usize = 0;
382404

383-
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
405+
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
384406
let rect_id = document_before_copy.root.as_folder().unwrap().layer_ids[RECT_INDEX];
385407
let ellipse_id = document_before_copy.root.as_folder().unwrap().layer_ids[ELLIPSE_INDEX];
386408

@@ -401,7 +423,7 @@ mod test {
401423
insert_index: -1,
402424
});
403425

404-
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
426+
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
405427

406428
let layers_before_copy = document_before_copy.root.as_folder().unwrap().layers();
407429
let layers_after_copy = document_after_copy.root.as_folder().unwrap().layers();
@@ -431,7 +453,7 @@ mod test {
431453
fn map_to_vec(paths: Vec<&[LayerId]>) -> Vec<Vec<LayerId>> {
432454
paths.iter().map(|layer| layer.to_vec()).collect::<Vec<_>>()
433455
}
434-
let sorted_layers = map_to_vec(editor.dispatcher.message_handlers.portfolio_message_handler.active_document().all_layers_sorted());
456+
let sorted_layers = map_to_vec(editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().all_layers_sorted());
435457
println!("Sorted layers: {:?}", sorted_layers);
436458

437459
let verify_order = |handler: &mut DocumentMessageHandler| {
@@ -447,15 +469,15 @@ mod test {
447469
});
448470

449471
editor.handle_message(DocumentMessage::ReorderSelectedLayers { relative_index_offset: 1 });
450-
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut());
472+
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut().unwrap());
451473
assert_eq!(all, non_selected.into_iter().chain(selected.into_iter()).collect::<Vec<_>>());
452474

453475
editor.handle_message(DocumentMessage::ReorderSelectedLayers { relative_index_offset: -1 });
454-
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut());
476+
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut().unwrap());
455477
assert_eq!(all, selected.into_iter().chain(non_selected.into_iter()).collect::<Vec<_>>());
456478

457479
editor.handle_message(DocumentMessage::ReorderSelectedLayers { relative_index_offset: isize::MAX });
458-
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut());
480+
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut().unwrap());
459481
assert_eq!(all, non_selected.into_iter().chain(selected.into_iter()).collect::<Vec<_>>());
460482
}
461483

editor/src/communication/message.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ where
2222
pub enum Message {
2323
#[remain::unsorted]
2424
NoOp,
25+
#[remain::unsorted]
26+
Init,
2527
#[child]
2628
Broadcast(BroadcastMessage),
2729
#[child]

editor/src/dialog/dialog_message.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub enum DialogMessage {
1818
// Messages
1919
CloseAllDocumentsWithConfirmation,
2020
CloseDialogAndThen {
21-
followup: Box<Message>,
21+
followups: Vec<Message>,
2222
},
2323
DisplayDialogError {
2424
title: String,

editor/src/dialog/dialog_message_handler.rs

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ impl MessageHandler<DialogMessage, &PortfolioMessageHandler> for DialogMessageHa
2525
dialog.register_properties(responses, LayoutTarget::DialogDetails);
2626
responses.push_back(FrontendMessage::DisplayDialog { icon: "Copy".to_string() }.into());
2727
}
28-
DialogMessage::CloseDialogAndThen { followup } => {
28+
DialogMessage::CloseDialogAndThen { followups } => {
2929
responses.push_back(FrontendMessage::DisplayDialogDismiss.into());
30-
responses.push_back(*followup);
30+
for message in followups.into_iter() {
31+
responses.push_back(message);
32+
}
3133
}
3234
DialogMessage::DisplayDialogError { title, description } => {
3335
let dialog = dialogs::Error { title, description };
@@ -54,36 +56,38 @@ impl MessageHandler<DialogMessage, &PortfolioMessageHandler> for DialogMessageHa
5456
responses.push_back(FrontendMessage::DisplayDialog { icon: "Warning".to_string() }.into());
5557
}
5658
DialogMessage::RequestExportDialog => {
57-
let artboard_handler = &portfolio.active_document().artboard_message_handler;
58-
let mut index = 0;
59-
let artboards = artboard_handler
60-
.artboard_ids
61-
.iter()
62-
.rev()
63-
.filter_map(|&artboard| artboard_handler.artboards_graphene_document.layer(&[artboard]).ok().map(|layer| (artboard, layer)))
64-
.map(|(artboard, layer)| {
65-
(
66-
artboard,
67-
format!(
68-
"Artboard: {}",
69-
layer.name.clone().unwrap_or_else(|| {
70-
index += 1;
71-
format!("Untitled {index}")
72-
})
73-
),
74-
)
75-
})
76-
.collect();
59+
if let Some(document) = portfolio.active_document() {
60+
let artboard_handler = &document.artboard_message_handler;
61+
let mut index = 0;
62+
let artboards = artboard_handler
63+
.artboard_ids
64+
.iter()
65+
.rev()
66+
.filter_map(|&artboard| artboard_handler.artboards_graphene_document.layer(&[artboard]).ok().map(|layer| (artboard, layer)))
67+
.map(|(artboard, layer)| {
68+
(
69+
artboard,
70+
format!(
71+
"Artboard: {}",
72+
layer.name.clone().unwrap_or_else(|| {
73+
index += 1;
74+
format!("Untitled {index}")
75+
})
76+
),
77+
)
78+
})
79+
.collect();
7780

78-
self.export_dialog = Export {
79-
file_name: portfolio.active_document().name.clone(),
80-
scale_factor: 1.,
81-
artboards,
82-
has_selection: portfolio.active_document().selected_layers().next().is_some(),
83-
..Default::default()
84-
};
85-
self.export_dialog.register_properties(responses, LayoutTarget::DialogDetails);
86-
responses.push_back(FrontendMessage::DisplayDialog { icon: "File".to_string() }.into());
81+
self.export_dialog = Export {
82+
file_name: document.name.clone(),
83+
scale_factor: 1.,
84+
artboards,
85+
has_selection: document.selected_layers().next().is_some(),
86+
..Default::default()
87+
};
88+
self.export_dialog.register_properties(responses, LayoutTarget::DialogDetails);
89+
responses.push_back(FrontendMessage::DisplayDialog { icon: "File".to_string() }.into());
90+
}
8791
}
8892
DialogMessage::RequestNewDocumentDialog => {
8993
self.new_document_dialog = NewDocument {
@@ -97,5 +101,9 @@ impl MessageHandler<DialogMessage, &PortfolioMessageHandler> for DialogMessageHa
97101
}
98102
}
99103

100-
advertise_actions!(DialogMessageDiscriminant;RequestNewDocumentDialog,RequestExportDialog,CloseAllDocumentsWithConfirmation);
104+
advertise_actions!(DialogMessageDiscriminant;
105+
RequestNewDocumentDialog,
106+
RequestExportDialog,
107+
CloseAllDocumentsWithConfirmation,
108+
);
101109
}

editor/src/dialog/dialogs/close_all_documents_dialog.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ impl PropertyHolder for CloseAllDocuments {
1212
min_width: 96,
1313
on_update: WidgetCallback::new(|_| {
1414
DialogMessage::CloseDialogAndThen {
15-
followup: Box::new(PortfolioMessage::CloseAllDocuments.into()),
15+
followups: vec![PortfolioMessage::CloseAllDocuments.into()],
1616
}
1717
.into()
1818
}),

editor/src/dialog/dialogs/close_document_dialog.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::layout::widgets::*;
2-
use crate::message_prelude::{DialogMessage, DocumentMessage, FrontendMessage, PortfolioMessage};
2+
use crate::message_prelude::*;
33

44
/// A dialog for confirming the closing a document with unsaved changes.
55
pub struct CloseDocument {
@@ -18,7 +18,7 @@ impl PropertyHolder for CloseDocument {
1818
emphasized: true,
1919
on_update: WidgetCallback::new(|_| {
2020
DialogMessage::CloseDialogAndThen {
21-
followup: Box::new(DocumentMessage::SaveDocument.into()),
21+
followups: vec![DocumentMessage::SaveDocument.into()],
2222
}
2323
.into()
2424
}),
@@ -29,7 +29,7 @@ impl PropertyHolder for CloseDocument {
2929
min_width: 96,
3030
on_update: WidgetCallback::new(move |_| {
3131
DialogMessage::CloseDialogAndThen {
32-
followup: Box::new(PortfolioMessage::CloseDocument { document_id }.into()),
32+
followups: vec![BroadcastSignal::ToolAbort.into(), PortfolioMessage::CloseDocument { document_id }.into()],
3333
}
3434
.into()
3535
}),

editor/src/dialog/dialogs/export_dialog.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ impl PropertyHolder for Export {
3333
WidgetHolder::new(Widget::TextInput(TextInput {
3434
value: self.file_name.clone(),
3535
on_update: WidgetCallback::new(|text_input: &TextInput| ExportDialogUpdate::FileName(text_input.value.clone()).into()),
36+
..Default::default()
3637
})),
3738
];
3839

@@ -123,7 +124,7 @@ impl PropertyHolder for Export {
123124
emphasized: true,
124125
on_update: WidgetCallback::new(|_| {
125126
DialogMessage::CloseDialogAndThen {
126-
followup: Box::new(ExportDialogUpdate::Submit.into()),
127+
followups: vec![ExportDialogUpdate::Submit.into()],
127128
}
128129
.into()
129130
}),

0 commit comments

Comments
 (0)