mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 13:28:12 +08:00
Replace text-only tooltips with custom richly styled tooltips (#3436)
* Replace the title attribute with custom FloatingMenu tooltips * Separate tooltip labels and descriptions into two styled blocks * Move keyboard shortcut tooltips to a separate section at the bottom * Update shortcut key styling in tooltips and hints bar * Fix .to_string()
This commit is contained in:
+44
-22
@@ -42,17 +42,18 @@ impl PreferencesDialogMessageHandler {
|
||||
|
||||
let navigation_header = vec![TextLabel::new("Navigation").italic(true).widget_holder()];
|
||||
|
||||
let zoom_rate_tooltip = "Adjust how fast zooming occurs when using the scroll wheel or pinch gesture (relative to a default of 50)";
|
||||
let zoom_rate_description = "Adjust how fast zooming occurs when using the scroll wheel or pinch gesture (relative to a default of 50).";
|
||||
let zoom_rate_label = vec![
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
TextLabel::new("Zoom Rate").tooltip(zoom_rate_tooltip).widget_holder(),
|
||||
TextLabel::new("Zoom Rate").tooltip_label("Zoom Rate").tooltip_description(zoom_rate_description).widget_holder(),
|
||||
];
|
||||
let zoom_rate = vec![
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
NumberInput::new(Some(map_zoom_rate_to_display(preferences.viewport_zoom_wheel_rate)))
|
||||
.tooltip(zoom_rate_tooltip)
|
||||
.tooltip_label("Zoom Rate")
|
||||
.tooltip_description(zoom_rate_description)
|
||||
.mode_range()
|
||||
.int()
|
||||
.min(1.)
|
||||
@@ -69,12 +70,13 @@ impl PreferencesDialogMessageHandler {
|
||||
];
|
||||
|
||||
let checkbox_id = CheckboxId::new();
|
||||
let zoom_with_scroll_tooltip = "Use the scroll wheel for zooming instead of vertically panning (not recommended for trackpads)";
|
||||
let zoom_with_scroll_description = "Use the scroll wheel for zooming instead of vertically panning (not recommended for trackpads).";
|
||||
let zoom_with_scroll = vec![
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
CheckboxInput::new(preferences.zoom_with_scroll)
|
||||
.tooltip(zoom_with_scroll_tooltip)
|
||||
.tooltip_label("Zoom with Scroll")
|
||||
.tooltip_description(zoom_with_scroll_description)
|
||||
.on_update(|checkbox_input: &CheckboxInput| {
|
||||
PreferencesMessage::ModifyLayout {
|
||||
zoom_with_scroll: checkbox_input.checked,
|
||||
@@ -84,9 +86,10 @@ impl PreferencesDialogMessageHandler {
|
||||
.for_label(checkbox_id)
|
||||
.widget_holder(),
|
||||
TextLabel::new("Zoom with Scroll")
|
||||
.table_align(true)
|
||||
.tooltip(zoom_with_scroll_tooltip)
|
||||
.tooltip_label("Zoom with Scroll")
|
||||
.tooltip_description(zoom_with_scroll_description)
|
||||
.for_checkbox(checkbox_id)
|
||||
.table_align(true)
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
@@ -105,7 +108,8 @@ impl PreferencesDialogMessageHandler {
|
||||
let selection_mode = RadioInput::new(vec![
|
||||
RadioEntryData::new(SelectionMode::Touched.to_string())
|
||||
.label(SelectionMode::Touched.to_string())
|
||||
.tooltip(SelectionMode::Touched.tooltip_description())
|
||||
.tooltip_label(SelectionMode::Touched.to_string())
|
||||
.tooltip_description(SelectionMode::Touched.tooltip_description())
|
||||
.on_update(move |_| {
|
||||
PreferencesMessage::SelectionMode {
|
||||
selection_mode: SelectionMode::Touched,
|
||||
@@ -114,7 +118,8 @@ impl PreferencesDialogMessageHandler {
|
||||
}),
|
||||
RadioEntryData::new(SelectionMode::Enclosed.to_string())
|
||||
.label(SelectionMode::Enclosed.to_string())
|
||||
.tooltip(SelectionMode::Enclosed.tooltip_description())
|
||||
.tooltip_label(SelectionMode::Enclosed.to_string())
|
||||
.tooltip_description(SelectionMode::Enclosed.tooltip_description())
|
||||
.on_update(move |_| {
|
||||
PreferencesMessage::SelectionMode {
|
||||
selection_mode: SelectionMode::Enclosed,
|
||||
@@ -123,7 +128,8 @@ impl PreferencesDialogMessageHandler {
|
||||
}),
|
||||
RadioEntryData::new(SelectionMode::Directional.to_string())
|
||||
.label(SelectionMode::Directional.to_string())
|
||||
.tooltip(SelectionMode::Directional.tooltip_description())
|
||||
.tooltip_label(SelectionMode::Directional.to_string())
|
||||
.tooltip_description(SelectionMode::Directional.tooltip_description())
|
||||
.on_update(move |_| {
|
||||
PreferencesMessage::SelectionMode {
|
||||
selection_mode: SelectionMode::Directional,
|
||||
@@ -145,20 +151,25 @@ impl PreferencesDialogMessageHandler {
|
||||
|
||||
let experimental_header = vec![TextLabel::new("Experimental").italic(true).widget_holder()];
|
||||
|
||||
let node_graph_section_tooltip = "Appearance of the wires running between node connections in the graph";
|
||||
let node_graph_section_description = "Appearance of the wires running between node connections in the graph.";
|
||||
let node_graph_wires_label = vec![
|
||||
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_label("Node Graph Wires")
|
||||
.tooltip_description(node_graph_section_description)
|
||||
.widget_holder(),
|
||||
];
|
||||
let graph_wire_style = RadioInput::new(vec![
|
||||
RadioEntryData::new(GraphWireStyle::Direct.to_string())
|
||||
.label(GraphWireStyle::Direct.to_string())
|
||||
.tooltip(GraphWireStyle::Direct.tooltip_description())
|
||||
.tooltip_label(GraphWireStyle::Direct.to_string())
|
||||
.tooltip_description(GraphWireStyle::Direct.tooltip_description())
|
||||
.on_update(move |_| PreferencesMessage::GraphWireStyle { style: GraphWireStyle::Direct }.into()),
|
||||
RadioEntryData::new(GraphWireStyle::GridAligned.to_string())
|
||||
.label(GraphWireStyle::GridAligned.to_string())
|
||||
.tooltip(GraphWireStyle::GridAligned.tooltip_description())
|
||||
.tooltip_label(GraphWireStyle::GridAligned.to_string())
|
||||
.tooltip_description(GraphWireStyle::GridAligned.tooltip_description())
|
||||
.on_update(move |_| PreferencesMessage::GraphWireStyle { style: GraphWireStyle::GridAligned }.into()),
|
||||
])
|
||||
.selected_index(Some(preferences.graph_wire_style as u32))
|
||||
@@ -170,36 +181,47 @@ impl PreferencesDialogMessageHandler {
|
||||
];
|
||||
|
||||
let checkbox_id = CheckboxId::new();
|
||||
let vello_tooltip = "Use the experimental Vello renderer (your browser must support WebGPU)";
|
||||
let vello_description = "Use the experimental Vello renderer. (Your browser must support WebGPU).";
|
||||
let use_vello = vec![
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
CheckboxInput::new(preferences.use_vello && preferences.supports_wgpu())
|
||||
.tooltip(vello_tooltip)
|
||||
.tooltip_label("Vello Renderer")
|
||||
.tooltip_description(vello_description)
|
||||
.disabled(!preferences.supports_wgpu())
|
||||
.on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::UseVello { use_vello: checkbox_input.checked }.into())
|
||||
.for_label(checkbox_id)
|
||||
.widget_holder(),
|
||||
TextLabel::new("Vello Renderer")
|
||||
.table_align(true)
|
||||
.tooltip(vello_tooltip)
|
||||
.tooltip_label("Vello Renderer")
|
||||
.tooltip_description(vello_description)
|
||||
.disabled(!preferences.supports_wgpu())
|
||||
.for_checkbox(checkbox_id)
|
||||
.table_align(true)
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let checkbox_id = CheckboxId::new();
|
||||
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 stroke joins and fills.";
|
||||
let vector_mesh_description = "
|
||||
Allow tools to produce vector meshes, where more than two segments can connect to an anchor point.\n\
|
||||
Currently this does not properly handle stroke joins and fills.
|
||||
"
|
||||
.trim();
|
||||
let vector_meshes = vec![
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
CheckboxInput::new(preferences.vector_meshes)
|
||||
.tooltip(vector_mesh_tooltip)
|
||||
.tooltip_label("Vector Meshes")
|
||||
.tooltip_description(vector_mesh_description)
|
||||
.on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::VectorMeshes { enabled: checkbox_input.checked }.into())
|
||||
.for_label(checkbox_id)
|
||||
.widget_holder(),
|
||||
TextLabel::new("Vector Meshes").table_align(true).tooltip(vector_mesh_tooltip).for_checkbox(checkbox_id).widget_holder(),
|
||||
TextLabel::new("Vector Meshes")
|
||||
.tooltip_label("Vector Meshes")
|
||||
.tooltip_description(vector_mesh_description)
|
||||
.for_checkbox(checkbox_id)
|
||||
.table_align(true)
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
|
||||
@@ -49,16 +49,19 @@ impl DialogLayoutHolder for LicensesDialog {
|
||||
|
||||
impl LayoutHolder for LicensesDialog {
|
||||
fn layout(&self) -> Layout {
|
||||
let description = concat!(
|
||||
"The Graphite logo and brand identity are copyright © [YEAR]\nGraphite Labs, LLC. See \"Graphite Logo\" for usage policy.",
|
||||
"\n\n",
|
||||
"The Graphite editor's icons and design assets are copyright\n© [YEAR] Graphite Labs, LLC. See \"Graphite Icons\" for details.",
|
||||
"\n\n",
|
||||
"Graphite code is copyright © [YEAR] Graphite contributors\nand is made available under the Apache 2.0 license. See\n\"Graphite License\" for details.",
|
||||
"\n\n",
|
||||
"Graphite is distributed with third-party open source code\ndependencies. See \"Other Licenses\" for details.",
|
||||
)
|
||||
.replace("[YEAR]", &self.localized_commit_year);
|
||||
let year = &self.localized_commit_year;
|
||||
let description = format!(
|
||||
"
|
||||
The Graphite logo and brand identity are copyright © {year}\nGraphite Labs, LLC. See \"Graphite Logo\" for usage policy.\n\
|
||||
\n\
|
||||
The Graphite editor's icons and design assets are copyright\n© {year} Graphite Labs, LLC. See \"Graphite Icons\" for details.\n\
|
||||
\n\
|
||||
Graphite code is copyright © {year} Graphite contributors\nand is made available under the Apache 2.0 license. See\n\"Graphite License\" for details.\n\
|
||||
\n\
|
||||
Graphite is distributed with third-party open source code\ndependencies. See \"Other Licenses\" for details.
|
||||
"
|
||||
);
|
||||
let description = description.trim();
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
|
||||
@@ -410,7 +410,6 @@ pub fn input_mappings() -> Mapping {
|
||||
entry!(KeyDown(FakeKeyPlus); modifiers=[Accel], canonical, action_dispatch=NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }),
|
||||
entry!(KeyDown(Equal); modifiers=[Accel], action_dispatch=NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }),
|
||||
entry!(KeyDown(Minus); modifiers=[Accel], action_dispatch=NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }),
|
||||
entry!(KeyDown(KeyF); modifiers=[Alt], action_dispatch=NavigationMessage::CanvasFlip),
|
||||
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::CanvasZoomMouseWheel),
|
||||
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: true }),
|
||||
entry!(WheelScroll; action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: false }),
|
||||
|
||||
@@ -296,9 +296,9 @@ impl fmt::Display for Key {
|
||||
KeyboardPlatformLayout::Standard => "Ctrl",
|
||||
KeyboardPlatformLayout::Mac => "⌘",
|
||||
},
|
||||
Self::MouseLeft => "LMB",
|
||||
Self::MouseRight => "RMB",
|
||||
Self::MouseMiddle => "MMB",
|
||||
Self::MouseLeft => "Click",
|
||||
Self::MouseRight => "R.Click",
|
||||
Self::MouseMiddle => "M.Click",
|
||||
Self::MouseBack => "Mouse Back",
|
||||
Self::MouseForward => "Mouse Fwd",
|
||||
|
||||
@@ -321,7 +321,7 @@ impl From<Key> for LayoutKey {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub struct LayoutKey {
|
||||
pub key: Key,
|
||||
key: Key,
|
||||
label: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -351,37 +351,73 @@ impl From<Vec<WidgetHolder>> for LayoutGroup {
|
||||
}
|
||||
|
||||
impl LayoutGroup {
|
||||
/// Applies a tooltip to all widgets in this row or column without a tooltip.
|
||||
pub fn with_tooltip(self, tooltip: impl Into<String>) -> Self {
|
||||
/// Applies a tooltip label to all widgets in this row or column without a tooltip.
|
||||
pub fn with_tooltip_label(self, label: impl Into<String>) -> Self {
|
||||
let (is_col, mut widgets) = match self {
|
||||
LayoutGroup::Column { widgets } => (true, widgets),
|
||||
LayoutGroup::Row { widgets } => (false, widgets),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let tooltip = tooltip.into();
|
||||
let label = label.into();
|
||||
for widget in &mut widgets {
|
||||
let val = match &mut widget.widget {
|
||||
Widget::CheckboxInput(x) => &mut x.tooltip,
|
||||
Widget::ColorInput(x) => &mut x.tooltip,
|
||||
Widget::CurveInput(x) => &mut x.tooltip,
|
||||
Widget::DropdownInput(x) => &mut x.tooltip,
|
||||
Widget::FontInput(x) => &mut x.tooltip,
|
||||
Widget::IconButton(x) => &mut x.tooltip,
|
||||
Widget::IconLabel(x) => &mut x.tooltip,
|
||||
Widget::ImageButton(x) => &mut x.tooltip,
|
||||
Widget::ImageLabel(x) => &mut x.tooltip,
|
||||
Widget::NumberInput(x) => &mut x.tooltip,
|
||||
Widget::ParameterExposeButton(x) => &mut x.tooltip,
|
||||
Widget::PopoverButton(x) => &mut x.tooltip,
|
||||
Widget::TextAreaInput(x) => &mut x.tooltip,
|
||||
Widget::TextButton(x) => &mut x.tooltip,
|
||||
Widget::TextInput(x) => &mut x.tooltip,
|
||||
Widget::TextLabel(x) => &mut x.tooltip,
|
||||
Widget::BreadcrumbTrailButtons(x) => &mut x.tooltip,
|
||||
Widget::CheckboxInput(x) => &mut x.tooltip_label,
|
||||
Widget::ColorInput(x) => &mut x.tooltip_label,
|
||||
Widget::CurveInput(x) => &mut x.tooltip_label,
|
||||
Widget::DropdownInput(x) => &mut x.tooltip_label,
|
||||
Widget::FontInput(x) => &mut x.tooltip_label,
|
||||
Widget::IconButton(x) => &mut x.tooltip_label,
|
||||
Widget::IconLabel(x) => &mut x.tooltip_label,
|
||||
Widget::ImageButton(x) => &mut x.tooltip_label,
|
||||
Widget::ImageLabel(x) => &mut x.tooltip_label,
|
||||
Widget::NumberInput(x) => &mut x.tooltip_label,
|
||||
Widget::ParameterExposeButton(x) => &mut x.tooltip_label,
|
||||
Widget::PopoverButton(x) => &mut x.tooltip_label,
|
||||
Widget::TextAreaInput(x) => &mut x.tooltip_label,
|
||||
Widget::TextButton(x) => &mut x.tooltip_label,
|
||||
Widget::TextInput(x) => &mut x.tooltip_label,
|
||||
Widget::TextLabel(x) => &mut x.tooltip_label,
|
||||
Widget::BreadcrumbTrailButtons(x) => &mut x.tooltip_label,
|
||||
Widget::InvisibleStandinInput(_) | Widget::ReferencePointInput(_) | Widget::RadioInput(_) | Widget::Separator(_) | Widget::WorkingColorsInput(_) | Widget::NodeCatalog(_) => continue,
|
||||
};
|
||||
if val.is_empty() {
|
||||
val.clone_from(&tooltip);
|
||||
val.clone_from(&label);
|
||||
}
|
||||
}
|
||||
if is_col { Self::Column { widgets } } else { Self::Row { widgets } }
|
||||
}
|
||||
|
||||
/// Applies a tooltip description to all widgets in this row or column without a tooltip.
|
||||
pub fn with_tooltip_description(self, description: impl Into<String>) -> Self {
|
||||
let (is_col, mut widgets) = match self {
|
||||
LayoutGroup::Column { widgets } => (true, widgets),
|
||||
LayoutGroup::Row { widgets } => (false, widgets),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let description = description.into();
|
||||
for widget in &mut widgets {
|
||||
let val = match &mut widget.widget {
|
||||
Widget::CheckboxInput(x) => &mut x.tooltip_description,
|
||||
Widget::ColorInput(x) => &mut x.tooltip_description,
|
||||
Widget::CurveInput(x) => &mut x.tooltip_description,
|
||||
Widget::DropdownInput(x) => &mut x.tooltip_description,
|
||||
Widget::FontInput(x) => &mut x.tooltip_description,
|
||||
Widget::IconButton(x) => &mut x.tooltip_description,
|
||||
Widget::IconLabel(x) => &mut x.tooltip_description,
|
||||
Widget::ImageButton(x) => &mut x.tooltip_description,
|
||||
Widget::ImageLabel(x) => &mut x.tooltip_description,
|
||||
Widget::NumberInput(x) => &mut x.tooltip_description,
|
||||
Widget::ParameterExposeButton(x) => &mut x.tooltip_description,
|
||||
Widget::PopoverButton(x) => &mut x.tooltip_description,
|
||||
Widget::TextAreaInput(x) => &mut x.tooltip_description,
|
||||
Widget::TextButton(x) => &mut x.tooltip_description,
|
||||
Widget::TextInput(x) => &mut x.tooltip_description,
|
||||
Widget::TextLabel(x) => &mut x.tooltip_description,
|
||||
Widget::BreadcrumbTrailButtons(x) => &mut x.tooltip_description,
|
||||
Widget::InvisibleStandinInput(_) | Widget::ReferencePointInput(_) | Widget::RadioInput(_) | Widget::Separator(_) | Widget::WorkingColorsInput(_) | Widget::NodeCatalog(_) => continue,
|
||||
};
|
||||
if val.is_empty() {
|
||||
val.clone_from(&description);
|
||||
}
|
||||
}
|
||||
if is_col { Self::Column { widgets } } else { Self::Row { widgets } }
|
||||
@@ -513,8 +549,9 @@ impl WidgetHolder {
|
||||
&& button1.style == button2.style
|
||||
&& button1.menu_direction == button2.menu_direction
|
||||
&& button1.icon == button2.icon
|
||||
&& button1.tooltip == button2.tooltip
|
||||
&& button1.tooltip_shortcut == button2.tooltip_shortcut
|
||||
&& button1.tooltip_label == button2.tooltip_label
|
||||
&& button1.tooltip_description == button2.tooltip_description
|
||||
&& button1.shortcut_keys == button2.shortcut_keys
|
||||
&& button1.popover_min_width == button2.popover_min_width
|
||||
{
|
||||
let mut new_widget_path = widget_path.to_vec();
|
||||
@@ -617,36 +654,29 @@ impl DiffUpdate {
|
||||
/// Append the keyboard shortcut to the tooltip where applicable
|
||||
pub fn apply_keyboard_shortcut(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) {
|
||||
// Function used multiple times later in this code block to convert `ActionKeys::Action` to `ActionKeys::Keys` and append its shortcut to the tooltip
|
||||
let apply_shortcut_to_tooltip = |tooltip_shortcut: &mut ActionKeys, tooltip: &mut String| {
|
||||
let shortcut_text = tooltip_shortcut.to_keys(action_input_mapping);
|
||||
let apply_shortcut_to_tooltip = |shortcut_keys: &mut ActionKeys, tooltip_shortcut: &mut String| {
|
||||
let shortcut_text = shortcut_keys.to_keys(action_input_mapping);
|
||||
|
||||
if let ActionKeys::Keys(_keys) = tooltip_shortcut
|
||||
&& !shortcut_text.is_empty()
|
||||
{
|
||||
if !tooltip.is_empty() {
|
||||
tooltip.push(' ');
|
||||
}
|
||||
tooltip.push('(');
|
||||
tooltip.push_str(&shortcut_text);
|
||||
tooltip.push(')');
|
||||
if matches!(shortcut_keys, ActionKeys::Keys(_)) && !shortcut_text.is_empty() {
|
||||
tooltip_shortcut.push_str(&shortcut_text);
|
||||
}
|
||||
};
|
||||
|
||||
// Go through each widget to convert `ActionKeys::Action` to `ActionKeys::Keys` and append the key combination to the widget tooltip
|
||||
let convert_tooltip = |widget_holder: &mut WidgetHolder| {
|
||||
// Handle all the widgets that have tooltips
|
||||
let mut tooltip_shortcut = match &mut widget_holder.widget {
|
||||
Widget::BreadcrumbTrailButtons(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::CheckboxInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::ColorInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::DropdownInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::FontInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::IconButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::NumberInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::ParameterExposeButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::PopoverButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::TextButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::ImageButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
let mut shortcut_keys = match &mut widget_holder.widget {
|
||||
Widget::BreadcrumbTrailButtons(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::CheckboxInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::ColorInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::DropdownInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::FontInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::IconButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::NumberInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::ParameterExposeButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::PopoverButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::TextButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::ImageButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
||||
Widget::IconLabel(_)
|
||||
| Widget::ImageLabel(_)
|
||||
| Widget::CurveInput(_)
|
||||
@@ -660,20 +690,20 @@ impl DiffUpdate {
|
||||
| Widget::TextLabel(_)
|
||||
| Widget::WorkingColorsInput(_) => None,
|
||||
};
|
||||
if let Some((tooltip, Some(tooltip_shortcut))) = &mut tooltip_shortcut {
|
||||
apply_shortcut_to_tooltip(tooltip_shortcut, tooltip);
|
||||
if let Some((tooltip_shortcut, Some(shortcut_keys))) = &mut shortcut_keys {
|
||||
apply_shortcut_to_tooltip(shortcut_keys, tooltip_shortcut);
|
||||
}
|
||||
|
||||
// Handle RadioInput separately because its tooltips are children of the widget
|
||||
if let Widget::RadioInput(radio_input) = &mut widget_holder.widget {
|
||||
for radio_entry_data in &mut radio_input.entries {
|
||||
if let RadioEntryData {
|
||||
tooltip,
|
||||
tooltip_shortcut: Some(tooltip_shortcut),
|
||||
tooltip_shortcut,
|
||||
shortcut_keys: Some(shortcut_keys),
|
||||
..
|
||||
} = radio_entry_data
|
||||
{
|
||||
apply_shortcut_to_tooltip(tooltip_shortcut, tooltip);
|
||||
apply_shortcut_to_tooltip(shortcut_keys, tooltip_shortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,17 @@ pub struct IconButton {
|
||||
|
||||
pub active: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
@@ -49,10 +56,17 @@ pub struct PopoverButton {
|
||||
|
||||
pub disabled: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
#[serde(rename = "popoverLayout")]
|
||||
pub popover_layout: SubLayout,
|
||||
@@ -83,10 +97,17 @@ pub struct ParameterExposeButton {
|
||||
#[serde(rename = "dataType")]
|
||||
pub data_type: FrontendGraphDataType,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
@@ -120,10 +141,17 @@ pub struct TextButton {
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
#[serde(rename = "menuListChildren")]
|
||||
pub menu_list_children: MenuListEntrySections,
|
||||
@@ -148,10 +176,17 @@ pub struct ImageButton {
|
||||
|
||||
pub height: Option<String>,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
@@ -186,10 +221,17 @@ pub struct ColorInput {
|
||||
#[serde(rename = "menuDirection")]
|
||||
pub menu_direction: Option<MenuDirection>,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
@@ -209,10 +251,17 @@ pub struct BreadcrumbTrailButtons {
|
||||
|
||||
pub disabled: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
|
||||
@@ -16,14 +16,21 @@ pub struct CheckboxInput {
|
||||
|
||||
pub icon: String,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(rename = "forLabel")]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub for_label: CheckboxId,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
@@ -38,11 +45,13 @@ pub struct CheckboxInput {
|
||||
impl Default for CheckboxInput {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
checked: false,
|
||||
disabled: false,
|
||||
icon: "Checkmark".into(),
|
||||
tooltip: Default::default(),
|
||||
checked: Default::default(),
|
||||
disabled: Default::default(),
|
||||
tooltip_label: Default::default(),
|
||||
tooltip_description: Default::default(),
|
||||
tooltip_shortcut: Default::default(),
|
||||
shortcut_keys: Default::default(),
|
||||
for_label: CheckboxId::new(),
|
||||
on_update: Default::default(),
|
||||
on_commit: Default::default(),
|
||||
@@ -90,10 +99,17 @@ pub struct DropdownInput {
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
// Styling
|
||||
#[serde(rename = "minWidth")]
|
||||
@@ -154,10 +170,17 @@ pub struct FontInput {
|
||||
|
||||
pub disabled: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
@@ -190,10 +213,17 @@ pub struct NumberInput {
|
||||
// Label
|
||||
pub label: String,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
// Disabled
|
||||
pub disabled: bool,
|
||||
@@ -359,10 +389,17 @@ pub struct RadioEntryData {
|
||||
|
||||
pub icon: String,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
@@ -394,7 +431,14 @@ pub struct TextAreaInput {
|
||||
|
||||
pub disabled: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
@@ -418,7 +462,14 @@ pub struct TextInput {
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
pub centered: bool,
|
||||
|
||||
@@ -446,7 +497,14 @@ pub struct CurveInput {
|
||||
|
||||
pub disabled: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
@@ -466,7 +524,14 @@ pub struct ReferencePointInput {
|
||||
|
||||
pub disabled: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
|
||||
@@ -9,7 +9,14 @@ pub struct IconLabel {
|
||||
|
||||
pub disabled: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
|
||||
@@ -60,7 +67,14 @@ pub struct TextLabel {
|
||||
#[serde(rename = "minWidth")]
|
||||
pub min_width: String,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
|
||||
#[serde(rename = "forCheckbox")]
|
||||
#[derivative(PartialEq = "ignore")]
|
||||
@@ -81,7 +95,14 @@ pub struct ImageLabel {
|
||||
|
||||
pub height: Option<String>,
|
||||
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: String,
|
||||
}
|
||||
|
||||
// TODO: Add UserInputLabel
|
||||
|
||||
@@ -97,13 +97,13 @@ impl DataPanelMessageHandler {
|
||||
|
||||
widgets.extend([
|
||||
if is_layer {
|
||||
IconLabel::new("Layer").tooltip("Name of the selected layer").widget_holder()
|
||||
IconLabel::new("Layer").tooltip_description("Name of the selected layer.").widget_holder()
|
||||
} else {
|
||||
IconLabel::new("Node").tooltip("Name of the selected node").widget_holder()
|
||||
IconLabel::new("Node").tooltip_description("Name of the selected node.").widget_holder()
|
||||
},
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
TextInput::new(network_interface.display_name(&node_id, &[]))
|
||||
.tooltip(if is_layer { "Name of the selected layer" } else { "Name of the selected node" })
|
||||
.tooltip_description(if is_layer { "Name of the selected layer." } else { "Name of the selected node." })
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id,
|
||||
|
||||
@@ -2211,21 +2211,21 @@ impl DocumentMessageHandler {
|
||||
|
||||
let mut widgets = vec![
|
||||
IconButton::new("PlaybackToStart", 24)
|
||||
.tooltip("Restart Animation")
|
||||
.tooltip_shortcut(action_keys!(AnimationMessageDiscriminant::RestartAnimation))
|
||||
.tooltip_label("Restart Animation")
|
||||
.shortcut_keys(action_keys!(AnimationMessageDiscriminant::RestartAnimation))
|
||||
.on_update(|_| AnimationMessage::RestartAnimation.into())
|
||||
.disabled(time == Duration::ZERO)
|
||||
.widget_holder(),
|
||||
IconButton::new(if animation_is_playing { "PlaybackPause" } else { "PlaybackPlay" }, 24)
|
||||
.tooltip(if animation_is_playing { "Pause Animation" } else { "Play Animation" })
|
||||
.tooltip_shortcut(action_keys!(AnimationMessageDiscriminant::ToggleLivePreview))
|
||||
.tooltip_label(if animation_is_playing { "Pause Animation" } else { "Play Animation" })
|
||||
.shortcut_keys(action_keys!(AnimationMessageDiscriminant::ToggleLivePreview))
|
||||
.on_update(|_| AnimationMessage::ToggleLivePreview.into())
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
CheckboxInput::new(self.overlays_visibility_settings.all)
|
||||
.icon("Overlays")
|
||||
.tooltip("Overlays")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleOverlaysVisibility))
|
||||
.tooltip_label("Overlays")
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleOverlaysVisibility))
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
@@ -2473,8 +2473,8 @@ impl DocumentMessageHandler {
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
CheckboxInput::new(snapping_state.snapping_enabled)
|
||||
.icon("Snapping")
|
||||
.tooltip("Snapping")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSnapping))
|
||||
.tooltip_label("Snapping")
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSnapping))
|
||||
.on_update(move |optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetSnapping {
|
||||
closure: Some(|snapping_state| &mut snapping_state.snapping_enabled),
|
||||
@@ -2494,7 +2494,7 @@ impl DocumentMessageHandler {
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
.chain(SNAP_FUNCTIONS_FOR_BOUNDING_BOXES.into_iter().map(|(name, closure, tooltip)| LayoutGroup::Row {
|
||||
.chain(SNAP_FUNCTIONS_FOR_BOUNDING_BOXES.into_iter().map(|(name, closure, description)| LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
@@ -2506,17 +2506,18 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.tooltip(tooltip)
|
||||
.tooltip_label(name)
|
||||
.tooltip_description(description)
|
||||
.for_label(checkbox_id)
|
||||
.widget_holder(),
|
||||
TextLabel::new(name).tooltip(tooltip).for_checkbox(checkbox_id).widget_holder(),
|
||||
TextLabel::new(name).tooltip_label(name).tooltip_description(description).for_checkbox(checkbox_id).widget_holder(),
|
||||
]
|
||||
},
|
||||
}))
|
||||
.chain([LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(SnappingOptions::Paths.to_string()).widget_holder()],
|
||||
}])
|
||||
.chain(SNAP_FUNCTIONS_FOR_PATHS.into_iter().map(|(name, closure, tooltip)| LayoutGroup::Row {
|
||||
.chain(SNAP_FUNCTIONS_FOR_PATHS.into_iter().map(|(name, closure, description)| LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
@@ -2528,10 +2529,11 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.tooltip(tooltip)
|
||||
.tooltip_label(name)
|
||||
.tooltip_description(description)
|
||||
.for_label(checkbox_id)
|
||||
.widget_holder(),
|
||||
TextLabel::new(name).tooltip(tooltip).for_checkbox(checkbox_id).widget_holder(),
|
||||
TextLabel::new(name).tooltip_label(name).tooltip_description(description).for_checkbox(checkbox_id).widget_holder(),
|
||||
]
|
||||
},
|
||||
}))
|
||||
@@ -2541,8 +2543,8 @@ impl DocumentMessageHandler {
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
CheckboxInput::new(self.snapping_state.grid_snapping)
|
||||
.icon("Grid")
|
||||
.tooltip("Grid")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleGridVisibility))
|
||||
.tooltip_label("Grid")
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleGridVisibility))
|
||||
.on_update(|optional_input: &CheckboxInput| DocumentMessage::GridVisibility { visible: optional_input.checked }.into())
|
||||
.widget_holder(),
|
||||
PopoverButton::new()
|
||||
@@ -2553,19 +2555,19 @@ impl DocumentMessageHandler {
|
||||
RadioInput::new(vec![
|
||||
RadioEntryData::new("Normal")
|
||||
.icon("RenderModeNormal")
|
||||
.tooltip("Render Mode: Normal")
|
||||
.tooltip_label("Render Mode: Normal")
|
||||
.on_update(|_| DocumentMessage::SetRenderMode { render_mode: RenderMode::Normal }.into()),
|
||||
RadioEntryData::new("Outline")
|
||||
.icon("RenderModeOutline")
|
||||
.tooltip("Render Mode: Outline")
|
||||
.tooltip_label("Render Mode: Outline")
|
||||
.on_update(|_| DocumentMessage::SetRenderMode { render_mode: RenderMode::Outline }.into()),
|
||||
// RadioEntryData::new("PixelPreview")
|
||||
// .icon("RenderModePixels")
|
||||
// .tooltip("Render Mode: Pixel Preview")
|
||||
// .tooltip_label("Render Mode: Pixel Preview")
|
||||
// .on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(320) }.into()),
|
||||
// RadioEntryData::new("SvgPreview")
|
||||
// .icon("RenderModeSvg")
|
||||
// .tooltip("Render Mode: SVG Preview")
|
||||
// .tooltip_label("Render Mode: SVG Preview")
|
||||
// .on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(1845) }.into()),
|
||||
])
|
||||
.selected_index(Some(self.render_mode as u32))
|
||||
@@ -2607,7 +2609,7 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.tooltip("Canvas Tilt")
|
||||
.tooltip_label("Canvas Tilt")
|
||||
.on_update(|number_input: &NumberInput| {
|
||||
NavigationMessage::CanvasTiltSet {
|
||||
angle_radians: number_input.value.unwrap().to_radians(),
|
||||
@@ -2623,8 +2625,8 @@ impl DocumentMessageHandler {
|
||||
TextButton::new("Node Graph")
|
||||
.icon(Some((if self.graph_view_overlay_open { "GraphViewOpen" } else { "GraphViewClosed" }).into()))
|
||||
.hover_icon(Some((if self.graph_view_overlay_open { "GraphViewClosed" } else { "GraphViewOpen" }).into()))
|
||||
.tooltip(if self.graph_view_overlay_open { "Hide Node Graph" } else { "Show Node Graph" })
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
||||
.tooltip_label(if self.graph_view_overlay_open { "Hide Node Graph" } else { "Show Node Graph" })
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
||||
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
||||
.widget_holder(),
|
||||
]);
|
||||
@@ -2723,7 +2725,7 @@ impl DocumentMessageHandler {
|
||||
.disabled(disabled)
|
||||
.draw_icon(false)
|
||||
.max_width(100)
|
||||
.tooltip("Blend Mode")
|
||||
.tooltip_label("Blend Mode")
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
NumberInput::new(opacity)
|
||||
@@ -2745,7 +2747,7 @@ impl DocumentMessageHandler {
|
||||
})
|
||||
.on_commit(|_| DocumentMessage::AddTransaction.into())
|
||||
.max_width(100)
|
||||
.tooltip("Opacity")
|
||||
.tooltip_label("Opacity")
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
NumberInput::new(fill)
|
||||
@@ -2767,7 +2769,7 @@ impl DocumentMessageHandler {
|
||||
})
|
||||
.on_commit(|_| DocumentMessage::AddTransaction.into())
|
||||
.max_width(100)
|
||||
.tooltip("Fill")
|
||||
.tooltip_label("Fill")
|
||||
.widget_holder(),
|
||||
];
|
||||
let layers_panel_control_bar_left = WidgetLayout::new(vec![LayoutGroup::Row { widgets }]);
|
||||
@@ -2775,15 +2777,15 @@ impl DocumentMessageHandler {
|
||||
let widgets = vec![
|
||||
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
||||
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
||||
.tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked))
|
||||
.tooltip_label(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked))
|
||||
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||
.disabled(!has_selection)
|
||||
.widget_holder(),
|
||||
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
||||
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
||||
.tooltip(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
|
||||
.tooltip_label(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
|
||||
.on_update(|_| DocumentMessage::ToggleSelectedVisibility.into())
|
||||
.disabled(!has_selection)
|
||||
.widget_holder(),
|
||||
@@ -2816,7 +2818,7 @@ impl DocumentMessageHandler {
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.menu_direction(Some(MenuDirection::Top))
|
||||
.tooltip("Add an operation to the end of this layer's chain of nodes")
|
||||
.tooltip_description("Add an operation to the end of this layer's chain of nodes.")
|
||||
.disabled(!has_selection || has_multiple_selection)
|
||||
.popover_layout({
|
||||
// Showing only compatible types for the layer based on the output type of the node upstream from its horizontal input
|
||||
@@ -2847,8 +2849,8 @@ impl DocumentMessageHandler {
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
IconButton::new("Folder", 24)
|
||||
.tooltip("Group Selected")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
||||
.tooltip_label("Group Selected")
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
||||
.on_update(|_| {
|
||||
let group_folder_type = GroupFolderType::Layer;
|
||||
DocumentMessage::GroupSelectedLayers { group_folder_type }.into()
|
||||
@@ -2856,13 +2858,13 @@ impl DocumentMessageHandler {
|
||||
.disabled(!has_selection)
|
||||
.widget_holder(),
|
||||
IconButton::new("NewLayer", 24)
|
||||
.tooltip("New Layer")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
||||
.tooltip_label("New Layer")
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
||||
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
|
||||
.widget_holder(),
|
||||
IconButton::new("Trash", 24)
|
||||
.tooltip("Delete Selected")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
||||
.tooltip_label("Delete Selected")
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
||||
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
|
||||
.disabled(!has_selection)
|
||||
.widget_holder(),
|
||||
@@ -3145,18 +3147,18 @@ impl<'a> ClickXRayIter<'a> {
|
||||
pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHandler, node_graph: bool) -> Vec<WidgetHolder> {
|
||||
let mut list = vec![
|
||||
IconButton::new("ZoomIn", 24)
|
||||
.tooltip("Zoom In")
|
||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasZoomIncrease))
|
||||
.tooltip_label("Zoom In")
|
||||
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasZoomIncrease))
|
||||
.on_update(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
|
||||
.widget_holder(),
|
||||
IconButton::new("ZoomOut", 24)
|
||||
.tooltip("Zoom Out")
|
||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasZoomDecrease))
|
||||
.tooltip_label("Zoom Out")
|
||||
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasZoomDecrease))
|
||||
.on_update(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
|
||||
.widget_holder(),
|
||||
IconButton::new("ZoomReset", 24)
|
||||
.tooltip("Reset Tilt and Zoom to 100%")
|
||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
|
||||
.tooltip_label("Reset Tilt and Zoom to 100%")
|
||||
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
|
||||
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
|
||||
.disabled(ptz.tilt().abs() < 1e-4 && (ptz.zoom() - 1.).abs() < 1e-4)
|
||||
.widget_holder(),
|
||||
@@ -3164,8 +3166,9 @@ pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHand
|
||||
if ptz.flip && !node_graph {
|
||||
list.push(
|
||||
IconButton::new("Reverse", 24)
|
||||
.tooltip("Flip the canvas back to its standard orientation")
|
||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasFlip))
|
||||
.tooltip_label("Unflip Canvas")
|
||||
.tooltip_description("Flip the canvas back to its standard orientation.")
|
||||
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasFlip))
|
||||
.on_update(|_| NavigationMessage::CanvasFlip.into())
|
||||
.widget_holder(),
|
||||
);
|
||||
@@ -3176,7 +3179,7 @@ pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHand
|
||||
.unit("%")
|
||||
.min(0.000001)
|
||||
.max(1000000.)
|
||||
.tooltip(if node_graph { "Node Graph Zoom" } else { "Canvas Zoom" })
|
||||
.tooltip_label(if node_graph { "Node Graph Zoom" } else { "Canvas Zoom" })
|
||||
.on_update(|number_input: &NumberInput| {
|
||||
NavigationMessage::CanvasZoomSet {
|
||||
zoom_factor: number_input.value.unwrap() / 100.,
|
||||
|
||||
@@ -137,7 +137,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("A default node network you can use to create your own custom nodes."),
|
||||
description: Cow::Borrowed("An empty node network you can use to create your own custom nodes."),
|
||||
properties: None,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
@@ -156,7 +156,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
description: Cow::Borrowed(
|
||||
"Improves rendering performance if used in rare circumstances where automatic caching is not yet advanced enough to handle the situation.
|
||||
Stores the last evaluated data that flowed through this node, and immediately returns that data on subsequent renders if the context has not changed.",
|
||||
),
|
||||
properties: None,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
@@ -1153,7 +1156,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("Generates different noise patterns."),
|
||||
description: Cow::Borrowed("Generates customizable procedural noise patterns."),
|
||||
properties: None,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
@@ -1335,7 +1338,9 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed(
|
||||
"Decomposes the X and Y components of a vec2.\n\nThe inverse of this node is \"Vec2 Value\", which can have either or both its X and Y parameters exposed as graph inputs.",
|
||||
"Decomposes the X and Y components of a vec2.\n\
|
||||
\n\
|
||||
The inverse of this node is \"Vec2 Value\", which can have either or both its X and Y parameters exposed as graph inputs.",
|
||||
),
|
||||
properties: None,
|
||||
},
|
||||
@@ -2008,10 +2013,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
}),
|
||||
input_metadata: vec![
|
||||
("Content", "The shape to be resampled and converted into a polyline.").into(),
|
||||
("Spacing", node_properties::SAMPLE_POLYLINE_TOOLTIP_SPACING).into(),
|
||||
("Spacing", node_properties::SAMPLE_POLYLINE_DESCRIPTION_SPACING).into(),
|
||||
InputMetadata::with_name_description_override(
|
||||
"Separation",
|
||||
node_properties::SAMPLE_POLYLINE_TOOLTIP_SEPARATION,
|
||||
node_properties::SAMPLE_POLYLINE_DESCRIPTION_SEPARATION,
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(0.),
|
||||
unit: Some(" px".to_string()),
|
||||
@@ -2020,7 +2025,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
),
|
||||
InputMetadata::with_name_description_override(
|
||||
"Quantity",
|
||||
node_properties::SAMPLE_POLYLINE_TOOLTIP_QUANTITY,
|
||||
node_properties::SAMPLE_POLYLINE_DESCRIPTION_QUANTITY,
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(2.),
|
||||
is_integer: true,
|
||||
@@ -2029,7 +2034,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
),
|
||||
InputMetadata::with_name_description_override(
|
||||
"Start Offset",
|
||||
node_properties::SAMPLE_POLYLINE_TOOLTIP_START_OFFSET,
|
||||
node_properties::SAMPLE_POLYLINE_DESCRIPTION_START_OFFSET,
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(0.),
|
||||
unit: Some(" px".to_string()),
|
||||
@@ -2038,14 +2043,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
),
|
||||
InputMetadata::with_name_description_override(
|
||||
"Stop Offset",
|
||||
node_properties::SAMPLE_POLYLINE_TOOLTIP_STOP_OFFSET,
|
||||
node_properties::SAMPLE_POLYLINE_DESCRIPTION_STOP_OFFSET,
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(0.),
|
||||
unit: Some(" px".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
),
|
||||
("Adaptive Spacing", node_properties::SAMPLE_POLYLINE_TOOLTIP_ADAPTIVE_SPACING).into(),
|
||||
("Adaptive Spacing", node_properties::SAMPLE_POLYLINE_DESCRIPTION_ADAPTIVE_SPACING).into(),
|
||||
],
|
||||
output_names: vec!["Vector".to_string()],
|
||||
..Default::default()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendNode};
|
||||
use super::{document_node_definitions, node_properties};
|
||||
use crate::consts::GRID_SIZE;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
|
||||
@@ -2105,7 +2106,9 @@ impl NodeGraphMessageHandler {
|
||||
let mut widgets = vec![
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.tooltip("New Node (Right Click)")
|
||||
.tooltip_label("New Node")
|
||||
.tooltip_description("To add a node at the pointer location, perform the shortcut in an open area of the graph.")
|
||||
.tooltip_shortcut(Key::MouseRight.to_string())
|
||||
.popover_layout({
|
||||
// Showing only compatible types
|
||||
let compatible_type = match (selection_includes_layers, has_multiple_selection, selected_layer) {
|
||||
@@ -2150,8 +2153,8 @@ impl NodeGraphMessageHandler {
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
//
|
||||
IconButton::new("Folder", 24)
|
||||
.tooltip("Group Selected")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
||||
.tooltip_label("Group Selected")
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
||||
.on_update(|_| {
|
||||
let group_folder_type = GroupFolderType::Layer;
|
||||
DocumentMessage::GroupSelectedLayers { group_folder_type }.into()
|
||||
@@ -2159,13 +2162,13 @@ impl NodeGraphMessageHandler {
|
||||
.disabled(!has_selection)
|
||||
.widget_holder(),
|
||||
IconButton::new("NewLayer", 24)
|
||||
.tooltip("New Layer")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
||||
.tooltip_label("New Layer")
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
||||
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
|
||||
.widget_holder(),
|
||||
IconButton::new("Trash", 24)
|
||||
.tooltip("Delete Selected")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
||||
.tooltip_label("Delete Selected")
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
||||
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
|
||||
.disabled(!has_selection)
|
||||
.widget_holder(),
|
||||
@@ -2174,15 +2177,15 @@ impl NodeGraphMessageHandler {
|
||||
//
|
||||
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
||||
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
||||
.tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
||||
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedLocked))
|
||||
.tooltip_label(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
||||
.shortcut_keys(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedLocked))
|
||||
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||
.disabled(!has_selection || !selection_includes_layers)
|
||||
.widget_holder(),
|
||||
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
||||
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
||||
.tooltip(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
||||
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
|
||||
.tooltip_label(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
||||
.shortcut_keys(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
|
||||
.on_update(|_| NodeGraphMessage::ToggleSelectedVisibility.into())
|
||||
.disabled(!has_selection)
|
||||
.widget_holder(),
|
||||
@@ -2208,7 +2211,7 @@ impl NodeGraphMessageHandler {
|
||||
if let Some(node_id) = previewing {
|
||||
let button = TextButton::new("End Preview")
|
||||
.icon(Some("FrameAll".to_string()))
|
||||
.tooltip("Restore preview to the graph output")
|
||||
.tooltip_description("Restore preview to the graph output.")
|
||||
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
||||
.widget_holder();
|
||||
widgets.extend([Separator::new(SeparatorType::Unrelated).widget_holder(), button]);
|
||||
@@ -2220,7 +2223,9 @@ impl NodeGraphMessageHandler {
|
||||
if selection_is_not_already_the_output && no_other_selections {
|
||||
let button = TextButton::new("Preview")
|
||||
.icon(Some("FrameAll".to_string()))
|
||||
.tooltip("Preview selected node/layer (Shortcut: Alt-click node/layer)")
|
||||
.tooltip_label("Preview")
|
||||
.tooltip_description("Temporarily set the graph output to the selected node or layer. Perform the shortcut on a node or layer for quick access.")
|
||||
.tooltip_shortcut(KeysGroup(vec![Key::Alt, Key::MouseLeft]).to_string())
|
||||
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
||||
.widget_holder();
|
||||
widgets.extend([Separator::new(SeparatorType::Unrelated).widget_holder(), button]);
|
||||
@@ -2262,7 +2267,7 @@ impl NodeGraphMessageHandler {
|
||||
.percentage()
|
||||
.display_decimal_places(0)
|
||||
.label("Fade Artwork")
|
||||
.tooltip("Opacity of the graph background that covers the artwork")
|
||||
.tooltip_description("Opacity of the graph background that covers the artwork.")
|
||||
.on_update(move |number_input: &NumberInput| {
|
||||
DocumentMessage::SetGraphFadeArtwork {
|
||||
percentage: number_input.value.unwrap_or(graph_fade_artwork_percentage),
|
||||
@@ -2278,8 +2283,8 @@ impl NodeGraphMessageHandler {
|
||||
TextButton::new("Node Graph")
|
||||
.icon(Some("GraphViewOpen".into()))
|
||||
.hover_icon(Some("GraphViewClosed".into()))
|
||||
.tooltip("Hide Node Graph")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
||||
.tooltip_label("Hide Node Graph")
|
||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
||||
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
||||
.widget_holder(),
|
||||
]);
|
||||
@@ -2329,10 +2334,10 @@ impl NodeGraphMessageHandler {
|
||||
properties.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
IconLabel::new("Node").tooltip("Name of the selected node").widget_holder(),
|
||||
IconLabel::new("Node").tooltip_description("Name of the selected node.").widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
TextInput::new(context.network_interface.display_name(&node_id, context.selection_network_path))
|
||||
.tooltip("Name of the selected node")
|
||||
.tooltip_description("Name of the selected node.")
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id,
|
||||
@@ -2357,10 +2362,10 @@ impl NodeGraphMessageHandler {
|
||||
let mut properties = vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
IconLabel::new("File").tooltip("Name of the current document").widget_holder(),
|
||||
IconLabel::new("File").tooltip_description("Name of the current document.").widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
TextInput::new(context.document_name)
|
||||
.tooltip("Name of the current document")
|
||||
.tooltip_description("Name of the current document.")
|
||||
.on_update(|text_input| DocumentMessage::RenameDocument { new_name: text_input.value.clone() }.into())
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
@@ -2404,10 +2409,10 @@ impl NodeGraphMessageHandler {
|
||||
let mut layer_properties = vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
IconLabel::new("Layer").tooltip("Name of the selected layer").widget_holder(),
|
||||
IconLabel::new("Layer").tooltip_description("Name of the selected layer.").widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
TextInput::new(context.network_interface.display_name(&layer, context.selection_network_path))
|
||||
.tooltip("Name of the selected layer")
|
||||
.tooltip_description("Name of the selected layer.")
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id: layer,
|
||||
@@ -2420,7 +2425,7 @@ impl NodeGraphMessageHandler {
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.tooltip("Add an operation to the end of this layer's chain of nodes")
|
||||
.tooltip_description("Add an operation to the end of this layer's chain of nodes.")
|
||||
.popover_layout({
|
||||
let compatible_type = context
|
||||
.network_interface
|
||||
@@ -2680,7 +2685,6 @@ impl NodeGraphMessageHandler {
|
||||
let data = LayerPanelEntry {
|
||||
id: node_id,
|
||||
alias: network_interface.display_name(&node_id, &[]),
|
||||
tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() },
|
||||
in_selected_network: selection_network_path.is_empty(),
|
||||
children_allowed,
|
||||
children_present: layer.has_children(network_interface.document_metadata()),
|
||||
|
||||
@@ -50,10 +50,10 @@ pub fn expose_widget(node_id: NodeId, index: usize, data_type: FrontendGraphData
|
||||
ParameterExposeButton::new()
|
||||
.exposed(exposed)
|
||||
.data_type(data_type)
|
||||
.tooltip(if exposed {
|
||||
"Stop exposing this parameter as a node input in the graph"
|
||||
.tooltip_description(if exposed {
|
||||
"Stop exposing this parameter as a node input in the graph."
|
||||
} else {
|
||||
"Expose this parameter as a node input in the graph"
|
||||
"Expose this parameter as a node input in the graph."
|
||||
})
|
||||
.on_update(move |_parameter| Message::Batched {
|
||||
messages: Box::new([NodeGraphMessage::ExposeInput {
|
||||
@@ -97,12 +97,11 @@ pub fn start_widgets(parameter_widgets_info: ParameterWidgetsInfo) -> Vec<Widget
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return vec![];
|
||||
};
|
||||
let description = if description != "TODO" { description } else { String::new() };
|
||||
let mut widgets = Vec::with_capacity(6);
|
||||
if exposable {
|
||||
widgets.push(expose_widget(node_id, index, input_type, input.is_exposed()));
|
||||
}
|
||||
widgets.push(TextLabel::new(name).tooltip(description).widget_holder());
|
||||
widgets.push(TextLabel::new(name).tooltip_description(description).widget_holder());
|
||||
if blank_assist {
|
||||
add_blank_assist(&mut widgets);
|
||||
}
|
||||
@@ -232,16 +231,16 @@ pub(crate) fn property_from_type(
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
TextLabel::new("-")
|
||||
.tooltip(format!(
|
||||
"This data can only be supplied through the node graph because no widget exists for its type:\n\
|
||||
{}",
|
||||
// TODO: Avoid needing to remove spaces here by fixing how `alias` is generated
|
||||
.tooltip_label(format!(
|
||||
"Data Type: {}",
|
||||
concrete_type
|
||||
.alias
|
||||
.as_deref()
|
||||
// TODO: Avoid needing to remove spaces here by fixing how `alias` is generated
|
||||
.map(|s| s.to_string().replace(" ", ""))
|
||||
.unwrap_or_else(|| graphene_std::format_type(concrete_type.name.as_ref()))
|
||||
.unwrap_or_else(|| graphene_std::format_type(concrete_type.name.as_ref())),
|
||||
))
|
||||
.tooltip_description("This data can only be supplied through the node graph because no widget exists for its type.")
|
||||
.widget_holder(),
|
||||
]);
|
||||
return Err(vec![widgets.into()]);
|
||||
@@ -961,7 +960,7 @@ pub fn blend_mode_widget(parameter_widgets_info: ParameterWidgetsInfo) -> Layout
|
||||
.widget_holder(),
|
||||
]);
|
||||
}
|
||||
LayoutGroup::Row { widgets }.with_tooltip("Formula used for blending")
|
||||
LayoutGroup::Row { widgets }.with_tooltip_description("Formula used for blending.")
|
||||
}
|
||||
|
||||
pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button: ColorInput) -> LayoutGroup {
|
||||
@@ -1400,12 +1399,12 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon
|
||||
widgets
|
||||
}
|
||||
|
||||
pub(crate) const SAMPLE_POLYLINE_TOOLTIP_SPACING: &str = "Use a point sampling density controlled by a distance between, or specific number of, points.";
|
||||
pub(crate) const SAMPLE_POLYLINE_TOOLTIP_SEPARATION: &str = "Distance between each instance (exact if 'Adaptive Spacing' is disabled, approximate if enabled).";
|
||||
pub(crate) const SAMPLE_POLYLINE_TOOLTIP_QUANTITY: &str = "Number of points to place along the path.";
|
||||
pub(crate) const SAMPLE_POLYLINE_TOOLTIP_START_OFFSET: &str = "Exclude some distance from the start of the path before the first instance.";
|
||||
pub(crate) const SAMPLE_POLYLINE_TOOLTIP_STOP_OFFSET: &str = "Exclude some distance from the end of the path after the last instance.";
|
||||
pub(crate) const SAMPLE_POLYLINE_TOOLTIP_ADAPTIVE_SPACING: &str = "Round 'Separation' to a nearby value that divides into the path length evenly.";
|
||||
pub(crate) const SAMPLE_POLYLINE_DESCRIPTION_SPACING: &str = "Use a point sampling density controlled by a distance between, or specific number of, points.";
|
||||
pub(crate) const SAMPLE_POLYLINE_DESCRIPTION_SEPARATION: &str = "Distance between each instance (exact if 'Adaptive Spacing' is disabled, approximate if enabled).";
|
||||
pub(crate) const SAMPLE_POLYLINE_DESCRIPTION_QUANTITY: &str = "Number of points to place along the path.";
|
||||
pub(crate) const SAMPLE_POLYLINE_DESCRIPTION_START_OFFSET: &str = "Exclude some distance from the start of the path before the first instance.";
|
||||
pub(crate) const SAMPLE_POLYLINE_DESCRIPTION_STOP_OFFSET: &str = "Exclude some distance from the end of the path after the last instance.";
|
||||
pub(crate) const SAMPLE_POLYLINE_DESCRIPTION_ADAPTIVE_SPACING: &str = "Round 'Separation' to a nearby value that divides into the path length evenly.";
|
||||
|
||||
pub(crate) fn sample_polyline_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
use graphene_std::vector::sample_polyline::*;
|
||||
@@ -1434,15 +1433,15 @@ pub(crate) fn sample_polyline_properties(node_id: NodeId, context: &mut NodeProp
|
||||
);
|
||||
|
||||
vec![
|
||||
spacing.with_tooltip(SAMPLE_POLYLINE_TOOLTIP_SPACING),
|
||||
spacing.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_SPACING),
|
||||
match current_spacing {
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Separation)) => LayoutGroup::Row { widgets: separation }.with_tooltip(SAMPLE_POLYLINE_TOOLTIP_SEPARATION),
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Quantity)) => LayoutGroup::Row { widgets: quantity }.with_tooltip(SAMPLE_POLYLINE_TOOLTIP_QUANTITY),
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Separation)) => LayoutGroup::Row { widgets: separation }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_SEPARATION),
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Quantity)) => LayoutGroup::Row { widgets: quantity }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_QUANTITY),
|
||||
_ => LayoutGroup::Row { widgets: vec![] },
|
||||
},
|
||||
LayoutGroup::Row { widgets: start_offset }.with_tooltip(SAMPLE_POLYLINE_TOOLTIP_START_OFFSET),
|
||||
LayoutGroup::Row { widgets: stop_offset }.with_tooltip(SAMPLE_POLYLINE_TOOLTIP_STOP_OFFSET),
|
||||
LayoutGroup::Row { widgets: adaptive_spacing }.with_tooltip(SAMPLE_POLYLINE_TOOLTIP_ADAPTIVE_SPACING),
|
||||
LayoutGroup::Row { widgets: start_offset }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_START_OFFSET),
|
||||
LayoutGroup::Row { widgets: stop_offset }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_STOP_OFFSET),
|
||||
LayoutGroup::Row { widgets: adaptive_spacing }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_ADAPTIVE_SPACING),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1776,7 +1775,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
Fill::Solid(_) | Fill::None => add_blank_assist(&mut row),
|
||||
Fill::Gradient(gradient) => {
|
||||
let reverse_button = IconButton::new("Reverse", 24)
|
||||
.tooltip("Reverse the gradient color stops")
|
||||
.tooltip_description("Reverse the gradient color stops.")
|
||||
.on_update(update_value(
|
||||
{
|
||||
let gradient = gradient.clone();
|
||||
@@ -1826,7 +1825,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
(gradient.start.x + gradient.start.y) < (gradient.end.x + gradient.end.y)
|
||||
};
|
||||
let reverse_radial_gradient_button = IconButton::new(if orientation { "ReverseRadialGradientToRight" } else { "ReverseRadialGradientToLeft" }, 24)
|
||||
.tooltip("Reverse which end the gradient radiates from")
|
||||
.tooltip_description("Reverse which end the gradient radiates from.")
|
||||
.on_update(update_value(
|
||||
{
|
||||
let gradient = gradient.clone();
|
||||
@@ -2027,9 +2026,9 @@ pub fn math_properties(node_id: NodeId, context: &mut NodePropertiesContext) ->
|
||||
let operand_a_hint = vec![TextLabel::new("(Operand A is the primary input)").widget_holder()];
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: expression }.with_tooltip(r#"A math expression that may incorporate "A" and/or "B", such as "sqrt(A + B) - B^2""#),
|
||||
LayoutGroup::Row { widgets: operand_b }.with_tooltip(r#"The value of "B" when calculating the expression"#),
|
||||
LayoutGroup::Row { widgets: operand_a_hint }.with_tooltip(r#""A" is fed by the value from the previous node in the primary data flow, or it is 0 if disconnected"#),
|
||||
LayoutGroup::Row { widgets: expression }.with_tooltip_description(r#"A math expression that may incorporate "A" and/or "B", such as "sqrt(A + B) - B^2"."#),
|
||||
LayoutGroup::Row { widgets: operand_b }.with_tooltip_description(r#"The value of "B" when calculating the expression."#),
|
||||
LayoutGroup::Row { widgets: operand_a_hint }.with_tooltip_description(r#""A" is fed by the value from the previous node in the primary data flow, or it is 0 if disconnected."#),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2149,13 +2148,12 @@ pub mod choice {
|
||||
.map(|(item, var_meta)| {
|
||||
let updater = updater_factory();
|
||||
let committer = committer_factory();
|
||||
let entry = RadioEntryData::new(var_meta.name).on_update(move |_| updater(item)).on_commit(committer);
|
||||
match (var_meta.icon, var_meta.docstring) {
|
||||
(None, None) => entry.label(var_meta.label),
|
||||
(None, Some(doc)) => entry.label(var_meta.label).tooltip(doc),
|
||||
(Some(icon), None) => entry.icon(icon).tooltip(var_meta.label),
|
||||
(Some(icon), Some(doc)) => entry.icon(icon).tooltip(format!("{}\n\n{}", var_meta.label, doc)),
|
||||
}
|
||||
let entry = RadioEntryData::new(var_meta.name)
|
||||
.on_update(move |_| updater(item))
|
||||
.on_commit(committer)
|
||||
.tooltip_label(var_meta.label)
|
||||
.tooltip_description(var_meta.description.unwrap_or_default());
|
||||
if let Some(icon) = var_meta.icon { entry.icon(icon) } else { entry.label(var_meta.label) }
|
||||
})
|
||||
.collect();
|
||||
RadioInput::new(items).selected_index(Some(current.as_u32())).widget_holder()
|
||||
@@ -2229,7 +2227,7 @@ pub mod choice {
|
||||
|
||||
let mut row = LayoutGroup::Row { widgets };
|
||||
if let Some(desc) = self.widget_factory.description() {
|
||||
row = row.with_tooltip(desc);
|
||||
row = row.with_tooltip_label(desc);
|
||||
}
|
||||
row
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ pub struct FrontendGraphInput {
|
||||
#[serde(rename = "validTypes")]
|
||||
pub valid_types: Vec<String>,
|
||||
#[serde(rename = "connectedTo")]
|
||||
/// Either "nothing", "import index {index}", or "{node name} output {output_index}".
|
||||
/// Either "nothing", "import #{index}", or "{node name} #{output_index}".
|
||||
pub connected_to: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -276,14 +276,14 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
||||
color_widgets.extend([
|
||||
CheckboxInput::new(grid.dot_display)
|
||||
.icon("GridDotted")
|
||||
.tooltip("Display as dotted grid")
|
||||
.tooltip_label("Display as Dotted Grid")
|
||||
.on_update(update_display(grid, |grid| Some(&mut grid.dot_display)))
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
]);
|
||||
color_widgets.push(
|
||||
ColorInput::new(FillChoice::Solid(grid.grid_color.to_gamma_srgb()))
|
||||
.tooltip("Grid display color")
|
||||
.tooltip_label("Grid Display Color")
|
||||
.allow_none(false)
|
||||
.on_update(update_color(grid, |grid| Some(&mut grid.grid_color)))
|
||||
.widget_holder(),
|
||||
|
||||
@@ -386,55 +386,55 @@ pub const SNAP_FUNCTIONS_FOR_BOUNDING_BOXES: [(&str, GetSnapState, &str); 5] = [
|
||||
(
|
||||
"Align with Edges",
|
||||
(|snapping_state| &mut snapping_state.bounding_box.align_with_edges) as GetSnapState,
|
||||
"Snaps to horizontal/vertical alignment with the edges of any layer's bounding box",
|
||||
"Snaps to horizontal/vertical alignment with the edges of any layer's bounding box.",
|
||||
),
|
||||
(
|
||||
"Corner Points",
|
||||
(|snapping_state| &mut snapping_state.bounding_box.corner_point) as GetSnapState,
|
||||
"Snaps to the four corners of any layer's bounding box",
|
||||
"Snaps to the four corners of any layer's bounding box.",
|
||||
),
|
||||
(
|
||||
"Center Points",
|
||||
(|snapping_state| &mut snapping_state.bounding_box.center_point) as GetSnapState,
|
||||
"Snaps to the center point of any layer's bounding box",
|
||||
"Snaps to the center point of any layer's bounding box.",
|
||||
),
|
||||
(
|
||||
"Edge Midpoints",
|
||||
(|snapping_state| &mut snapping_state.bounding_box.edge_midpoint) as GetSnapState,
|
||||
"Snaps to any of the four points at the middle of the edges of any layer's bounding box",
|
||||
"Snaps to any of the four points at the middle of the edges of any layer's bounding box.",
|
||||
),
|
||||
(
|
||||
"Distribute Evenly",
|
||||
(|snapping_state| &mut snapping_state.bounding_box.distribute_evenly) as GetSnapState,
|
||||
"Snaps to a consistent distance offset established by the bounding boxes of nearby layers",
|
||||
"Snaps to a consistent distance offset established by the bounding boxes of nearby layers.",
|
||||
),
|
||||
];
|
||||
pub const SNAP_FUNCTIONS_FOR_PATHS: [(&str, GetSnapState, &str); 7] = [
|
||||
(
|
||||
"Align with Anchor Points",
|
||||
(|snapping_state: &mut SnappingState| &mut snapping_state.path.align_with_anchor_point) as GetSnapState,
|
||||
"Snaps to horizontal/vertical alignment with the anchor points of any vector path",
|
||||
"Snaps to horizontal/vertical alignment with the anchor points of any vector path.",
|
||||
),
|
||||
(
|
||||
"Anchor Points",
|
||||
(|snapping_state: &mut SnappingState| &mut snapping_state.path.anchor_point) as GetSnapState,
|
||||
"Snaps to the anchor point of any vector path",
|
||||
"Snaps to the anchor point of any vector path.",
|
||||
),
|
||||
(
|
||||
// TODO: Extend to the midpoints of curved segments and rename to "Segment Midpoint"
|
||||
"Line Midpoints",
|
||||
(|snapping_state: &mut SnappingState| &mut snapping_state.path.line_midpoint) as GetSnapState,
|
||||
"Snaps to the point at the middle of any straight line segment of a vector path",
|
||||
"Snaps to the point at the middle of any straight line segment of a vector path.",
|
||||
),
|
||||
(
|
||||
"Path Intersection Points",
|
||||
(|snapping_state: &mut SnappingState| &mut snapping_state.path.path_intersection_point) as GetSnapState,
|
||||
"Snaps to any points where vector paths intersect",
|
||||
"Snaps to any points where vector paths intersect.",
|
||||
),
|
||||
(
|
||||
"Along Paths",
|
||||
(|snapping_state: &mut SnappingState| &mut snapping_state.path.along_path) as GetSnapState,
|
||||
"Snaps along the length of any vector path",
|
||||
"Snaps along the length of any vector path.",
|
||||
),
|
||||
(
|
||||
// TODO: This works correctly for line segments, but not curved segments.
|
||||
@@ -442,7 +442,7 @@ pub const SNAP_FUNCTIONS_FOR_PATHS: [(&str, GetSnapState, &str); 7] = [
|
||||
"Normal to Paths",
|
||||
(|snapping_state: &mut SnappingState| &mut snapping_state.path.normal_to_path) as GetSnapState,
|
||||
// TODO: Fix the bug/limitation that requires 'Intersections of Paths' to be enabled
|
||||
"Snaps a line to a point perpendicular to a vector path\n(due to a bug, 'Intersections of Paths' must be enabled)",
|
||||
"Snaps a line to a point perpendicular to a vector path.\n(Due to a bug, 'Intersections of Paths' must be enabled.)",
|
||||
),
|
||||
(
|
||||
// TODO: This works correctly for line segments, but not curved segments.
|
||||
@@ -450,7 +450,7 @@ pub const SNAP_FUNCTIONS_FOR_PATHS: [(&str, GetSnapState, &str); 7] = [
|
||||
"Tangent to Paths",
|
||||
(|snapping_state: &mut SnappingState| &mut snapping_state.path.tangent_to_path) as GetSnapState,
|
||||
// TODO: Fix the bug/limitation that requires 'Intersections of Paths' to be enabled
|
||||
"Snaps a line to a point tangent to a vector path\n(due to a bug, 'Intersections of Paths' must be enabled)",
|
||||
"Snaps a line to a point tangent to a vector path.\n(Due to a bug, 'Intersections of Paths' must be enabled.)",
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
@@ -631,15 +631,12 @@ impl NodeNetworkInterface {
|
||||
.upstream_output_connector(input_connector, network_path)
|
||||
.map(|output_connector| match output_connector {
|
||||
OutputConnector::Node { node_id, output_index } => {
|
||||
let mut name = self.display_name(&node_id, network_path);
|
||||
if cfg!(debug_assertions) {
|
||||
name.push_str(&format!(" (id: {node_id})"));
|
||||
}
|
||||
format!("{name} output {output_index}")
|
||||
let name = self.display_name(&node_id, network_path);
|
||||
format!("Connected to output #{output_index} of \"{name}\", ID: {node_id}.")
|
||||
}
|
||||
OutputConnector::Import(import_index) => format!("Import index {import_index}"),
|
||||
OutputConnector::Import(import_index) => format!("Connected to import #{import_index}."),
|
||||
})
|
||||
.unwrap_or("nothing".to_string());
|
||||
.unwrap_or("Connected to nothing.".to_string());
|
||||
|
||||
let (name, description) = match input_connector {
|
||||
InputConnector::Node { node_id, input_index } => self.displayed_input_name_and_description(node_id, *input_index, network_path),
|
||||
@@ -659,7 +656,7 @@ impl NodeNetworkInterface {
|
||||
} else if let Some(export_type_name) = input_type.compiled_nested_type().map(|nested| nested.to_string()) {
|
||||
export_type_name
|
||||
} else {
|
||||
format!("Export index {}", export_index)
|
||||
format!("Export #{}", export_index)
|
||||
};
|
||||
|
||||
(export_name, String::new())
|
||||
@@ -709,7 +706,7 @@ impl NodeNetworkInterface {
|
||||
} else if let Some(import_type_name) = output_type.compiled_nested_type().map(|nested| nested.to_string()) {
|
||||
import_type_name
|
||||
} else {
|
||||
format!("Import index {}", import_index)
|
||||
format!("Import #{}", import_index)
|
||||
};
|
||||
|
||||
(import_name, description)
|
||||
@@ -724,19 +721,16 @@ impl NodeNetworkInterface {
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|input| match input {
|
||||
InputConnector::Node { node_id, input_index } => {
|
||||
let mut name = self.display_name(node_id, network_path);
|
||||
if cfg!(debug_assertions) {
|
||||
name.push_str(&format!(" (id: {node_id})"));
|
||||
}
|
||||
format!("{name} input {input_index}")
|
||||
&InputConnector::Node { node_id, input_index } => {
|
||||
let name = self.display_name(&node_id, network_path);
|
||||
format!("Connected to input #{input_index} of \"{name}\", ID: {node_id}.")
|
||||
}
|
||||
InputConnector::Export(export_index) => format!("Export index {export_index}"),
|
||||
InputConnector::Export(export_index) => format!("Connected to export #{export_index}."),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if connected_to.is_empty() {
|
||||
connected_to.push("nothing".to_string());
|
||||
connected_to.push("Connected to nothing.".to_string());
|
||||
}
|
||||
|
||||
Some(FrontendGraphOutput {
|
||||
@@ -1014,7 +1008,6 @@ impl NodeNetworkInterface {
|
||||
pub fn description(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
|
||||
self.get_node_definition(node_id, network_path)
|
||||
.map(|node_definition| node_definition.description.to_string())
|
||||
.filter(|description| description != "TODO")
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -6170,7 +6163,7 @@ pub struct InputPersistentMetadata {
|
||||
pub widget_override: Option<String>,
|
||||
/// An empty input name means to use the type as the name.
|
||||
pub input_name: String,
|
||||
/// Displayed as the tooltip.
|
||||
/// Displayed as the tooltip description.
|
||||
pub input_description: String,
|
||||
}
|
||||
|
||||
@@ -6233,8 +6226,8 @@ impl InputPersistentMetadata {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_description(mut self, tooltip: &str) -> Self {
|
||||
self.input_description = tooltip.to_string();
|
||||
pub fn with_description(mut self, description: &str) -> Self {
|
||||
self.input_description = description.to_string();
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -6314,9 +6307,9 @@ impl From<(&str, &str)> for InputMetadata {
|
||||
}
|
||||
|
||||
impl InputMetadata {
|
||||
pub fn with_name_description_override(input_name: &str, tooltip: &str, widget_override: WidgetOverride) -> Self {
|
||||
pub fn with_name_description_override(input_name: &str, description: &str, widget_override: WidgetOverride) -> Self {
|
||||
InputMetadata {
|
||||
persistent_metadata: InputPersistentMetadata::default().with_name(input_name).with_description(tooltip).with_override(widget_override),
|
||||
persistent_metadata: InputPersistentMetadata::default().with_name(input_name).with_description(description).with_override(widget_override),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ impl TypeSource {
|
||||
self.compiled_nested_type().map(|ty| format!("type:{ty}"))
|
||||
}
|
||||
|
||||
/// The type to display in the tooltip.
|
||||
/// The type to display in the tooltip label.
|
||||
pub fn resolved_type_tooltip_string(&self) -> String {
|
||||
match self {
|
||||
TypeSource::Compiled(compiled_type) => format!("Data Type: {:?}", compiled_type.nested_type().to_string()),
|
||||
|
||||
@@ -35,7 +35,6 @@ impl serde::Serialize for JsRawBuffer {
|
||||
pub struct LayerPanelEntry {
|
||||
pub id: NodeId,
|
||||
pub alias: String,
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "inSelectedNetwork")]
|
||||
pub in_selected_network: bool,
|
||||
#[serde(rename = "childrenAllowed")]
|
||||
|
||||
@@ -42,8 +42,8 @@ impl std::fmt::Display for GraphWireStyle {
|
||||
impl GraphWireStyle {
|
||||
pub fn tooltip_description(&self) -> &'static str {
|
||||
match self {
|
||||
GraphWireStyle::GridAligned => "Wires follow the grid, running in straight lines between nodes",
|
||||
GraphWireStyle::Direct => "Wires bend to run at an angle directly between nodes",
|
||||
GraphWireStyle::GridAligned => "Wires follow the grid, running in straight lines between nodes.",
|
||||
GraphWireStyle::Direct => "Wires bend to run at an angle directly between nodes.",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ impl std::fmt::Display for SelectionMode {
|
||||
impl SelectionMode {
|
||||
pub fn tooltip_description(&self) -> &'static str {
|
||||
match self {
|
||||
SelectionMode::Touched => "Select all layers at least partially covered by the dragged selection area",
|
||||
SelectionMode::Enclosed => "Select only layers fully enclosed by the dragged selection area",
|
||||
SelectionMode::Directional => r#""Touched" for leftward drags, "Enclosed" for rightward drags"#,
|
||||
SelectionMode::Touched => "Select all layers at least partially covered by the dragged selection area.",
|
||||
SelectionMode::Enclosed => "Select only layers fully enclosed by the dragged selection area.",
|
||||
SelectionMode::Directional => r#""Touched" for leftward drags, "Enclosed" for rightward drags."#,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ impl ToolColorOptions {
|
||||
} else {
|
||||
let reset = IconButton::new("CloseX", 12)
|
||||
.disabled(self.custom_color.is_none() && self.color_type == ToolColorType::Custom)
|
||||
.tooltip("Clear Color")
|
||||
.tooltip_label("Clear Color")
|
||||
.on_update(reset_callback);
|
||||
|
||||
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
|
||||
@@ -101,8 +101,8 @@ impl ToolColorOptions {
|
||||
("CustomColor", "Custom Color", ToolColorType::Custom),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(icon, tooltip, color_type)| {
|
||||
let mut entry = RadioEntryData::new(format!("{color_type:?}")).tooltip(tooltip).icon(icon);
|
||||
.map(|(icon, label, color_type)| {
|
||||
let mut entry = RadioEntryData::new(format!("{color_type:?}")).tooltip_label(label).icon(icon);
|
||||
entry.on_update = radio_callback(color_type);
|
||||
entry
|
||||
})
|
||||
|
||||
@@ -14,7 +14,8 @@ use std::fmt;
|
||||
|
||||
pub fn pin_pivot_widget(active: bool, enabled: bool, source: PivotToolSource) -> WidgetHolder {
|
||||
IconButton::new(if active { "PinActive" } else { "PinInactive" }, 24)
|
||||
.tooltip(String::from(if active { "Unpin Custom Pivot" } else { "Pin Custom Pivot" }) + "\n\nUnless pinned, the pivot will return to its prior reference point when a new selection is made.")
|
||||
.tooltip_label(if active { "Unpin Custom Pivot" } else { "Pin Custom Pivot" })
|
||||
.tooltip_description("Unless pinned, the pivot will return to its prior reference point when a new selection is made.")
|
||||
.disabled(!enabled)
|
||||
.on_update(move |_| match source {
|
||||
PivotToolSource::Select => SelectToolMessage::SelectOptions {
|
||||
@@ -31,7 +32,8 @@ pub fn pin_pivot_widget(active: bool, enabled: bool, source: PivotToolSource) ->
|
||||
|
||||
pub fn pivot_reference_point_widget(disabled: bool, reference_point: ReferencePoint, source: PivotToolSource) -> WidgetHolder {
|
||||
ReferencePointInput::new(reference_point)
|
||||
.tooltip("Custom Pivot Reference Point\n\nPlaces the pivot at a corner, edge, or center of the selection bounds, unless it is dragged elsewhere.")
|
||||
.tooltip_label("Custom Pivot Reference Point")
|
||||
.tooltip_description("Places the pivot at a corner, edge, or center of the selection bounds, unless it is dragged elsewhere.")
|
||||
.disabled(disabled)
|
||||
.on_update(move |pivot_input: &ReferencePointInput| match source {
|
||||
PivotToolSource::Select => SelectToolMessage::SetPivot { position: pivot_input.value }.into(),
|
||||
@@ -62,11 +64,13 @@ pub fn pivot_gizmo_type_widget(state: PivotGizmoState, source: PivotToolSource)
|
||||
|
||||
vec![
|
||||
CheckboxInput::new(!state.disabled)
|
||||
.tooltip(
|
||||
"Pivot Gizmo\n\
|
||||
\n\
|
||||
.tooltip_label("Pivot Gizmo")
|
||||
.tooltip_description(
|
||||
"
|
||||
Enabled: the chosen gizmo type is shown and used to control rotation and scaling.\n\
|
||||
Disabled: rotation and scaling occurs about the center of the selection bounds.",
|
||||
Disabled: rotation and scaling occurs about the center of the selection bounds.
|
||||
"
|
||||
.trim(),
|
||||
)
|
||||
.on_update(move |optional_input: &CheckboxInput| match source {
|
||||
PivotToolSource::Select => SelectToolMessage::SelectOptions {
|
||||
@@ -86,14 +90,16 @@ pub fn pivot_gizmo_type_widget(state: PivotGizmoState, source: PivotToolSource)
|
||||
PivotGizmoType::Average => 1,
|
||||
PivotGizmoType::Active => 2,
|
||||
}))
|
||||
.tooltip(
|
||||
"Pivot Gizmo Type\n\
|
||||
\n\
|
||||
.tooltip_label("Pivot Gizmo Type")
|
||||
.tooltip_description(
|
||||
"
|
||||
Selects which gizmo type is shown and used as the center of rotation/scaling transformations.\n\
|
||||
\n\
|
||||
Custom Pivot: rotates and scales relative to the selection bounds, or elsewhere if dragged.\n\
|
||||
Origin (Average Point): rotates and scales about the average point of all selected layer origins.\n\
|
||||
Origin (Active Object): rotates and scales about the origin of the most recently selected layer.",
|
||||
Origin (Active Object): rotates and scales about the origin of the most recently selected layer.
|
||||
"
|
||||
.trim(),
|
||||
)
|
||||
.disabled(state.disabled)
|
||||
.widget_holder(),
|
||||
|
||||
@@ -52,7 +52,7 @@ impl ShapeType {
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn tooltip(&self) -> String {
|
||||
pub fn tooltip_label(&self) -> String {
|
||||
(match self {
|
||||
Self::Line => "Line Tool",
|
||||
Self::Rectangle => "Rectangle Tool",
|
||||
@@ -62,6 +62,14 @@ impl ShapeType {
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn tooltip_description(&self) -> String {
|
||||
(match self {
|
||||
// TODO: Add descriptions to all the shape tools
|
||||
_ => "",
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn icon_name(&self) -> String {
|
||||
(match self {
|
||||
Self::Line => "VectorLineTool",
|
||||
|
||||
@@ -42,7 +42,7 @@ impl ToolMetadata for ArtboardTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"GeneralArtboardTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Artboard Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
|
||||
@@ -90,7 +90,7 @@ impl ToolMetadata for BrushTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"RasterBrushTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Brush Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
@@ -215,7 +215,7 @@ impl LayoutHolder for BrushTool {
|
||||
widgets.push(
|
||||
DropdownInput::new(blend_mode_entries)
|
||||
.selected_index(self.options.blend_mode.index_in_list().map(|index| index as u32))
|
||||
.tooltip("The blend mode used with the background when performing a brush stroke. Only used in draw mode.")
|
||||
.tooltip_description("The blend mode used with the background when performing a brush stroke. Only used in draw mode.")
|
||||
.disabled(self.options.draw_mode != DrawMode::Draw)
|
||||
.widget_holder(),
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ impl ToolMetadata for EyedropperTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"GeneralEyedropperTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Eyedropper Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
|
||||
@@ -27,7 +27,7 @@ impl ToolMetadata for FillTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"GeneralFillTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Fill Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
|
||||
@@ -72,7 +72,7 @@ impl ToolMetadata for FreehandTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"VectorFreehandTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Freehand Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
|
||||
@@ -45,7 +45,7 @@ impl ToolMetadata for GradientTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"GeneralGradientTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Gradient Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
@@ -91,13 +91,13 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Grad
|
||||
impl LayoutHolder for GradientTool {
|
||||
fn layout(&self) -> Layout {
|
||||
let gradient_type = RadioInput::new(vec![
|
||||
RadioEntryData::new("Linear").label("Linear").tooltip("Linear gradient").on_update(move |_| {
|
||||
RadioEntryData::new("Linear").label("Linear").tooltip_label("Linear Gradient").on_update(move |_| {
|
||||
GradientToolMessage::UpdateOptions {
|
||||
options: GradientOptionsUpdate::Type(GradientType::Linear),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
RadioEntryData::new("Radial").label("Radial").tooltip("Radial gradient").on_update(move |_| {
|
||||
RadioEntryData::new("Radial").label("Radial").tooltip_label("Radial Gradient").on_update(move |_| {
|
||||
GradientToolMessage::UpdateOptions {
|
||||
options: GradientOptionsUpdate::Type(GradientType::Radial),
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ impl ToolMetadata for NavigateTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"GeneralNavigateTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Navigate Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::consts::{
|
||||
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GRAY, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, DEFAULT_STROKE_WIDTH, DOUBLE_CLICK_MILLISECONDS, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD,
|
||||
DRILL_THROUGH_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, SEGMENT_INSERTION_DISTANCE, SEGMENT_OVERLAY_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE,
|
||||
};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
|
||||
use crate::messages::portfolio::document::overlays::utility_functions::{path_overlays, selected_segments};
|
||||
@@ -184,7 +185,7 @@ impl ToolMetadata for PathTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"VectorPathTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Path Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
@@ -235,7 +236,7 @@ impl LayoutHolder for PathTool {
|
||||
let related_seperator = Separator::new(SeparatorType::Related).widget_holder();
|
||||
let unrelated_seperator = Separator::new(SeparatorType::Unrelated).widget_holder();
|
||||
|
||||
let colinear_handles_tooltip = "Keep both handles unbent, each 180° apart, when moving either";
|
||||
let colinear_handles_description = "Keep both handles unbent, each 180° apart, when moving either.";
|
||||
let colinear_handles_state = manipulator_angle.and_then(|angle| match angle {
|
||||
ManipulatorAngle::Colinear => Some(true),
|
||||
ManipulatorAngle::Free => Some(false),
|
||||
@@ -253,32 +254,39 @@ impl LayoutHolder for PathTool {
|
||||
PathToolMessage::ManipulatorMakeHandlesFree.into()
|
||||
}
|
||||
})
|
||||
.tooltip(colinear_handles_tooltip)
|
||||
.tooltip_label("Colinear Handles")
|
||||
.tooltip_description(colinear_handles_description)
|
||||
.for_label(checkbox_id)
|
||||
.widget_holder();
|
||||
let colinear_handles_label = TextLabel::new("Colinear Handles")
|
||||
.disabled(!self.tool_data.can_toggle_colinearity)
|
||||
.tooltip(colinear_handles_tooltip)
|
||||
.tooltip_label("Colinear Handles")
|
||||
.tooltip_description(colinear_handles_description)
|
||||
.for_checkbox(checkbox_id)
|
||||
.widget_holder();
|
||||
|
||||
let point_editing_mode = CheckboxInput::new(self.options.path_editing_mode.point_editing_mode)
|
||||
// TODO(Keavon): Replace with a real icon
|
||||
.icon("Dot")
|
||||
.tooltip("Point Editing Mode\n\nShift + click to select both modes.")
|
||||
.tooltip_label("Point Editing Mode")
|
||||
.tooltip_description("To multi-select modes, perform the shortcut shown.")
|
||||
.tooltip_shortcut(KeysGroup(vec![Key::Shift, Key::MouseLeft]).to_string())
|
||||
.on_update(|_| PathToolMessage::TogglePointEditing.into())
|
||||
.widget_holder();
|
||||
let segment_editing_mode = CheckboxInput::new(self.options.path_editing_mode.segment_editing_mode)
|
||||
// TODO(Keavon): Replace with a real icon
|
||||
.icon("Remove")
|
||||
.tooltip("Segment Editing Mode\n\nShift + click to select both modes.")
|
||||
.tooltip_label("Segment Editing Mode")
|
||||
.tooltip_description("To multi-select modes, perform the shortcut shown.")
|
||||
.tooltip_shortcut(KeysGroup(vec![Key::Shift, Key::MouseLeft]).to_string())
|
||||
.on_update(|_| PathToolMessage::ToggleSegmentEditing.into())
|
||||
.widget_holder();
|
||||
|
||||
let path_overlay_mode_widget = RadioInput::new(vec![
|
||||
RadioEntryData::new("all")
|
||||
.icon("HandleVisibilityAll")
|
||||
.tooltip("Show all handles regardless of selection")
|
||||
.tooltip_label("Show All Handles")
|
||||
.tooltip_description("Show all handles regardless of selection.")
|
||||
.on_update(move |_| {
|
||||
PathToolMessage::UpdateOptions {
|
||||
options: PathOptionsUpdate::OverlayModeType(PathOverlayMode::AllHandles),
|
||||
@@ -287,7 +295,8 @@ impl LayoutHolder for PathTool {
|
||||
}),
|
||||
RadioEntryData::new("selected")
|
||||
.icon("HandleVisibilitySelected")
|
||||
.tooltip("Show only handles of the segments connected to selected points")
|
||||
.tooltip_label("Show Connected Handles")
|
||||
.tooltip_description("Show only handles of the segments connected to selected points.")
|
||||
.on_update(move |_| {
|
||||
PathToolMessage::UpdateOptions {
|
||||
options: PathOptionsUpdate::OverlayModeType(PathOverlayMode::SelectedPointHandles),
|
||||
@@ -296,7 +305,8 @@ impl LayoutHolder for PathTool {
|
||||
}),
|
||||
RadioEntryData::new("frontier")
|
||||
.icon("HandleVisibilityFrontier")
|
||||
.tooltip("Show only handles at the frontiers of the segments connected to selected points")
|
||||
.tooltip_label("Show Frontier Handles")
|
||||
.tooltip_description("Show only handles at the frontiers of the segments connected to selected points.")
|
||||
.on_update(move |_| {
|
||||
PathToolMessage::UpdateOptions {
|
||||
options: PathOptionsUpdate::OverlayModeType(PathOverlayMode::FrontierHandles),
|
||||
@@ -310,7 +320,10 @@ impl LayoutHolder for PathTool {
|
||||
// Works only if a single layer is selected and its type is Vector
|
||||
let path_node_button = TextButton::new("Make Path Editable")
|
||||
.icon(Some("NodeShape".into()))
|
||||
.tooltip("Make Path Editable")
|
||||
.tooltip_label("Make Path Editable")
|
||||
.tooltip_description(
|
||||
"Enables the Pen and Path tools to directly edit layer geometry resulting from nondestructive operations. This inserts a 'Path' node as the last operation of the selected layer.",
|
||||
)
|
||||
.on_update(|_| NodeGraphMessage::AddPathNode.into())
|
||||
.disabled(!self.tool_data.make_path_editable_is_allowed)
|
||||
.widget_holder();
|
||||
|
||||
@@ -128,7 +128,7 @@ impl ToolMetadata for PenTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"VectorPenTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Pen Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
@@ -215,7 +215,8 @@ impl LayoutHolder for PenTool {
|
||||
RadioInput::new(vec![
|
||||
RadioEntryData::new("all")
|
||||
.icon("HandleVisibilityAll")
|
||||
.tooltip("Show all handles regardless of selection")
|
||||
.tooltip_label("Show All Handles")
|
||||
.tooltip_description("Show all handles regardless of selection.")
|
||||
.on_update(move |_| {
|
||||
PenToolMessage::UpdateOptions {
|
||||
options: PenOptionsUpdate::OverlayModeType(PenOverlayMode::AllHandles),
|
||||
@@ -224,7 +225,8 @@ impl LayoutHolder for PenTool {
|
||||
}),
|
||||
RadioEntryData::new("frontier")
|
||||
.icon("HandleVisibilityFrontier")
|
||||
.tooltip("Show only handles at the frontiers of the segments connected to selected points")
|
||||
.tooltip_label("Show Frontier Handles")
|
||||
.tooltip_description("Show only handles at the frontiers of the segments connected to selected points.")
|
||||
.on_update(move |_| {
|
||||
PenToolMessage::UpdateOptions {
|
||||
options: PenOptionsUpdate::OverlayModeType(PenOverlayMode::FrontierHandles),
|
||||
|
||||
@@ -122,7 +122,7 @@ impl ToolMetadata for SelectTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"GeneralSelectTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Select Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
@@ -146,10 +146,9 @@ impl SelectTool {
|
||||
|
||||
DropdownInput::new(vec![layer_selection_behavior_entries])
|
||||
.selected_index(Some((self.tool_data.nested_selection_behavior == NestedSelectionBehavior::Deepest) as u32))
|
||||
.tooltip(
|
||||
"Selection Mode\n\
|
||||
\n\
|
||||
Shallow Select: clicks initially select the least-nested layers and double clicks drill deeper into the folder hierarchy.\n\
|
||||
.tooltip_label("Selection Mode")
|
||||
.tooltip_description(
|
||||
"Shallow Select: clicks initially select the least-nested layers and double clicks drill deeper into the folder hierarchy.\n\
|
||||
Deep Select: clicks directly select the most-nested layers in the folder hierarchy.",
|
||||
)
|
||||
.widget_holder()
|
||||
@@ -160,7 +159,7 @@ impl SelectTool {
|
||||
.into_iter()
|
||||
.flat_map(|axis| [(axis, AlignAggregate::Min), (axis, AlignAggregate::Center), (axis, AlignAggregate::Max)])
|
||||
.map(move |(axis, aggregate)| {
|
||||
let (icon, tooltip) = match (axis, aggregate) {
|
||||
let (icon, label) = match (axis, aggregate) {
|
||||
(AlignAxis::X, AlignAggregate::Min) => ("AlignLeft", "Align Left"),
|
||||
(AlignAxis::X, AlignAggregate::Center) => ("AlignHorizontalCenter", "Align Horizontal Center"),
|
||||
(AlignAxis::X, AlignAggregate::Max) => ("AlignRight", "Align Right"),
|
||||
@@ -169,7 +168,7 @@ impl SelectTool {
|
||||
(AlignAxis::Y, AlignAggregate::Max) => ("AlignBottom", "Align Bottom"),
|
||||
};
|
||||
IconButton::new(icon, 24)
|
||||
.tooltip(tooltip)
|
||||
.tooltip_label(label)
|
||||
.on_update(move |_| DocumentMessage::AlignSelectedLayers { axis, aggregate }.into())
|
||||
.disabled(disabled)
|
||||
.widget_holder()
|
||||
@@ -177,21 +176,23 @@ impl SelectTool {
|
||||
}
|
||||
|
||||
fn flip_widgets(&self, disabled: bool) -> impl Iterator<Item = WidgetHolder> + use<> {
|
||||
[(FlipAxis::X, "Horizontal"), (FlipAxis::Y, "Vertical")].into_iter().map(move |(flip_axis, name)| {
|
||||
IconButton::new("Flip".to_string() + name, 24)
|
||||
.tooltip("Flip ".to_string() + name)
|
||||
.on_update(move |_| DocumentMessage::FlipSelectedLayers { flip_axis }.into())
|
||||
.disabled(disabled)
|
||||
.widget_holder()
|
||||
})
|
||||
[(FlipAxis::X, "FlipHorizontal", "Flip Horizontal"), (FlipAxis::Y, "FlipVertical", "Flip Vertical")]
|
||||
.into_iter()
|
||||
.map(move |(flip_axis, icon, label)| {
|
||||
IconButton::new(icon, 24)
|
||||
.tooltip_label(label)
|
||||
.on_update(move |_| DocumentMessage::FlipSelectedLayers { flip_axis }.into())
|
||||
.disabled(disabled)
|
||||
.widget_holder()
|
||||
})
|
||||
}
|
||||
|
||||
fn turn_widgets(&self, disabled: bool) -> impl Iterator<Item = WidgetHolder> + use<> {
|
||||
[(-90., "TurnNegative90", "Turn -90°"), (90., "TurnPositive90", "Turn 90°")]
|
||||
.into_iter()
|
||||
.map(move |(degrees, icon, name)| {
|
||||
.map(move |(degrees, icon, label)| {
|
||||
IconButton::new(icon, 24)
|
||||
.tooltip(name)
|
||||
.tooltip_label(label)
|
||||
.on_update(move |_| DocumentMessage::RotateSelectedLayers { degrees }.into())
|
||||
.disabled(disabled)
|
||||
.widget_holder()
|
||||
@@ -201,13 +202,9 @@ impl SelectTool {
|
||||
fn boolean_widgets(&self, selected_count: usize) -> impl Iterator<Item = WidgetHolder> + use<> {
|
||||
let list = <BooleanOperation as graphene_std::choice_type::ChoiceTypeStatic>::list();
|
||||
list.iter().flat_map(|i| i.iter()).map(move |(operation, info)| {
|
||||
let mut tooltip = info.label.to_string();
|
||||
if let Some(doc) = info.docstring {
|
||||
tooltip.push_str("\n\n");
|
||||
tooltip.push_str(doc);
|
||||
}
|
||||
IconButton::new(info.icon.unwrap(), 24)
|
||||
.tooltip(tooltip)
|
||||
.tooltip_label(info.label)
|
||||
.tooltip_description(info.description.unwrap_or_default())
|
||||
.disabled(selected_count == 0)
|
||||
.on_update(move |_| {
|
||||
let group_folder_type = GroupFolderType::BooleanOperation(*operation);
|
||||
|
||||
@@ -432,7 +432,7 @@ impl ToolMetadata for ShapeTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"VectorPolygonTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Shape Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> ToolType {
|
||||
|
||||
@@ -79,7 +79,7 @@ impl ToolMetadata for SplineTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"VectorSplineTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Spline Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
|
||||
@@ -88,7 +88,7 @@ impl ToolMetadata for TextTool {
|
||||
fn icon_name(&self) -> String {
|
||||
"VectorTextTool".into()
|
||||
}
|
||||
fn tooltip(&self) -> String {
|
||||
fn tooltip_label(&self) -> String {
|
||||
"Text Tool".into()
|
||||
}
|
||||
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
|
||||
|
||||
@@ -124,13 +124,13 @@ impl DocumentToolData {
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
IconButton::new("SwapVertical", 16)
|
||||
.tooltip("Swap")
|
||||
.tooltip_shortcut(action_keys!(ToolMessageDiscriminant::SwapColors))
|
||||
.tooltip_label("Swap")
|
||||
.shortcut_keys(action_keys!(ToolMessageDiscriminant::SwapColors))
|
||||
.on_update(|_| ToolMessage::SwapColors.into())
|
||||
.widget_holder(),
|
||||
IconButton::new("WorkingColors", 16)
|
||||
.tooltip("Reset")
|
||||
.tooltip_shortcut(action_keys!(ToolMessageDiscriminant::ResetColors))
|
||||
.tooltip_label("Reset")
|
||||
.shortcut_keys(action_keys!(ToolMessageDiscriminant::ResetColors))
|
||||
.on_update(|_| ToolMessage::ResetColors.into())
|
||||
.widget_holder(),
|
||||
],
|
||||
@@ -201,7 +201,11 @@ pub trait ToolTransition {
|
||||
|
||||
pub trait ToolMetadata {
|
||||
fn icon_name(&self) -> String;
|
||||
fn tooltip(&self) -> String;
|
||||
fn tooltip_label(&self) -> String;
|
||||
fn tooltip_description(&self) -> String {
|
||||
// TODO: Remove this to make tool descriptions mandatory once we've written them all
|
||||
String::new()
|
||||
}
|
||||
fn tool_type(&self) -> ToolType;
|
||||
}
|
||||
|
||||
@@ -240,12 +244,13 @@ impl LayoutHolder for ToolData {
|
||||
match tool_availability {
|
||||
ToolAvailability::Available(tool) =>
|
||||
ToolEntry::new(tool.tool_type(), tool.icon_name())
|
||||
.tooltip(tool.tooltip())
|
||||
.tooltip_shortcut(action_keys!(tool_type_to_activate_tool_message(tool.tool_type()))),
|
||||
.tooltip_label(tool.tooltip_label())
|
||||
.shortcut_keys(action_keys!(tool_type_to_activate_tool_message(tool.tool_type()))),
|
||||
ToolAvailability::AvailableAsShape(shape) =>
|
||||
ToolEntry::new(shape.tool_type(), shape.icon_name())
|
||||
.tooltip(shape.tooltip())
|
||||
.tooltip_shortcut(action_keys!(tool_type_to_activate_tool_message(shape.tool_type()))),
|
||||
.tooltip_label(shape.tooltip_label())
|
||||
.tooltip_description(shape.tooltip_description())
|
||||
.shortcut_keys(action_keys!(tool_type_to_activate_tool_message(shape.tool_type()))),
|
||||
ToolAvailability::ComingSoon(tool) => tool.clone(),
|
||||
}
|
||||
})
|
||||
@@ -253,15 +258,19 @@ impl LayoutHolder for ToolData {
|
||||
)
|
||||
.flat_map(|group| {
|
||||
let separator = std::iter::once(Separator::new(SeparatorType::Section).direction(SeparatorDirection::Vertical).widget_holder());
|
||||
let buttons = group.into_iter().map(|ToolEntry { tooltip, tooltip_shortcut, tool_type, icon_name }| {
|
||||
let buttons = group.into_iter().map(|ToolEntry { tooltip_label, tooltip_description, tooltip_shortcut, shortcut_keys, tool_type, icon_name }| {
|
||||
let coming_soon = tooltip_description.contains("Coming soon.");
|
||||
|
||||
IconButton::new(icon_name, 32)
|
||||
.disabled(false)
|
||||
.active(match tool_type {
|
||||
ToolType::Line | ToolType::Ellipse | ToolType::Rectangle => { self.active_shape_type.is_some() && active_tool == tool_type }
|
||||
_ => active_tool == tool_type,
|
||||
})
|
||||
.tooltip(tooltip.clone())
|
||||
.tooltip_label(tooltip_label.clone())
|
||||
.tooltip_description(tooltip_description)
|
||||
.tooltip_shortcut(tooltip_shortcut)
|
||||
.shortcut_keys(shortcut_keys)
|
||||
.on_update(move |_| {
|
||||
match tool_type {
|
||||
ToolType::Line => ToolMessage::ActivateToolShapeLine.into(),
|
||||
@@ -269,7 +278,7 @@ impl LayoutHolder for ToolData {
|
||||
ToolType::Ellipse => ToolMessage::ActivateToolShapeEllipse.into(),
|
||||
ToolType::Shape => ToolMessage::ActivateToolShape.into(),
|
||||
_ => {
|
||||
if !tooltip.contains("Coming Soon") { (ToolMessage::ActivateTool { tool_type }).into() } else { (DialogMessage::RequestComingSoonDialog { issue: None }).into() }
|
||||
if !coming_soon { (ToolMessage::ActivateTool { tool_type }).into() } else { (DialogMessage::RequestComingSoonDialog { issue: None }).into() }
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -295,8 +304,10 @@ pub struct ToolEntry {
|
||||
pub tool_type: ToolType,
|
||||
#[widget_builder(constructor)]
|
||||
pub icon_name: String,
|
||||
pub tooltip: String,
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub tooltip_label: String,
|
||||
pub tooltip_description: String,
|
||||
pub tooltip_shortcut: String,
|
||||
pub shortcut_keys: Option<ActionKeys>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -414,11 +425,31 @@ fn list_tools_in_groups() -> Vec<Vec<ToolAvailability>> {
|
||||
vec![
|
||||
// Raster tool group
|
||||
ToolAvailability::Available(Box::<brush_tool::BrushTool>::default()),
|
||||
ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Heal, "RasterHealTool").tooltip("Coming Soon: Heal Tool (J)")),
|
||||
ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Clone, "RasterCloneTool").tooltip("Coming Soon: Clone Tool (C)")),
|
||||
ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Patch, "RasterPatchTool").tooltip("Coming Soon: Patch Tool")),
|
||||
ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Detail, "RasterDetailTool").tooltip("Coming Soon: Detail Tool (D)")),
|
||||
ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Relight, "RasterRelightTool").tooltip("Coming Soon: Relight Tool (O)")),
|
||||
ToolAvailability::ComingSoon(
|
||||
ToolEntry::new(ToolType::Heal, "RasterHealTool")
|
||||
.tooltip_label("Heal Tool")
|
||||
.tooltip_description("Coming soon.")
|
||||
.tooltip_shortcut(Key::KeyJ.to_string()),
|
||||
),
|
||||
ToolAvailability::ComingSoon(
|
||||
ToolEntry::new(ToolType::Clone, "RasterCloneTool")
|
||||
.tooltip_label("Clone Tool")
|
||||
.tooltip_description("Coming soon.")
|
||||
.tooltip_shortcut(Key::KeyC.to_string()),
|
||||
),
|
||||
ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Patch, "RasterPatchTool").tooltip_label("Patch Tool").tooltip_description("Coming soon.")),
|
||||
ToolAvailability::ComingSoon(
|
||||
ToolEntry::new(ToolType::Detail, "RasterDetailTool")
|
||||
.tooltip_label("Detail Tool")
|
||||
.tooltip_description("Coming soon.")
|
||||
.tooltip_shortcut(Key::KeyD.to_string()),
|
||||
),
|
||||
ToolAvailability::ComingSoon(
|
||||
ToolEntry::new(ToolType::Relight, "RasterRelightTool")
|
||||
.tooltip_label("Relight Tool")
|
||||
.tooltip_description("Coming soon.")
|
||||
.tooltip_shortcut(Key::KeyO.to_string()),
|
||||
),
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user