mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 06:18: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:
@@ -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