Reorganize the menu bar and add additional commands to it
This commit is contained in:
parent
fd81d043a2
commit
ddb0c8c249
|
|
@ -41,6 +41,7 @@ impl PreferencesDialogMessageHandler {
|
||||||
|
|
||||||
let zoom_with_scroll_tooltip = "Use the scroll wheel for zooming instead of vertically panning (not recommended for trackpads)";
|
let zoom_with_scroll_tooltip = "Use the scroll wheel for zooming instead of vertically panning (not recommended for trackpads)";
|
||||||
let zoom_with_scroll = vec![
|
let zoom_with_scroll = vec![
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
CheckboxInput::new(preferences.zoom_with_scroll)
|
CheckboxInput::new(preferences.zoom_with_scroll)
|
||||||
.tooltip(zoom_with_scroll_tooltip)
|
.tooltip(zoom_with_scroll_tooltip)
|
||||||
|
|
@ -60,7 +61,11 @@ impl PreferencesDialogMessageHandler {
|
||||||
|
|
||||||
let editing_header = vec![TextLabel::new("Editing").italic(true).widget_holder()];
|
let editing_header = vec![TextLabel::new("Editing").italic(true).widget_holder()];
|
||||||
|
|
||||||
let selection_label = vec![Separator::new(SeparatorType::Unrelated).widget_holder(), TextLabel::new("Selection").widget_holder()];
|
let selection_label = vec![
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
TextLabel::new("Selection").widget_holder(),
|
||||||
|
];
|
||||||
|
|
||||||
let selection_mode = RadioInput::new(vec![
|
let selection_mode = RadioInput::new(vec![
|
||||||
RadioEntryData::new(SelectionMode::Touched.to_string())
|
RadioEntryData::new(SelectionMode::Touched.to_string())
|
||||||
|
|
@ -93,6 +98,11 @@ impl PreferencesDialogMessageHandler {
|
||||||
])
|
])
|
||||||
.selected_index(Some(preferences.selection_mode as u32))
|
.selected_index(Some(preferences.selection_mode as u32))
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
|
let selection_mode = vec![
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
selection_mode,
|
||||||
|
];
|
||||||
|
|
||||||
// ============
|
// ============
|
||||||
// EXPERIMENTAL
|
// EXPERIMENTAL
|
||||||
|
|
@ -102,6 +112,7 @@ impl PreferencesDialogMessageHandler {
|
||||||
|
|
||||||
let node_graph_section_tooltip = "Appearance of the wires running between node connections in the graph";
|
let node_graph_section_tooltip = "Appearance of the wires running between node connections in the graph";
|
||||||
let node_graph_wires_label = vec![
|
let node_graph_wires_label = vec![
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
TextLabel::new("Node Graph Wires").tooltip(node_graph_section_tooltip).widget_holder(),
|
TextLabel::new("Node Graph Wires").tooltip(node_graph_section_tooltip).widget_holder(),
|
||||||
];
|
];
|
||||||
|
|
@ -117,9 +128,15 @@ impl PreferencesDialogMessageHandler {
|
||||||
])
|
])
|
||||||
.selected_index(Some(preferences.graph_wire_style as u32))
|
.selected_index(Some(preferences.graph_wire_style as u32))
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
|
let graph_wire_style = vec![
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
graph_wire_style,
|
||||||
|
];
|
||||||
|
|
||||||
let vello_tooltip = "Use the experimental Vello renderer (your browser must support WebGPU)";
|
let vello_tooltip = "Use the experimental Vello renderer (your browser must support WebGPU)";
|
||||||
let use_vello = vec![
|
let use_vello = vec![
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
CheckboxInput::new(preferences.use_vello && preferences.supports_wgpu())
|
CheckboxInput::new(preferences.use_vello && preferences.supports_wgpu())
|
||||||
.tooltip(vello_tooltip)
|
.tooltip(vello_tooltip)
|
||||||
|
|
@ -135,6 +152,7 @@ impl PreferencesDialogMessageHandler {
|
||||||
|
|
||||||
let vector_mesh_tooltip = "Allow tools to produce vector meshes, where more than two segments can connect to an anchor point.\n\nCurrently this does not properly handle line joins and fills.";
|
let vector_mesh_tooltip = "Allow tools to produce vector meshes, where more than two segments can connect to an anchor point.\n\nCurrently this does not properly handle line joins and fills.";
|
||||||
let vector_meshes = vec![
|
let vector_meshes = vec![
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
CheckboxInput::new(preferences.vector_meshes)
|
CheckboxInput::new(preferences.vector_meshes)
|
||||||
.tooltip(vector_mesh_tooltip)
|
.tooltip(vector_mesh_tooltip)
|
||||||
|
|
@ -169,14 +187,10 @@ impl PreferencesDialogMessageHandler {
|
||||||
LayoutGroup::Row { widgets: zoom_with_scroll },
|
LayoutGroup::Row { widgets: zoom_with_scroll },
|
||||||
LayoutGroup::Row { widgets: editing_header },
|
LayoutGroup::Row { widgets: editing_header },
|
||||||
LayoutGroup::Row { widgets: selection_label },
|
LayoutGroup::Row { widgets: selection_label },
|
||||||
LayoutGroup::Row {
|
LayoutGroup::Row { widgets: selection_mode },
|
||||||
widgets: vec![Separator::new(SeparatorType::Unrelated).widget_holder(), selection_mode],
|
|
||||||
},
|
|
||||||
LayoutGroup::Row { widgets: experimental_header },
|
LayoutGroup::Row { widgets: experimental_header },
|
||||||
LayoutGroup::Row { widgets: node_graph_wires_label },
|
LayoutGroup::Row { widgets: node_graph_wires_label },
|
||||||
LayoutGroup::Row {
|
LayoutGroup::Row { widgets: graph_wire_style },
|
||||||
widgets: vec![Separator::new(SeparatorType::Unrelated).widget_holder(), graph_wire_style],
|
|
||||||
},
|
|
||||||
LayoutGroup::Row { widgets: use_vello },
|
LayoutGroup::Row { widgets: use_vello },
|
||||||
LayoutGroup::Row { widgets: vector_meshes },
|
LayoutGroup::Row { widgets: vector_meshes },
|
||||||
// LayoutGroup::Row { widgets: imaginate_server_hostname },
|
// LayoutGroup::Row { widgets: imaginate_server_hostname },
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@ pub enum DocumentMessage {
|
||||||
FlipSelectedLayers {
|
FlipSelectedLayers {
|
||||||
flip_axis: FlipAxis,
|
flip_axis: FlipAxis,
|
||||||
},
|
},
|
||||||
|
RotateSelectedLayers {
|
||||||
|
degrees: f64,
|
||||||
|
},
|
||||||
GraphViewOverlay {
|
GraphViewOverlay {
|
||||||
open: bool,
|
open: bool,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -454,6 +454,30 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DocumentMessage::RotateSelectedLayers { degrees } => {
|
||||||
|
// Get the bounding box of selected layers in viewport space
|
||||||
|
if let Some([min, max]) = self.selected_visible_and_unlock_layers_bounding_box_viewport() {
|
||||||
|
// Calculate the center of the bounding box to use as rotation pivot
|
||||||
|
let center = (max + min) / 2.;
|
||||||
|
// Transform that moves pivot point to origin
|
||||||
|
let bbox_trans = DAffine2::from_translation(-center);
|
||||||
|
|
||||||
|
let mut added_transaction = false;
|
||||||
|
for layer in self.network_interface.selected_nodes(&[]).unwrap().selected_unlocked_layers(&self.network_interface) {
|
||||||
|
if !added_transaction {
|
||||||
|
responses.add(DocumentMessage::AddTransaction);
|
||||||
|
added_transaction = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
responses.add(GraphOperationMessage::TransformChange {
|
||||||
|
layer,
|
||||||
|
transform: DAffine2::from_angle(degrees.to_radians()),
|
||||||
|
transform_in: TransformIn::Scope { scope: bbox_trans },
|
||||||
|
skip_rerender: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
DocumentMessage::GraphViewOverlay { open } => {
|
DocumentMessage::GraphViewOverlay { open } => {
|
||||||
self.graph_view_overlay_open = open;
|
self.graph_view_overlay_open = open;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
use graphene_std::vector::misc::BooleanOperation;
|
||||||
|
|
||||||
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
||||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
pub struct MenuBarMessageData {
|
pub struct MenuBarMessageData {
|
||||||
|
|
@ -199,8 +201,26 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
vec![
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Duplicate".into(),
|
||||||
|
icon: Some("Copy".into()),
|
||||||
|
shortcut: action_keys!(DocumentMessageDiscriminant::DuplicateSelectedLayers),
|
||||||
|
action: MenuBarEntry::create_action(|_| DocumentMessage::DuplicateSelectedLayers.into()),
|
||||||
|
disabled: no_active_document || !has_selected_nodes,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Delete".into(),
|
||||||
|
icon: Some("Trash".into()),
|
||||||
|
shortcut: action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers),
|
||||||
|
action: MenuBarEntry::create_action(|_| DocumentMessage::DeleteSelectedLayers.into()),
|
||||||
|
disabled: no_active_document || !has_selected_nodes,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
vec![MenuBarEntry {
|
vec![MenuBarEntry {
|
||||||
label: "Remove Artboards".into(),
|
label: "Convert to Infinite Canvas".into(),
|
||||||
icon: Some("Artboard".into()),
|
icon: Some("Artboard".into()),
|
||||||
action: MenuBarEntry::create_action(|_| DocumentMessage::RemoveArtboards.into()),
|
action: MenuBarEntry::create_action(|_| DocumentMessage::RemoveArtboards.into()),
|
||||||
disabled: no_active_document,
|
disabled: no_active_document,
|
||||||
|
|
@ -212,17 +232,17 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||||
"Layer".into(),
|
"Layer".into(),
|
||||||
no_active_document,
|
no_active_document,
|
||||||
MenuBarEntryChildren(vec![
|
MenuBarEntryChildren(vec![
|
||||||
|
vec![MenuBarEntry {
|
||||||
|
label: "New".into(),
|
||||||
|
icon: Some("NewLayer".into()),
|
||||||
|
shortcut: action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder),
|
||||||
|
action: MenuBarEntry::create_action(|_| DocumentMessage::CreateEmptyFolder.into()),
|
||||||
|
disabled: no_active_document,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
}],
|
||||||
vec![
|
vec![
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
label: "New Layer".into(),
|
label: "Group".into(),
|
||||||
icon: Some("NewLayer".into()),
|
|
||||||
shortcut: action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder),
|
|
||||||
action: MenuBarEntry::create_action(|_| DocumentMessage::CreateEmptyFolder.into()),
|
|
||||||
disabled: no_active_document,
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
},
|
|
||||||
MenuBarEntry {
|
|
||||||
label: "Group Selected".into(),
|
|
||||||
icon: Some("Folder".into()),
|
icon: Some("Folder".into()),
|
||||||
shortcut: action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers),
|
shortcut: action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers),
|
||||||
action: MenuBarEntry::create_action(|_| {
|
action: MenuBarEntry::create_action(|_| {
|
||||||
|
|
@ -235,14 +255,216 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
label: "Delete Selected".into(),
|
label: "Ungroup".into(),
|
||||||
icon: Some("Trash".into()),
|
icon: Some("FolderOpen".into()),
|
||||||
shortcut: action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers),
|
shortcut: action_keys!(DocumentMessageDiscriminant::UngroupSelectedLayers),
|
||||||
action: MenuBarEntry::create_action(|_| DocumentMessage::DeleteSelectedLayers.into()),
|
action: MenuBarEntry::create_action(|_| DocumentMessage::UngroupSelectedLayers.into()),
|
||||||
disabled: no_active_document || !has_selected_nodes,
|
disabled: no_active_document || !has_selected_layers,
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
vec![
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Hide/Show".into(),
|
||||||
|
icon: Some("EyeHide".into()),
|
||||||
|
shortcut: action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility),
|
||||||
|
action: MenuBarEntry::create_action(|_| DocumentMessage::ToggleSelectedVisibility.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Lock/Unlock".into(),
|
||||||
|
icon: Some("PadlockLocked".into()),
|
||||||
|
shortcut: action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked),
|
||||||
|
action: MenuBarEntry::create_action(|_| DocumentMessage::ToggleSelectedLocked.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
vec![
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Grab".into(),
|
||||||
|
icon: Some("TransformationGrab".into()),
|
||||||
|
shortcut: action_keys!(TransformLayerMessageDiscriminant::BeginGrab),
|
||||||
|
action: MenuBarEntry::create_action(|_| TransformLayerMessage::BeginGrab.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Rotate".into(),
|
||||||
|
icon: Some("TransformationRotate".into()),
|
||||||
|
shortcut: action_keys!(TransformLayerMessageDiscriminant::BeginRotate),
|
||||||
|
action: MenuBarEntry::create_action(|_| TransformLayerMessage::BeginRotate.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Scale".into(),
|
||||||
|
icon: Some("TransformationScale".into()),
|
||||||
|
shortcut: action_keys!(TransformLayerMessageDiscriminant::BeginScale),
|
||||||
|
action: MenuBarEntry::create_action(|_| TransformLayerMessage::BeginScale.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
vec![
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Arrange".into(),
|
||||||
|
icon: Some("StackHollow".into()),
|
||||||
|
action: MenuBarEntry::no_action(),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
children: MenuBarEntryChildren(vec![
|
||||||
|
vec![
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Raise To Front".into(),
|
||||||
|
icon: Some("Stack".into()),
|
||||||
|
shortcut: action_keys!(DocumentMessageDiscriminant::SelectedLayersRaiseToFront),
|
||||||
|
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersRaiseToFront.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Raise".into(),
|
||||||
|
icon: Some("StackRaise".into()),
|
||||||
|
shortcut: action_keys!(DocumentMessageDiscriminant::SelectedLayersRaise),
|
||||||
|
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersRaise.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Lower".into(),
|
||||||
|
icon: Some("StackLower".into()),
|
||||||
|
shortcut: action_keys!(DocumentMessageDiscriminant::SelectedLayersLower),
|
||||||
|
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersLower.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Lower to Back".into(),
|
||||||
|
icon: Some("StackBottom".into()),
|
||||||
|
shortcut: action_keys!(DocumentMessageDiscriminant::SelectedLayersLowerToBack),
|
||||||
|
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersLowerToBack.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
vec![MenuBarEntry {
|
||||||
|
label: "Reverse".into(),
|
||||||
|
icon: Some("StackReverse".into()),
|
||||||
|
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersReverse.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
}],
|
||||||
|
]),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Align".into(),
|
||||||
|
icon: Some("AlignVerticalCenter".into()),
|
||||||
|
action: MenuBarEntry::no_action(),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
children: MenuBarEntryChildren({
|
||||||
|
let choices = [
|
||||||
|
[
|
||||||
|
(AlignAxis::X, AlignAggregate::Min, "AlignLeft", "Align Left"),
|
||||||
|
(AlignAxis::X, AlignAggregate::Center, "AlignHorizontalCenter", "Align Horizontal Center"),
|
||||||
|
(AlignAxis::X, AlignAggregate::Max, "AlignRight", "Align Right"),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
(AlignAxis::Y, AlignAggregate::Min, "AlignTop", "Align Top"),
|
||||||
|
(AlignAxis::Y, AlignAggregate::Center, "AlignVerticalCenter", "Align Vertical Center"),
|
||||||
|
(AlignAxis::Y, AlignAggregate::Max, "AlignBottom", "Align Bottom"),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
choices
|
||||||
|
.into_iter()
|
||||||
|
.map(|group| {
|
||||||
|
group
|
||||||
|
.into_iter()
|
||||||
|
.map(|(axis, aggregate, icon, name)| MenuBarEntry {
|
||||||
|
label: name.into(),
|
||||||
|
icon: Some(icon.into()),
|
||||||
|
action: MenuBarEntry::create_action(move |_| DocumentMessage::AlignSelectedLayers { axis, aggregate }.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Flip".into(),
|
||||||
|
icon: Some("FlipVertical".into()),
|
||||||
|
action: MenuBarEntry::no_action(),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
children: MenuBarEntryChildren(vec![{
|
||||||
|
[(FlipAxis::X, "FlipHorizontal", "Horizontal"), (FlipAxis::Y, "FlipVertical", "Vertical")]
|
||||||
|
.into_iter()
|
||||||
|
.map(|(flip_axis, icon, name)| MenuBarEntry {
|
||||||
|
label: name.into(),
|
||||||
|
icon: Some(icon.into()),
|
||||||
|
action: MenuBarEntry::create_action(move |_| DocumentMessage::FlipSelectedLayers { flip_axis }.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}]),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Turn".into(),
|
||||||
|
icon: Some("TurnPositive90".into()),
|
||||||
|
action: MenuBarEntry::no_action(),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
children: MenuBarEntryChildren(vec![{
|
||||||
|
[(-90., "TurnNegative90", "Turn -90°"), (90., "TurnPositive90", "Turn 90°")]
|
||||||
|
.into_iter()
|
||||||
|
.map(|(degrees, icon, name)| MenuBarEntry {
|
||||||
|
label: name.into(),
|
||||||
|
icon: Some(icon.into()),
|
||||||
|
action: MenuBarEntry::create_action(move |_| DocumentMessage::RotateSelectedLayers { degrees }.into()),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}]),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Boolean".into(),
|
||||||
|
icon: Some("BooleanSubtractFront".into()),
|
||||||
|
action: MenuBarEntry::no_action(),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
children: MenuBarEntryChildren(vec![{
|
||||||
|
let operations = BooleanOperation::list();
|
||||||
|
let icons = BooleanOperation::icons();
|
||||||
|
operations
|
||||||
|
.into_iter()
|
||||||
|
.zip(icons)
|
||||||
|
.map(move |(operation, icon)| MenuBarEntry {
|
||||||
|
label: operation.to_string(),
|
||||||
|
icon: Some(icon.into()),
|
||||||
|
action: MenuBarEntry::create_action(move |_| {
|
||||||
|
let group_folder_type = GroupFolderType::BooleanOperation(operation);
|
||||||
|
DocumentMessage::GroupSelectedLayers { group_folder_type }.into()
|
||||||
|
}),
|
||||||
|
disabled: no_active_document || !has_selected_layers,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}]),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
MenuBarEntry::new_root(
|
||||||
|
"Select".into(),
|
||||||
|
no_active_document,
|
||||||
|
MenuBarEntryChildren(vec![
|
||||||
vec![
|
vec![
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
label: "Select All".into(),
|
label: "Select All".into(),
|
||||||
|
|
@ -262,11 +484,14 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||||
},
|
},
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
label: "Select Parent".into(),
|
label: "Select Parent".into(),
|
||||||
|
icon: Some("SelectParent".into()),
|
||||||
shortcut: action_keys!(DocumentMessageDiscriminant::SelectParentLayer),
|
shortcut: action_keys!(DocumentMessageDiscriminant::SelectParentLayer),
|
||||||
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectParentLayer.into()),
|
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectParentLayer.into()),
|
||||||
disabled: no_active_document || !has_selected_nodes,
|
disabled: no_active_document || !has_selected_nodes,
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
vec![
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
label: "Previous Selection".into(),
|
label: "Previous Selection".into(),
|
||||||
icon: Some("HistoryUndo".into()),
|
icon: Some("HistoryUndo".into()),
|
||||||
|
|
@ -284,82 +509,6 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
vec![
|
|
||||||
MenuBarEntry {
|
|
||||||
label: "Grab Selected".into(),
|
|
||||||
icon: Some("TransformationGrab".into()),
|
|
||||||
shortcut: action_keys!(TransformLayerMessageDiscriminant::BeginGrab),
|
|
||||||
action: MenuBarEntry::create_action(|_| TransformLayerMessage::BeginGrab.into()),
|
|
||||||
disabled: no_active_document || !has_selected_layers,
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
},
|
|
||||||
MenuBarEntry {
|
|
||||||
label: "Rotate Selected".into(),
|
|
||||||
icon: Some("TransformationRotate".into()),
|
|
||||||
shortcut: action_keys!(TransformLayerMessageDiscriminant::BeginRotate),
|
|
||||||
action: MenuBarEntry::create_action(|_| TransformLayerMessage::BeginRotate.into()),
|
|
||||||
disabled: no_active_document || !has_selected_layers,
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
},
|
|
||||||
MenuBarEntry {
|
|
||||||
label: "Scale Selected".into(),
|
|
||||||
icon: Some("TransformationScale".into()),
|
|
||||||
shortcut: action_keys!(TransformLayerMessageDiscriminant::BeginScale),
|
|
||||||
action: MenuBarEntry::create_action(|_| TransformLayerMessage::BeginScale.into()),
|
|
||||||
disabled: no_active_document || !has_selected_layers,
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
},
|
|
||||||
],
|
|
||||||
vec![MenuBarEntry {
|
|
||||||
label: "Order".into(),
|
|
||||||
icon: Some("StackHollow".into()),
|
|
||||||
action: MenuBarEntry::no_action(),
|
|
||||||
disabled: no_active_document || !has_selected_layers,
|
|
||||||
children: MenuBarEntryChildren(vec![
|
|
||||||
vec![
|
|
||||||
MenuBarEntry {
|
|
||||||
label: "Raise To Front".into(),
|
|
||||||
icon: Some("Stack".into()),
|
|
||||||
shortcut: action_keys!(DocumentMessageDiscriminant::SelectedLayersRaiseToFront),
|
|
||||||
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersRaiseToFront.into()),
|
|
||||||
disabled: no_active_document || !has_selected_layers,
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
},
|
|
||||||
MenuBarEntry {
|
|
||||||
label: "Raise".into(),
|
|
||||||
icon: Some("StackRaise".into()),
|
|
||||||
shortcut: action_keys!(DocumentMessageDiscriminant::SelectedLayersRaise),
|
|
||||||
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersRaise.into()),
|
|
||||||
disabled: no_active_document || !has_selected_layers,
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
},
|
|
||||||
MenuBarEntry {
|
|
||||||
label: "Lower".into(),
|
|
||||||
icon: Some("StackLower".into()),
|
|
||||||
shortcut: action_keys!(DocumentMessageDiscriminant::SelectedLayersLower),
|
|
||||||
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersLower.into()),
|
|
||||||
disabled: no_active_document || !has_selected_layers,
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
},
|
|
||||||
MenuBarEntry {
|
|
||||||
label: "Lower to Back".into(),
|
|
||||||
icon: Some("StackBottom".into()),
|
|
||||||
shortcut: action_keys!(DocumentMessageDiscriminant::SelectedLayersLowerToBack),
|
|
||||||
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersLowerToBack.into()),
|
|
||||||
disabled: no_active_document || !has_selected_layers,
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
},
|
|
||||||
],
|
|
||||||
vec![MenuBarEntry {
|
|
||||||
label: "Reverse".into(),
|
|
||||||
icon: Some("StackReverse".into()),
|
|
||||||
action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersReverse.into()),
|
|
||||||
disabled: no_active_document || !has_selected_layers,
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
}],
|
|
||||||
]),
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
}],
|
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
MenuBarEntry::new_root(
|
MenuBarEntry::new_root(
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,18 @@ impl SelectTool {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn turn_widgets(&self, disabled: bool) -> impl Iterator<Item = WidgetHolder> {
|
||||||
|
[(-90., "TurnNegative90", "Turn -90°"), (90., "TurnPositive90", "Turn 90°")]
|
||||||
|
.into_iter()
|
||||||
|
.map(move |(degrees, icon, name)| {
|
||||||
|
IconButton::new(icon, 24)
|
||||||
|
.tooltip(name)
|
||||||
|
.on_update(move |_| DocumentMessage::RotateSelectedLayers { degrees }.into())
|
||||||
|
.disabled(disabled)
|
||||||
|
.widget_holder()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn boolean_widgets(&self, selected_count: usize) -> impl Iterator<Item = WidgetHolder> {
|
fn boolean_widgets(&self, selected_count: usize) -> impl Iterator<Item = WidgetHolder> {
|
||||||
let operations = BooleanOperation::list();
|
let operations = BooleanOperation::list();
|
||||||
let icons = BooleanOperation::icons();
|
let icons = BooleanOperation::icons();
|
||||||
|
|
@ -218,6 +230,10 @@ impl LayoutHolder for SelectTool {
|
||||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
widgets.extend(self.flip_widgets(disabled));
|
widgets.extend(self.flip_widgets(disabled));
|
||||||
|
|
||||||
|
// Turn
|
||||||
|
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
|
widgets.extend(self.turn_widgets(disabled));
|
||||||
|
|
||||||
// Boolean
|
// Boolean
|
||||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
widgets.extend(self.boolean_widgets(self.tool_data.selected_layers_count));
|
widgets.extend(self.boolean_widgets(self.tool_data.selected_layers_count));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<path d="M15,7v-3c0-.55-.45-1-1-1H6l-1-1H1c-.55,0-1,.45-1,1v10c0,.55.45,1,1,1h12.28c.45-.01.84-.31.96-.74l1.76-6.26h-1zM14,7h-6l-1,1H3l-2,4V3h3.59l1,1h8.41v3z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 232 B |
|
|
@ -0,0 +1,13 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<polygon points="4,14 4,13 3,13 3,15 5,15 5,14" />
|
||||||
|
<polygon points="13,13 13,14 12,14 12,15 14,15 14,13" />
|
||||||
|
<polygon points="5,1 3,1 3,3 4,3 4,2 5,2" />
|
||||||
|
<polygon points="12,1 12,2 13,2 13,3 14,3 14,1" />
|
||||||
|
<rect x="3" y="5" width="1" height="2" />
|
||||||
|
<rect x="13" y="5" width="1" height="2" />
|
||||||
|
<rect x="13" y="9" width="1" height="2" />
|
||||||
|
<rect x="3" y="9" width="1" height="2" />
|
||||||
|
<rect x="7" y="14" width="3" height="1" />
|
||||||
|
<rect x="7" y="1" width="3" height="1" />
|
||||||
|
<polygon points="8.5,4 6,7 8,7 8,11 9,11 9,7 11,7" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 592 B |
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<path d="M0,11v5s12,0,12,0v-4h4s0-12,0-12h-5s0,11,0,11H0zM12,11V1h3v10h-3z" />
|
||||||
|
<path d="M9,1.7V.3c-3.25,0-5.92,2.52-6.17,5.7h-1.83l2.5,3,2.5-3h-1.75c.25-2.41,2.27-4.3,4.75-4.3z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 251 B |
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<path d="M5,11V0H0v12h4v4h12v-5H5zM1,11V1h3v10H1z" />
|
||||||
|
<path d="M7,1.7V.3c3.25,0,5.92,2.52,6.17,5.7h1.83s-2.5,3-2.5,3l-2.5-3h1.75c-.25-2.41-2.27-4.3-4.75-4.3z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 232 B |
|
|
@ -127,6 +127,7 @@ import FileImport from "@graphite-frontend/assets/icon-16px-solid/file-import.sv
|
||||||
import File from "@graphite-frontend/assets/icon-16px-solid/file.svg";
|
import File from "@graphite-frontend/assets/icon-16px-solid/file.svg";
|
||||||
import FlipHorizontal from "@graphite-frontend/assets/icon-16px-solid/flip-horizontal.svg";
|
import FlipHorizontal from "@graphite-frontend/assets/icon-16px-solid/flip-horizontal.svg";
|
||||||
import FlipVertical from "@graphite-frontend/assets/icon-16px-solid/flip-vertical.svg";
|
import FlipVertical from "@graphite-frontend/assets/icon-16px-solid/flip-vertical.svg";
|
||||||
|
import FolderOpen from "@graphite-frontend/assets/icon-16px-solid/folder-open.svg";
|
||||||
import Folder from "@graphite-frontend/assets/icon-16px-solid/folder.svg";
|
import Folder from "@graphite-frontend/assets/icon-16px-solid/folder.svg";
|
||||||
import FrameAll from "@graphite-frontend/assets/icon-16px-solid/frame-all.svg";
|
import FrameAll from "@graphite-frontend/assets/icon-16px-solid/frame-all.svg";
|
||||||
import FrameSelected from "@graphite-frontend/assets/icon-16px-solid/frame-selected.svg";
|
import FrameSelected from "@graphite-frontend/assets/icon-16px-solid/frame-selected.svg";
|
||||||
|
|
@ -171,6 +172,7 @@ import ReverseRadialGradientToRight from "@graphite-frontend/assets/icon-16px-so
|
||||||
import Reverse from "@graphite-frontend/assets/icon-16px-solid/reverse.svg";
|
import Reverse from "@graphite-frontend/assets/icon-16px-solid/reverse.svg";
|
||||||
import Save from "@graphite-frontend/assets/icon-16px-solid/save.svg";
|
import Save from "@graphite-frontend/assets/icon-16px-solid/save.svg";
|
||||||
import SelectAll from "@graphite-frontend/assets/icon-16px-solid/select-all.svg";
|
import SelectAll from "@graphite-frontend/assets/icon-16px-solid/select-all.svg";
|
||||||
|
import SelectParent from "@graphite-frontend/assets/icon-16px-solid/select-parent.svg";
|
||||||
import Settings from "@graphite-frontend/assets/icon-16px-solid/settings.svg";
|
import Settings from "@graphite-frontend/assets/icon-16px-solid/settings.svg";
|
||||||
import SmallDot from "@graphite-frontend/assets/icon-16px-solid/small-dot.svg";
|
import SmallDot from "@graphite-frontend/assets/icon-16px-solid/small-dot.svg";
|
||||||
import StackBottom from "@graphite-frontend/assets/icon-16px-solid/stack-bottom.svg";
|
import StackBottom from "@graphite-frontend/assets/icon-16px-solid/stack-bottom.svg";
|
||||||
|
|
@ -185,6 +187,8 @@ import TransformationGrab from "@graphite-frontend/assets/icon-16px-solid/transf
|
||||||
import TransformationRotate from "@graphite-frontend/assets/icon-16px-solid/transformation-rotate.svg";
|
import TransformationRotate from "@graphite-frontend/assets/icon-16px-solid/transformation-rotate.svg";
|
||||||
import TransformationScale from "@graphite-frontend/assets/icon-16px-solid/transformation-scale.svg";
|
import TransformationScale from "@graphite-frontend/assets/icon-16px-solid/transformation-scale.svg";
|
||||||
import Trash from "@graphite-frontend/assets/icon-16px-solid/trash.svg";
|
import Trash from "@graphite-frontend/assets/icon-16px-solid/trash.svg";
|
||||||
|
import TurnNegative90 from "@graphite-frontend/assets/icon-16px-solid/turn-negative-90.svg";
|
||||||
|
import TurnPositive90 from "@graphite-frontend/assets/icon-16px-solid/turn-positive-90.svg";
|
||||||
import UserManual from "@graphite-frontend/assets/icon-16px-solid/user-manual.svg";
|
import UserManual from "@graphite-frontend/assets/icon-16px-solid/user-manual.svg";
|
||||||
import ViewModeNormal from "@graphite-frontend/assets/icon-16px-solid/view-mode-normal.svg";
|
import ViewModeNormal from "@graphite-frontend/assets/icon-16px-solid/view-mode-normal.svg";
|
||||||
import ViewModeOutline from "@graphite-frontend/assets/icon-16px-solid/view-mode-outline.svg";
|
import ViewModeOutline from "@graphite-frontend/assets/icon-16px-solid/view-mode-outline.svg";
|
||||||
|
|
@ -240,6 +244,7 @@ const SOLID_16PX = {
|
||||||
FlipHorizontal: { svg: FlipHorizontal, size: 16 },
|
FlipHorizontal: { svg: FlipHorizontal, size: 16 },
|
||||||
FlipVertical: { svg: FlipVertical, size: 16 },
|
FlipVertical: { svg: FlipVertical, size: 16 },
|
||||||
Folder: { svg: Folder, size: 16 },
|
Folder: { svg: Folder, size: 16 },
|
||||||
|
FolderOpen: { svg: FolderOpen, size: 16 },
|
||||||
FrameAll: { svg: FrameAll, size: 16 },
|
FrameAll: { svg: FrameAll, size: 16 },
|
||||||
FrameSelected: { svg: FrameSelected, size: 16 },
|
FrameSelected: { svg: FrameSelected, size: 16 },
|
||||||
GraphiteLogo: { svg: GraphiteLogo, size: 16 },
|
GraphiteLogo: { svg: GraphiteLogo, size: 16 },
|
||||||
|
|
@ -283,6 +288,7 @@ const SOLID_16PX = {
|
||||||
ReverseRadialGradientToRight: { svg: ReverseRadialGradientToRight, size: 16 },
|
ReverseRadialGradientToRight: { svg: ReverseRadialGradientToRight, size: 16 },
|
||||||
Save: { svg: Save, size: 16 },
|
Save: { svg: Save, size: 16 },
|
||||||
SelectAll: { svg: SelectAll, size: 16 },
|
SelectAll: { svg: SelectAll, size: 16 },
|
||||||
|
SelectParent: { svg: SelectParent, size: 16 },
|
||||||
Settings: { svg: Settings, size: 16 },
|
Settings: { svg: Settings, size: 16 },
|
||||||
SmallDot: { svg: SmallDot, size: 16 },
|
SmallDot: { svg: SmallDot, size: 16 },
|
||||||
Stack: { svg: Stack, size: 16 },
|
Stack: { svg: Stack, size: 16 },
|
||||||
|
|
@ -297,6 +303,8 @@ const SOLID_16PX = {
|
||||||
TransformationRotate: { svg: TransformationRotate, size: 16 },
|
TransformationRotate: { svg: TransformationRotate, size: 16 },
|
||||||
TransformationScale: { svg: TransformationScale, size: 16 },
|
TransformationScale: { svg: TransformationScale, size: 16 },
|
||||||
Trash: { svg: Trash, size: 16 },
|
Trash: { svg: Trash, size: 16 },
|
||||||
|
TurnNegative90: { svg: TurnNegative90, size: 16 },
|
||||||
|
TurnPositive90: { svg: TurnPositive90, size: 16 },
|
||||||
UserManual: { svg: UserManual, size: 16 },
|
UserManual: { svg: UserManual, size: 16 },
|
||||||
ViewModeNormal: { svg: ViewModeNormal, size: 16 },
|
ViewModeNormal: { svg: ViewModeNormal, size: 16 },
|
||||||
ViewModeOutline: { svg: ViewModeOutline, size: 16 },
|
ViewModeOutline: { svg: ViewModeOutline, size: 16 },
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue