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 <keavon@keavon.com>
This commit is contained in:
mfish33
2022-01-30 17:53:37 -08:00
committed by Keavon Chambers
parent 121a68ad3c
commit 96d3ef2650
44 changed files with 1357 additions and 532 deletions

View File

@@ -7,6 +7,10 @@ use crate::consts::{
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,
};
use crate::input::InputPreprocessorMessageHandler;
use crate::layout::widgets::{
IconButton, LayoutRow, NumberInput, NumberInputIncrementBehavior, OptionalInput, PopoverButton, PropertyHolder, RadioEntryData, RadioInput, Separator, SeparatorDirection, SeparatorType, Widget,
WidgetCallback, WidgetHolder, WidgetLayout,
};
use crate::message_prelude::*;
use crate::EditorError;
@@ -459,6 +463,154 @@ impl DocumentMessageHandler {
}
}
impl PropertyHolder for DocumentMessageHandler {
fn properties(&self) -> WidgetLayout {
WidgetLayout::new(vec![LayoutRow::Row {
name: "".into(),
widgets: vec![
WidgetHolder::new(Widget::OptionalInput(OptionalInput {
checked: self.snapping_enabled,
icon: "Snapping".into(),
tooltip: "Snapping".into(),
on_update: WidgetCallback::new(|updated_optional_input| DocumentMessage::SetSnapping { snap: updated_optional_input.checked }.into()),
})),
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
title: "Snapping".into(),
text: "The contents of this popover menu are coming soon".into(),
})),
WidgetHolder::new(Widget::Separator(Separator {
separator_type: SeparatorType::Unrelated,
direction: SeparatorDirection::Horizontal,
})),
WidgetHolder::new(Widget::OptionalInput(OptionalInput {
checked: true,
icon: "Grid".into(),
tooltip: "Grid".into(),
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogComingSoon { issue: Some(318) }.into()),
})),
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
title: "Grid".into(),
text: "The contents of this popover menu are coming soon".into(),
})),
WidgetHolder::new(Widget::Separator(Separator {
separator_type: SeparatorType::Unrelated,
direction: SeparatorDirection::Horizontal,
})),
WidgetHolder::new(Widget::OptionalInput(OptionalInput {
checked: self.overlays_visible,
icon: "Overlays".into(),
tooltip: "Overlays".into(),
on_update: WidgetCallback::new(|updated_optional_input| {
DocumentMessage::SetOverlaysVisibility {
visible: updated_optional_input.checked,
}
.into()
}),
})),
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
title: "Overlays".into(),
text: "The contents of this popover menu are coming soon".into(),
})),
WidgetHolder::new(Widget::Separator(Separator {
separator_type: SeparatorType::Unrelated,
direction: SeparatorDirection::Horizontal,
})),
WidgetHolder::new(Widget::RadioInput(RadioInput {
selected_index: if self.view_mode == ViewMode::Normal { 0 } else { 1 },
entries: vec![
RadioEntryData {
value: "normal".into(),
icon: "ViewModeNormal".into(),
tooltip: "View Mode: Normal".into(),
on_update: WidgetCallback::new(|_| DocumentMessage::SetViewMode { view_mode: ViewMode::Normal }.into()),
..RadioEntryData::default()
},
RadioEntryData {
value: "outline".into(),
icon: "ViewModeOutline".into(),
tooltip: "View Mode: Outline".into(),
on_update: WidgetCallback::new(|_| DocumentMessage::SetViewMode { view_mode: ViewMode::Outline }.into()),
..RadioEntryData::default()
},
RadioEntryData {
value: "pixels".into(),
icon: "ViewModePixels".into(),
tooltip: "View Mode: Pixels".into(),
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogComingSoon { issue: Some(320) }.into()),
..RadioEntryData::default()
},
],
})),
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
title: "View Mode".into(),
text: "The contents of this popover menu are coming soon".into(),
})),
WidgetHolder::new(Widget::Separator(Separator {
separator_type: SeparatorType::Section,
direction: SeparatorDirection::Horizontal,
})),
WidgetHolder::new(Widget::NumberInput(NumberInput {
unit: "°".into(),
value: self.movement_handler.tilt / (std::f64::consts::PI / 180.),
increment_factor: 15.,
on_update: WidgetCallback::new(|number_input| {
MovementMessage::SetCanvasRotation {
angle_radians: number_input.value * (std::f64::consts::PI / 180.),
}
.into()
}),
..NumberInput::default()
})),
WidgetHolder::new(Widget::Separator(Separator {
separator_type: SeparatorType::Section,
direction: SeparatorDirection::Horizontal,
})),
WidgetHolder::new(Widget::IconButton(IconButton {
size: 24,
icon: "ZoomIn".into(),
tooltip: "Zoom In".into(),
on_update: WidgetCallback::new(|_| MovementMessage::IncreaseCanvasZoom { center_on_mouse: false }.into()),
..IconButton::default()
})),
WidgetHolder::new(Widget::IconButton(IconButton {
size: 24,
icon: "ZoomOut".into(),
tooltip: "Zoom Out".into(),
on_update: WidgetCallback::new(|_| MovementMessage::DecreaseCanvasZoom { center_on_mouse: false }.into()),
..IconButton::default()
})),
WidgetHolder::new(Widget::IconButton(IconButton {
size: 24,
icon: "ZoomReset".into(),
tooltip: "Zoom to 100%".into(),
on_update: WidgetCallback::new(|_| MovementMessage::SetCanvasZoom { zoom_factor: 1. }.into()),
..IconButton::default()
})),
WidgetHolder::new(Widget::Separator(Separator {
separator_type: SeparatorType::Related,
direction: SeparatorDirection::Horizontal,
})),
WidgetHolder::new(Widget::NumberInput(NumberInput {
unit: "%".into(),
value: self.movement_handler.zoom * 100.,
min: Some(0.000001),
max: Some(1000000.),
on_update: WidgetCallback::new(|number_input| {
MovementMessage::SetCanvasZoom {
zoom_factor: number_input.value / 100.,
}
.into()
}),
increment_behavior: NumberInputIncrementBehavior::Callback,
increment_callback_decrease: WidgetCallback::new(|_| MovementMessage::DecreaseCanvasZoom { center_on_mouse: false }.into()),
increment_callback_increase: WidgetCallback::new(|_| MovementMessage::IncreaseCanvasZoom { center_on_mouse: false }.into()),
..NumberInput::default()
})),
],
}])
}
}
impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for DocumentMessageHandler {
#[remain::check]
fn process_action(&mut self, message: DocumentMessage, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
@@ -674,7 +826,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
responses.extend([LayerChanged { affected_layer_path }.into(), DocumentStructureChanged.into()]);
}
GroupSelectedLayers => {
let mut new_folder_path: Vec<u64> = self.graphene_document.shallowest_common_folder(self.selected_layers()).unwrap_or(&[]).to_vec();
let mut new_folder_path = self.graphene_document.shallowest_common_folder(self.selected_layers()).unwrap_or(&[]).to_vec();
// Required for grouping parent folders with their own children
if !new_folder_path.is_empty() && self.selected_layers_contains(&new_folder_path) {