Skip to content

Commit 96d3ef2

Browse files
mfish33Keavon
andcommitted
Layout system implementation and applied to tool options bar (#499)
* initial layout system with tool options * cargo fmt * cargo fmt again * document bar defined on the backend * cargo fmt * removed RC<RefCell> * cargo fmt * - fix increment behavior - removed hashmap from layout message handler - removed no op message from layoutMessage * cargo fmt * only send documentBar when zoom or rotation is updated * ctrl-0 changes zoom properly * Code review changes Co-authored-by: Keavon Chambers <[email protected]>
1 parent 121a68a commit 96d3ef2

Some content is hidden

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

44 files changed

+1357
-532
lines changed

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

editor/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
2424
"serde",
2525
] }
2626
remain = "0.2.2"
27+
derivative = "2.2.0"
2728

2829
[dependencies.graphene]
2930
path = "../graphene"

editor/src/communication/dispatcher.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::document::PortfolioMessageHandler;
22
use crate::global::GlobalMessageHandler;
33
use crate::input::{InputMapperMessageHandler, InputPreprocessorMessageHandler};
4+
use crate::layout::layout_message_handler::LayoutMessageHandler;
45
use crate::message_prelude::*;
56
use crate::viewport_tools::tool_message_handler::ToolMessageHandler;
67

@@ -19,6 +20,7 @@ struct DispatcherMessageHandlers {
1920
global_message_handler: GlobalMessageHandler,
2021
input_mapper_message_handler: InputMapperMessageHandler,
2122
input_preprocessor_message_handler: InputPreprocessorMessageHandler,
23+
layout_message_handler: LayoutMessageHandler,
2224
portfolio_message_handler: PortfolioMessageHandler,
2325
tool_message_handler: ToolMessageHandler,
2426
}
@@ -76,6 +78,7 @@ impl Dispatcher {
7678
InputPreprocessor(message) => {
7779
self.message_handlers.input_preprocessor_message_handler.process_action(message, (), &mut self.message_queue);
7880
}
81+
Layout(message) => self.message_handlers.layout_message_handler.process_action(message, (), &mut self.message_queue),
7982
Portfolio(message) => {
8083
self.message_handlers
8184
.portfolio_message_handler

editor/src/communication/message.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pub enum Message {
3131
#[child]
3232
InputPreprocessor(InputPreprocessorMessage),
3333
#[child]
34+
Layout(LayoutMessage),
35+
#[child]
3436
Portfolio(PortfolioMessage),
3537
#[child]
3638
Tool(ToolMessage),

editor/src/document/document_message_handler.rs

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ use crate::consts::{
77
ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX, GRAPHITE_DOCUMENT_VERSION, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR,
88
};
99
use crate::input::InputPreprocessorMessageHandler;
10+
use crate::layout::widgets::{
11+
IconButton, LayoutRow, NumberInput, NumberInputIncrementBehavior, OptionalInput, PopoverButton, PropertyHolder, RadioEntryData, RadioInput, Separator, SeparatorDirection, SeparatorType, Widget,
12+
WidgetCallback, WidgetHolder, WidgetLayout,
13+
};
1014
use crate::message_prelude::*;
1115
use crate::EditorError;
1216

@@ -459,6 +463,154 @@ impl DocumentMessageHandler {
459463
}
460464
}
461465

466+
impl PropertyHolder for DocumentMessageHandler {
467+
fn properties(&self) -> WidgetLayout {
468+
WidgetLayout::new(vec![LayoutRow::Row {
469+
name: "".into(),
470+
widgets: vec![
471+
WidgetHolder::new(Widget::OptionalInput(OptionalInput {
472+
checked: self.snapping_enabled,
473+
icon: "Snapping".into(),
474+
tooltip: "Snapping".into(),
475+
on_update: WidgetCallback::new(|updated_optional_input| DocumentMessage::SetSnapping { snap: updated_optional_input.checked }.into()),
476+
})),
477+
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
478+
title: "Snapping".into(),
479+
text: "The contents of this popover menu are coming soon".into(),
480+
})),
481+
WidgetHolder::new(Widget::Separator(Separator {
482+
separator_type: SeparatorType::Unrelated,
483+
direction: SeparatorDirection::Horizontal,
484+
})),
485+
WidgetHolder::new(Widget::OptionalInput(OptionalInput {
486+
checked: true,
487+
icon: "Grid".into(),
488+
tooltip: "Grid".into(),
489+
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogComingSoon { issue: Some(318) }.into()),
490+
})),
491+
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
492+
title: "Grid".into(),
493+
text: "The contents of this popover menu are coming soon".into(),
494+
})),
495+
WidgetHolder::new(Widget::Separator(Separator {
496+
separator_type: SeparatorType::Unrelated,
497+
direction: SeparatorDirection::Horizontal,
498+
})),
499+
WidgetHolder::new(Widget::OptionalInput(OptionalInput {
500+
checked: self.overlays_visible,
501+
icon: "Overlays".into(),
502+
tooltip: "Overlays".into(),
503+
on_update: WidgetCallback::new(|updated_optional_input| {
504+
DocumentMessage::SetOverlaysVisibility {
505+
visible: updated_optional_input.checked,
506+
}
507+
.into()
508+
}),
509+
})),
510+
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
511+
title: "Overlays".into(),
512+
text: "The contents of this popover menu are coming soon".into(),
513+
})),
514+
WidgetHolder::new(Widget::Separator(Separator {
515+
separator_type: SeparatorType::Unrelated,
516+
direction: SeparatorDirection::Horizontal,
517+
})),
518+
WidgetHolder::new(Widget::RadioInput(RadioInput {
519+
selected_index: if self.view_mode == ViewMode::Normal { 0 } else { 1 },
520+
entries: vec![
521+
RadioEntryData {
522+
value: "normal".into(),
523+
icon: "ViewModeNormal".into(),
524+
tooltip: "View Mode: Normal".into(),
525+
on_update: WidgetCallback::new(|_| DocumentMessage::SetViewMode { view_mode: ViewMode::Normal }.into()),
526+
..RadioEntryData::default()
527+
},
528+
RadioEntryData {
529+
value: "outline".into(),
530+
icon: "ViewModeOutline".into(),
531+
tooltip: "View Mode: Outline".into(),
532+
on_update: WidgetCallback::new(|_| DocumentMessage::SetViewMode { view_mode: ViewMode::Outline }.into()),
533+
..RadioEntryData::default()
534+
},
535+
RadioEntryData {
536+
value: "pixels".into(),
537+
icon: "ViewModePixels".into(),
538+
tooltip: "View Mode: Pixels".into(),
539+
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogComingSoon { issue: Some(320) }.into()),
540+
..RadioEntryData::default()
541+
},
542+
],
543+
})),
544+
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
545+
title: "View Mode".into(),
546+
text: "The contents of this popover menu are coming soon".into(),
547+
})),
548+
WidgetHolder::new(Widget::Separator(Separator {
549+
separator_type: SeparatorType::Section,
550+
direction: SeparatorDirection::Horizontal,
551+
})),
552+
WidgetHolder::new(Widget::NumberInput(NumberInput {
553+
unit: "°".into(),
554+
value: self.movement_handler.tilt / (std::f64::consts::PI / 180.),
555+
increment_factor: 15.,
556+
on_update: WidgetCallback::new(|number_input| {
557+
MovementMessage::SetCanvasRotation {
558+
angle_radians: number_input.value * (std::f64::consts::PI / 180.),
559+
}
560+
.into()
561+
}),
562+
..NumberInput::default()
563+
})),
564+
WidgetHolder::new(Widget::Separator(Separator {
565+
separator_type: SeparatorType::Section,
566+
direction: SeparatorDirection::Horizontal,
567+
})),
568+
WidgetHolder::new(Widget::IconButton(IconButton {
569+
size: 24,
570+
icon: "ZoomIn".into(),
571+
tooltip: "Zoom In".into(),
572+
on_update: WidgetCallback::new(|_| MovementMessage::IncreaseCanvasZoom { center_on_mouse: false }.into()),
573+
..IconButton::default()
574+
})),
575+
WidgetHolder::new(Widget::IconButton(IconButton {
576+
size: 24,
577+
icon: "ZoomOut".into(),
578+
tooltip: "Zoom Out".into(),
579+
on_update: WidgetCallback::new(|_| MovementMessage::DecreaseCanvasZoom { center_on_mouse: false }.into()),
580+
..IconButton::default()
581+
})),
582+
WidgetHolder::new(Widget::IconButton(IconButton {
583+
size: 24,
584+
icon: "ZoomReset".into(),
585+
tooltip: "Zoom to 100%".into(),
586+
on_update: WidgetCallback::new(|_| MovementMessage::SetCanvasZoom { zoom_factor: 1. }.into()),
587+
..IconButton::default()
588+
})),
589+
WidgetHolder::new(Widget::Separator(Separator {
590+
separator_type: SeparatorType::Related,
591+
direction: SeparatorDirection::Horizontal,
592+
})),
593+
WidgetHolder::new(Widget::NumberInput(NumberInput {
594+
unit: "%".into(),
595+
value: self.movement_handler.zoom * 100.,
596+
min: Some(0.000001),
597+
max: Some(1000000.),
598+
on_update: WidgetCallback::new(|number_input| {
599+
MovementMessage::SetCanvasZoom {
600+
zoom_factor: number_input.value / 100.,
601+
}
602+
.into()
603+
}),
604+
increment_behavior: NumberInputIncrementBehavior::Callback,
605+
increment_callback_decrease: WidgetCallback::new(|_| MovementMessage::DecreaseCanvasZoom { center_on_mouse: false }.into()),
606+
increment_callback_increase: WidgetCallback::new(|_| MovementMessage::IncreaseCanvasZoom { center_on_mouse: false }.into()),
607+
..NumberInput::default()
608+
})),
609+
],
610+
}])
611+
}
612+
}
613+
462614
impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for DocumentMessageHandler {
463615
#[remain::check]
464616
fn process_action(&mut self, message: DocumentMessage, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
@@ -674,7 +826,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
674826
responses.extend([LayerChanged { affected_layer_path }.into(), DocumentStructureChanged.into()]);
675827
}
676828
GroupSelectedLayers => {
677-
let mut new_folder_path: Vec<u64> = self.graphene_document.shallowest_common_folder(self.selected_layers()).unwrap_or(&[]).to_vec();
829+
let mut new_folder_path = self.graphene_document.shallowest_common_folder(self.selected_layers()).unwrap_or(&[]).to_vec();
678830

679831
// Required for grouping parent folders with their own children
680832
if !new_folder_path.is_empty() && self.selected_layers_contains(&new_folder_path) {

editor/src/document/movement_message_handler.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
156156
responses.push_back(FrontendMessage::UpdateCanvasZoom { factor: self.zoom }.into());
157157
responses.push_back(ToolMessage::DocumentIsDirty.into());
158158
responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into());
159+
responses.push_back(PortfolioMessage::UpdateDocumentBar.into());
159160
self.create_document_transform(&ipp.viewport_bounds, responses);
160161
}
161162
IncreaseCanvasZoom { center_on_mouse } => {
@@ -245,12 +246,14 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
245246
self.create_document_transform(&ipp.viewport_bounds, responses);
246247
responses.push_back(ToolMessage::DocumentIsDirty.into());
247248
responses.push_back(FrontendMessage::UpdateCanvasRotation { angle_radians: self.snapped_angle() }.into());
249+
responses.push_back(PortfolioMessage::UpdateDocumentBar.into());
248250
}
249251
SetCanvasZoom { zoom_factor } => {
250252
self.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
251253
responses.push_back(FrontendMessage::UpdateCanvasZoom { factor: self.snapped_scale() }.into());
252254
responses.push_back(ToolMessage::DocumentIsDirty.into());
253255
responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into());
256+
responses.push_back(PortfolioMessage::UpdateDocumentBar.into());
254257
self.create_document_transform(&ipp.viewport_bounds, responses);
255258
}
256259
TransformCanvasEnd => {

editor/src/document/portfolio_message.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,6 @@ pub enum PortfolioMessage {
6060
SelectDocument {
6161
document_id: u64,
6262
},
63+
UpdateDocumentBar,
6364
UpdateOpenDocumentsList,
6465
}

editor/src/document/portfolio_message_handler.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use super::DocumentMessageHandler;
33
use crate::consts::{DEFAULT_DOCUMENT_NAME, GRAPHITE_DOCUMENT_VERSION};
44
use crate::frontend::utility_types::FrontendDocumentDetails;
55
use crate::input::InputPreprocessorMessageHandler;
6+
use crate::layout::layout_message::LayoutTarget;
7+
use crate::layout::widgets::PropertyHolder;
68
use crate::message_prelude::*;
79

810
use graphene::Operation as DocumentOperation;
@@ -369,6 +371,11 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
369371
responses.push_back(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() }.into());
370372
}
371373
responses.push_back(ToolMessage::DocumentIsDirty.into());
374+
responses.push_back(PortfolioMessage::UpdateDocumentBar.into());
375+
}
376+
UpdateDocumentBar => {
377+
let active_document = self.active_document();
378+
active_document.register_properties(responses, LayoutTarget::DocumentBar)
372379
}
373380
UpdateOpenDocumentsList => {
374381
// Send the list of document tab names

editor/src/frontend/frontend_message.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon};
22
use crate::document::layer_panel::{LayerPanelEntry, RawBuffer};
3+
use crate::layout::layout_message::LayoutTarget;
4+
use crate::layout::widgets::SubLayout;
35
use crate::message_prelude::*;
46
use crate::misc::HintData;
5-
use crate::viewport_tools::tool_options::ToolOptions;
67
use crate::Color;
78

89
use serde::{Deserialize, Serialize};
@@ -15,6 +16,7 @@ pub enum FrontendMessage {
1516
DisplayConfirmationToCloseAllDocuments,
1617
DisplayConfirmationToCloseDocument { document_id: u64 },
1718
DisplayDialogAboutGraphite,
19+
DisplayDialogComingSoon { issue: Option<i32> },
1820
DisplayDialogError { title: String, description: String },
1921
DisplayDialogPanic { panic_info: String, title: String, description: String },
2022
DisplayDocumentLayerTreeStructure { data_buffer: RawBuffer },
@@ -30,17 +32,19 @@ pub enum FrontendMessage {
3032

3133
// Update prefix: give the frontend a new value or state for it to use
3234
UpdateActiveDocument { document_id: u64 },
33-
UpdateActiveTool { tool_name: String, tool_options: Option<ToolOptions> },
35+
UpdateActiveTool { tool_name: String },
3436
UpdateCanvasRotation { angle_radians: f64 },
3537
UpdateCanvasZoom { factor: f64 },
3638
UpdateDocumentArtboards { svg: String },
3739
UpdateDocumentArtwork { svg: String },
40+
UpdateDocumentBarLayout { layout_target: LayoutTarget, layout: SubLayout },
3841
UpdateDocumentLayer { data: LayerPanelEntry },
3942
UpdateDocumentOverlays { svg: String },
4043
UpdateDocumentRulers { origin: (f64, f64), spacing: f64, interval: f64 },
4144
UpdateDocumentScrollbars { position: (f64, f64), size: (f64, f64), multiplier: (f64, f64) },
4245
UpdateInputHints { hint_data: HintData },
4346
UpdateMouseCursor { cursor: MouseCursorIcon },
4447
UpdateOpenDocumentsList { open_documents: Vec<FrontendDocumentDetails> },
48+
UpdateToolOptionsLayout { layout_target: LayoutTarget, layout: SubLayout },
4549
UpdateWorkingColors { primary: Color, secondary: Color },
4650
}

editor/src/layout/layout_message.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use super::widgets::WidgetLayout;
2+
use crate::message_prelude::*;
3+
4+
use serde::{Deserialize, Serialize};
5+
6+
#[remain::sorted]
7+
#[impl_message(Message, Layout)]
8+
#[derive(PartialEq, Clone, Deserialize, Serialize, Debug)]
9+
pub enum LayoutMessage {
10+
SendLayout { layout: WidgetLayout, layout_target: LayoutTarget },
11+
UpdateLayout { layout_target: LayoutTarget, widget_id: u64, value: serde_json::Value },
12+
}
13+
14+
#[remain::sorted]
15+
#[derive(PartialEq, Clone, Deserialize, Serialize, Debug, Hash, Eq, Copy)]
16+
#[repr(u8)]
17+
pub enum LayoutTarget {
18+
DocumentBar,
19+
ToolOptions,
20+
21+
// KEEP THIS ENUM LAST
22+
// This is a marker that is used to define an array that is used to hold widgets
23+
#[remain::unsorted]
24+
LayoutTargetLength,
25+
}

0 commit comments

Comments
 (0)