Switch to the widget builder pattern on all remaining layouts (#1346)
* Prefer widget builder pattern * Nits --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
40f9a7d051
commit
de27f2c006
|
|
@ -4791,8 +4791,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "specta"
|
||||
version = "1.0.4"
|
||||
source = "git+https://github.com/0HyperCube/specta.git?rev=d28cd72ea4ea2f6eaa85e0622d3a98340dbb43d3#d28cd72ea4ea2f6eaa85e0622d3a98340dbb43d3"
|
||||
version = "1.0.5"
|
||||
source = "git+https://github.com/0HyperCube/specta.git?rev=c47a22b4c0863d27bc47529f300de3969480c66d#c47a22b4c0863d27bc47529f300de3969480c66d"
|
||||
dependencies = [
|
||||
"document-features",
|
||||
"glam",
|
||||
|
|
@ -4807,8 +4807,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "specta-macros"
|
||||
version = "1.0.4"
|
||||
source = "git+https://github.com/0HyperCube/specta.git?rev=d28cd72ea4ea2f6eaa85e0622d3a98340dbb43d3#d28cd72ea4ea2f6eaa85e0622d3a98340dbb43d3"
|
||||
version = "1.0.5"
|
||||
source = "git+https://github.com/0HyperCube/specta.git?rev=c47a22b4c0863d27bc47529f300de3969480c66d#c47a22b4c0863d27bc47529f300de3969480c66d"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"itertools",
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ resolver = "2"
|
|||
exclude = ["node-graph/gpu-compiler"]
|
||||
|
||||
[workspace.dependencies]
|
||||
specta = { git = "https://github.com/0HyperCube/specta.git", rev = "d28cd72ea4ea2f6eaa85e0622d3a98340dbb43d3", features = [
|
||||
specta = { git = "https://github.com/0HyperCube/specta.git", rev = "c47a22b4c0863d27bc47529f300de3969480c66d", features = [
|
||||
"glam",
|
||||
] }
|
||||
xxhash-rust = { version = "0.8", features = ["xxh3"] }
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::TextButton;
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{CheckboxInput, DropdownEntryData, DropdownInput, NumberInput, RadioEntryData, RadioInput, TextInput};
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::{Separator, SeparatorDirection, SeparatorType, TextLabel};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use document_legacy::LayerId;
|
||||
|
|
@ -47,46 +44,22 @@ impl MessageHandler<ExportDialogMessage, ()> for ExportDialogMessageHandler {
|
|||
impl PropertyHolder for ExportDialogMessageHandler {
|
||||
fn properties(&self) -> Layout {
|
||||
let file_name = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "File Name".into(),
|
||||
table_align: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextInput(TextInput {
|
||||
value: self.file_name.clone(),
|
||||
on_update: WidgetCallback::new(|text_input: &TextInput| ExportDialogMessage::FileName(text_input.value.clone()).into()),
|
||||
..Default::default()
|
||||
})),
|
||||
TextLabel::new("File Name").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
TextInput::new(&self.file_name)
|
||||
.on_update(|text_input: &TextInput| ExportDialogMessage::FileName(text_input.value.clone()).into())
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let entries = [(FileType::Png, "PNG"), (FileType::Jpg, "JPG"), (FileType::Svg, "SVG")]
|
||||
.into_iter()
|
||||
.map(|(val, name)| RadioEntryData {
|
||||
label: name.into(),
|
||||
on_update: WidgetCallback::new(move |_| ExportDialogMessage::FileType(val).into()),
|
||||
..RadioEntryData::default()
|
||||
})
|
||||
.map(|(val, name)| RadioEntryData::new(name).on_update(move |_| ExportDialogMessage::FileType(val).into()))
|
||||
.collect();
|
||||
|
||||
let export_type = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "File Type".into(),
|
||||
table_align: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::RadioInput(RadioInput {
|
||||
selected_index: self.file_type as u32,
|
||||
entries,
|
||||
..Default::default()
|
||||
})),
|
||||
TextLabel::new("File Type").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
RadioInput::new(entries).selected_index(self.file_type as u32).widget_holder(),
|
||||
];
|
||||
|
||||
let artboards = self.artboards.iter().map(|(&val, name)| (ExportBounds::Artboard(val), name.to_string(), false));
|
||||
|
|
@ -98,98 +71,52 @@ impl PropertyHolder for ExportDialogMessageHandler {
|
|||
let index = export_area_options.iter().position(|(val, _, _)| val == &self.bounds).unwrap();
|
||||
let entries = vec![export_area_options
|
||||
.into_iter()
|
||||
.map(|(val, name, disabled)| DropdownEntryData {
|
||||
label: name,
|
||||
on_update: WidgetCallback::new(move |_| ExportDialogMessage::ExportBounds(val).into()),
|
||||
disabled,
|
||||
..Default::default()
|
||||
})
|
||||
.map(|(val, name, disabled)| DropdownEntryData::new(name).on_update(move |_| ExportDialogMessage::ExportBounds(val).into()).disabled(disabled))
|
||||
.collect()];
|
||||
|
||||
let export_area = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Bounds".into(),
|
||||
table_align: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::DropdownInput(DropdownInput {
|
||||
selected_index: Some(index as u32),
|
||||
entries,
|
||||
..Default::default()
|
||||
})),
|
||||
TextLabel::new("Bounds").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
DropdownInput::new(entries).selected_index(Some(index as u32)).widget_holder(),
|
||||
];
|
||||
|
||||
let transparent_background = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Transparency".into(),
|
||||
table_align: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::CheckboxInput(CheckboxInput {
|
||||
checked: self.transparent_background,
|
||||
disabled: self.file_type == FileType::Jpg,
|
||||
on_update: WidgetCallback::new(move |value: &CheckboxInput| ExportDialogMessage::TransparentBackground(value.checked).into()),
|
||||
..Default::default()
|
||||
})),
|
||||
TextLabel::new("Transparency").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
CheckboxInput::new(self.transparent_background)
|
||||
.disabled(self.file_type == FileType::Jpg)
|
||||
.on_update(move |value: &CheckboxInput| ExportDialogMessage::TransparentBackground(value.checked).into())
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let resolution = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Scale Factor".into(),
|
||||
table_align: true,
|
||||
..TextLabel::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: Some(self.scale_factor),
|
||||
label: "".into(),
|
||||
unit: " ".into(),
|
||||
min: Some(0.),
|
||||
disabled: self.file_type == FileType::Svg,
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| ExportDialogMessage::ScaleFactor(number_input.value.unwrap()).into()),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
TextLabel::new("Scale Factor").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
NumberInput::new(Some(self.scale_factor))
|
||||
.unit(" ")
|
||||
.min(0.)
|
||||
.disabled(self.file_type == FileType::Svg)
|
||||
.on_update(|number_input: &NumberInput| ExportDialogMessage::ScaleFactor(number_input.value.unwrap()).into())
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let button_widgets = vec![
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "Export".to_string(),
|
||||
min_width: 96,
|
||||
emphasized: true,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
TextButton::new("Export")
|
||||
.min_width(96)
|
||||
.emphasized(true)
|
||||
.on_update(|_| {
|
||||
DialogMessage::CloseDialogAndThen {
|
||||
followups: vec![ExportDialogMessage::Submit.into()],
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "Cancel".to_string(),
|
||||
min_width: 96,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogDismiss.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
})
|
||||
.widget_holder(),
|
||||
TextButton::new("Cancel").min_width(96).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_holder(),
|
||||
];
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Export".to_string(),
|
||||
bold: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new("Export").bold(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row { widgets: file_name },
|
||||
LayoutGroup::Row { widgets: export_type },
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::TextButton;
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{CheckboxInput, NumberInput, TextInput};
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::{Separator, SeparatorDirection, SeparatorType, TextLabel};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use graphene_core::uuid::generate_uuid;
|
||||
|
|
@ -54,103 +51,60 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
|
|||
|
||||
impl PropertyHolder for NewDocumentDialogMessageHandler {
|
||||
fn properties(&self) -> Layout {
|
||||
let title = vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "New document".into(),
|
||||
bold: true,
|
||||
..Default::default()
|
||||
}))];
|
||||
let title = vec![TextLabel::new("New document").bold(true).widget_holder()];
|
||||
|
||||
let name = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Name".into(),
|
||||
table_align: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextInput(TextInput {
|
||||
value: self.name.clone(),
|
||||
on_update: WidgetCallback::new(|text_input: &TextInput| NewDocumentDialogMessage::Name(text_input.value.clone()).into()),
|
||||
..Default::default()
|
||||
})),
|
||||
TextLabel::new("Name").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
TextInput::new(&self.name)
|
||||
.on_update(|text_input: &TextInput| NewDocumentDialogMessage::Name(text_input.value.clone()).into())
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let infinite = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Infinite Canvas".into(),
|
||||
table_align: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::CheckboxInput(CheckboxInput {
|
||||
checked: self.infinite,
|
||||
on_update: WidgetCallback::new(|checkbox_input: &CheckboxInput| NewDocumentDialogMessage::Infinite(checkbox_input.checked).into()),
|
||||
..Default::default()
|
||||
})),
|
||||
TextLabel::new("Infinite Canvas").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
CheckboxInput::new(self.infinite)
|
||||
.on_update(|checkbox_input: &CheckboxInput| NewDocumentDialogMessage::Infinite(checkbox_input.checked).into())
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let scale = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Dimensions".into(),
|
||||
table_align: true,
|
||||
..TextLabel::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
label: "W".into(),
|
||||
unit: " px".into(),
|
||||
value: Some(self.dimensions.x as f64),
|
||||
min: Some(0.),
|
||||
is_integer: true,
|
||||
disabled: self.infinite,
|
||||
min_width: 100,
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| NewDocumentDialogMessage::DimensionsX(number_input.value.unwrap()).into()),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Related,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
label: "H".into(),
|
||||
unit: " px".into(),
|
||||
value: Some(self.dimensions.y as f64),
|
||||
min: Some(0.),
|
||||
is_integer: true,
|
||||
disabled: self.infinite,
|
||||
min_width: 100,
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| NewDocumentDialogMessage::DimensionsY(number_input.value.unwrap()).into()),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
TextLabel::new("Dimensions").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
NumberInput::new(Some(self.dimensions.x as f64))
|
||||
.label("W")
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
.is_integer(true)
|
||||
.disabled(self.infinite)
|
||||
.min_width(100)
|
||||
.on_update(|number_input: &NumberInput| NewDocumentDialogMessage::DimensionsX(number_input.value.unwrap()).into())
|
||||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
NumberInput::new(Some(self.dimensions.y as f64))
|
||||
.label("H")
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
.is_integer(true)
|
||||
.disabled(self.infinite)
|
||||
.min_width(100)
|
||||
.on_update(|number_input: &NumberInput| NewDocumentDialogMessage::DimensionsY(number_input.value.unwrap()).into())
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let button_widgets = vec![
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "OK".to_string(),
|
||||
min_width: 96,
|
||||
emphasized: true,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
TextButton::new("OK")
|
||||
.min_width(96)
|
||||
.emphasized(true)
|
||||
.on_update(|_| {
|
||||
DialogMessage::CloseDialogAndThen {
|
||||
followups: vec![NewDocumentDialogMessage::Submit.into()],
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "Cancel".to_string(),
|
||||
min_width: 96,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogDismiss.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
})
|
||||
.widget_holder(),
|
||||
TextButton::new("Cancel").min_width(96).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_holder(),
|
||||
];
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::TextButton;
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{CheckboxInput, NumberInput, TextInput};
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::{Separator, SeparatorDirection, SeparatorType, TextLabel};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
/// A dialog to allow users to customize Graphite editor options
|
||||
|
|
@ -31,107 +28,62 @@ impl PreferencesDialogMessageHandler {
|
|||
|
||||
fn properties(&self, preferences: &PreferencesMessageHandler) -> Layout {
|
||||
let zoom_with_scroll = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Input".into(),
|
||||
min_width: 60,
|
||||
italic: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Zoom with Scroll".into(),
|
||||
table_align: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::CheckboxInput(CheckboxInput {
|
||||
checked: preferences.zoom_with_scroll,
|
||||
tooltip: "Use the scroll wheel for zooming instead of vertically panning (not recommended for trackpads)".into(),
|
||||
on_update: WidgetCallback::new(|checkbox_input: &CheckboxInput| {
|
||||
TextLabel::new("Input").min_width(60).italic(true).widget_holder(),
|
||||
TextLabel::new("Zoom with Scroll").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
CheckboxInput::new(preferences.zoom_with_scroll)
|
||||
.tooltip("Use the scroll wheel for zooming instead of vertically panning (not recommended for trackpads)")
|
||||
.on_update(|checkbox_input: &CheckboxInput| {
|
||||
PreferencesMessage::ModifyLayout {
|
||||
zoom_with_scroll: checkbox_input.checked,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
})
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let imaginate_server_hostname = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Imaginate".into(),
|
||||
min_width: 60,
|
||||
italic: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Server Hostname".into(),
|
||||
table_align: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextInput(TextInput {
|
||||
value: preferences.imaginate_server_hostname.clone(),
|
||||
min_width: 200,
|
||||
on_update: WidgetCallback::new(|text_input: &TextInput| PreferencesMessage::ImaginateServerHostname { hostname: text_input.value.clone() }.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
TextLabel::new("Imaginate").min_width(60).italic(true).widget_holder(),
|
||||
TextLabel::new("Server Hostname").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
TextInput::new(&preferences.imaginate_server_hostname)
|
||||
.min_width(200)
|
||||
.on_update(|text_input: &TextInput| PreferencesMessage::ImaginateServerHostname { hostname: text_input.value.clone() }.into())
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let imaginate_refresh_frequency = vec![
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel { min_width: 60, ..Default::default() })),
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Refresh Frequency".into(),
|
||||
table_align: true,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: " seconds".into(),
|
||||
value: Some(preferences.imaginate_refresh_frequency),
|
||||
min: Some(0.),
|
||||
min_width: 200,
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| PreferencesMessage::ImaginateRefreshFrequency { seconds: number_input.value.unwrap() }.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
TextLabel::new("").min_width(60).widget_holder(),
|
||||
TextLabel::new("Refresh Frequency").table_align(true).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
NumberInput::new(Some(preferences.imaginate_refresh_frequency))
|
||||
.unit(" seconds")
|
||||
.min(0.)
|
||||
.min_width(200)
|
||||
.on_update(|number_input: &NumberInput| PreferencesMessage::ImaginateRefreshFrequency { seconds: number_input.value.unwrap() }.into())
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let button_widgets = vec![
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "Ok".to_string(),
|
||||
min_width: 96,
|
||||
emphasized: true,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
TextButton::new("Ok")
|
||||
.min_width(96)
|
||||
.emphasized(true)
|
||||
.on_update(|_| {
|
||||
DialogMessage::CloseDialogAndThen {
|
||||
followups: vec![PreferencesDialogMessage::Confirm.into()],
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "Reset to Defaults".to_string(),
|
||||
min_width: 96,
|
||||
on_update: WidgetCallback::new(|_| PreferencesMessage::ResetToDefaults.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
})
|
||||
.widget_holder(),
|
||||
TextButton::new("Reset to Defaults")
|
||||
.min_width(96)
|
||||
.on_update(|_| PreferencesMessage::ResetToDefaults.into())
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Editor Preferences".to_string(),
|
||||
bold: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new("Editor Preferences").bold(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row { widgets: zoom_with_scroll },
|
||||
LayoutGroup::Row { widgets: imaginate_server_hostname },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
use crate::application::{commit_info_localized, release_series};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::TextButton;
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::TextLabel;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
/// A dialog for displaying information on [BuildMetadata] viewable via *Help* > *About Graphite* in the menu bar.
|
||||
|
|
@ -19,34 +17,17 @@ impl PropertyHolder for AboutGraphiteDialog {
|
|||
];
|
||||
let link_widgets = links
|
||||
.into_iter()
|
||||
.map(|(label, url)| {
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: label.to_string(),
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::TriggerVisitLink { url: url.to_string() }.into()),
|
||||
..Default::default()
|
||||
}))
|
||||
})
|
||||
.map(|(label, url)| TextButton::new(label).on_update(|_| FrontendMessage::TriggerVisitLink { url: url.to_string() }.into()).widget_holder())
|
||||
.collect();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Graphite".to_string(),
|
||||
bold: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new("Graphite".to_string()).bold(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: release_series(),
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new(release_series()).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: commit_info_localized(self.localized_commit_date.as_str()),
|
||||
multiline: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new(commit_info_localized(&self.localized_commit_date)).multiline(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row { widgets: link_widgets },
|
||||
]))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::TextButton;
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::TextLabel;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
/// A dialog for confirming the closing of all documents viewable via `file -> close all` in the menu bar.
|
||||
|
|
@ -8,42 +6,25 @@ pub struct CloseAllDocumentsDialog;
|
|||
|
||||
impl PropertyHolder for CloseAllDocumentsDialog {
|
||||
fn properties(&self) -> Layout {
|
||||
let button_widgets = vec![
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "Discard All".to_string(),
|
||||
min_width: 96,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
DialogMessage::CloseDialogAndThen {
|
||||
followups: vec![PortfolioMessage::CloseAllDocuments.into()],
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "Cancel".to_string(),
|
||||
min_width: 96,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogDismiss.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
];
|
||||
let discard = TextButton::new("Discard All")
|
||||
.min_width(96)
|
||||
.on_update(|_| {
|
||||
DialogMessage::CloseDialogAndThen {
|
||||
followups: vec![PortfolioMessage::CloseAllDocuments.into()],
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_holder();
|
||||
let cancel = TextButton::new("Cancel").min_width(96).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_holder();
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Close all documents?".to_string(),
|
||||
bold: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new("Close all documents?").multiline(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Unsaved work will be lost!".to_string(),
|
||||
multiline: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new("Unsaved work will be lost!").multiline(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row { widgets: button_widgets },
|
||||
LayoutGroup::Row { widgets: vec![discard, cancel] },
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
use crate::messages::broadcast::broadcast_event::BroadcastEvent;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::TextButton;
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::TextLabel;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
/// A dialog for confirming the closing a document with unsaved changes.
|
||||
|
|
@ -15,51 +13,34 @@ impl PropertyHolder for CloseDocumentDialog {
|
|||
let document_id = self.document_id;
|
||||
|
||||
let button_widgets = vec![
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "Save".to_string(),
|
||||
min_width: 96,
|
||||
emphasized: true,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
TextButton::new("Save")
|
||||
.min_width(96)
|
||||
.emphasized(true)
|
||||
.on_update(|_| {
|
||||
DialogMessage::CloseDialogAndThen {
|
||||
followups: vec![DocumentMessage::SaveDocument.into()],
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "Discard".to_string(),
|
||||
min_width: 96,
|
||||
on_update: WidgetCallback::new(move |_| {
|
||||
})
|
||||
.widget_holder(),
|
||||
TextButton::new("Discard")
|
||||
.min_width(96)
|
||||
.on_update(move |_| {
|
||||
DialogMessage::CloseDialogAndThen {
|
||||
followups: vec![BroadcastEvent::ToolAbort.into(), PortfolioMessage::CloseDocument { document_id }.into()],
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "Cancel".to_string(),
|
||||
min_width: 96,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogDismiss.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
})
|
||||
.widget_holder(),
|
||||
TextButton::new("Cancel").min_width(96).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_holder(),
|
||||
];
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Save changes before closing?".to_string(),
|
||||
bold: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new("Save changes before closing?").bold(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: self.document_name.clone(),
|
||||
multiline: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new(&self.document_name).multiline(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row { widgets: button_widgets },
|
||||
]))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::TextButton;
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::TextLabel;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use std::fmt::Write;
|
||||
|
|
@ -13,41 +11,33 @@ pub struct ComingSoonDialog {
|
|||
impl PropertyHolder for ComingSoonDialog {
|
||||
fn properties(&self) -> Layout {
|
||||
let mut details = "This feature is not implemented yet".to_string();
|
||||
let mut buttons = vec![WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "OK".to_string(),
|
||||
emphasized: true,
|
||||
min_width: 96,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogDismiss.into()),
|
||||
..Default::default()
|
||||
}))];
|
||||
|
||||
let mut buttons = vec![TextButton::new("OK")
|
||||
.emphasized(true)
|
||||
.min_width(96)
|
||||
.on_update(|_| FrontendMessage::DisplayDialogDismiss.into())
|
||||
.widget_holder()];
|
||||
|
||||
if let Some(issue) = self.issue {
|
||||
let _ = write!(details, "— but you can help add it!\nSee issue #{issue} on GitHub.");
|
||||
buttons.push(WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: format!("Issue #{issue}"),
|
||||
min_width: 96,
|
||||
on_update: WidgetCallback::new(move |_| {
|
||||
FrontendMessage::TriggerVisitLink {
|
||||
url: format!("https://github.com/GraphiteEditor/Graphite/issues/{issue}"),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})));
|
||||
buttons.push(
|
||||
TextButton::new(format!("Issue #{issue}"))
|
||||
.min_width(96)
|
||||
.on_update(move |_| {
|
||||
FrontendMessage::TriggerVisitLink {
|
||||
url: format!("https://github.com/GraphiteEditor/Graphite/issues/{issue}"),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_holder(),
|
||||
);
|
||||
}
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Coming soon".to_string(),
|
||||
bold: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new("Coming soon").bold(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: details,
|
||||
multiline: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new(details).multiline(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row { widgets: buttons },
|
||||
]))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::TextButton;
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::TextLabel;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
/// A dialog to notify users of a non-fatal error.
|
||||
|
|
@ -13,27 +11,17 @@ impl PropertyHolder for ErrorDialog {
|
|||
fn properties(&self) -> Layout {
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: self.title.clone(),
|
||||
bold: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new(&self.title).bold(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: self.description.clone(),
|
||||
multiline: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextLabel::new(&self.description).multiline(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: "OK".to_string(),
|
||||
emphasized: true,
|
||||
min_width: 96,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogDismiss.into()),
|
||||
..Default::default()
|
||||
}))],
|
||||
widgets: vec![TextButton::new("OK")
|
||||
.emphasized(true)
|
||||
.min_width(96)
|
||||
.on_update(|_| FrontendMessage::DisplayDialogDismiss.into())
|
||||
.widget_holder()],
|
||||
},
|
||||
]))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -452,6 +452,7 @@ pub struct WidgetHolder {
|
|||
}
|
||||
|
||||
impl WidgetHolder {
|
||||
#[deprecated(since = "0.0.0", note = "Please use the builder pattern, e.g. TextLabel::new(\"hello\").widget_holder()")]
|
||||
pub fn new(widget: Widget) -> Self {
|
||||
Self { widget_id: generate_uuid(), widget }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,27 @@ use serde::{Deserialize, Serialize};
|
|||
#[derive(PartialEq, Clone, Debug, Hash, Eq, Copy, Serialize, Deserialize, specta::Type)]
|
||||
#[repr(u8)]
|
||||
pub enum LayoutTarget {
|
||||
/// Contains the contents of the dialog, including the title and action buttons. Must be shown with the `FrontendMessage::DisplayDialog` message.
|
||||
DialogDetails,
|
||||
/// Contains the widgets located directly above the canvas to the right, for example the zoom in and out buttons.
|
||||
DocumentBar,
|
||||
/// Contains the dropdown for design / select / guide mode found on the top left of the canvas.
|
||||
DocumentMode,
|
||||
/// Options for opacity seen at the top of the Layers panel.
|
||||
LayerTreeOptions,
|
||||
/// The dropdown menu at the very top of the application: File, Edit, etc.
|
||||
MenuBar,
|
||||
/// Bar at the top of the node graph containing the location and the 'preview' and 'hide' buttons.
|
||||
NodeGraphBar,
|
||||
/// The bar at the top of the Properties panel containing the layer name and icon.
|
||||
PropertiesOptions,
|
||||
/// The body of the Properties panel containing many collapsable sections.
|
||||
PropertiesSections,
|
||||
/// The bar directly above the canvas, left-aligned and to the right of the document mode dropdown.
|
||||
ToolOptions,
|
||||
/// The vertical buttons for all of the tools on the left of the canvas.
|
||||
ToolShelf,
|
||||
/// The color swatch for the working colors and a flip and reset button found at the bottom of the tool shelf.
|
||||
WorkingColors,
|
||||
|
||||
// KEEP THIS ENUM LAST
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
||||
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
|
||||
use crate::messages::layout::utility_types::layout_widget::WidgetHolder;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Widget, WidgetCallback};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -51,9 +50,7 @@ impl MenuBarEntry {
|
|||
}
|
||||
|
||||
pub fn create_action(callback: impl Fn(&()) -> Message + 'static + Send + Sync) -> WidgetHolder {
|
||||
WidgetHolder::new(Widget::InvisibleStandinInput(InvisibleStandinInput {
|
||||
on_update: WidgetCallback::new(callback),
|
||||
}))
|
||||
InvisibleStandinInput::new().on_update(callback).widget_holder()
|
||||
}
|
||||
|
||||
pub fn no_action() -> WidgetHolder {
|
||||
|
|
|
|||
|
|
@ -5,19 +5,12 @@ use crate::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX,
|
|||
use crate::messages::frontend::utility_types::ExportBounds;
|
||||
use crate::messages::frontend::utility_types::FileType;
|
||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{CheckboxInput, TextLabel};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::{IconButton, PopoverButton};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{
|
||||
DropdownEntryData, DropdownInput, NumberInput, NumberInputIncrementBehavior, NumberInputMode, OptionalInput, RadioEntryData, RadioInput,
|
||||
};
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::{Separator, SeparatorDirection, SeparatorType};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::properties_panel::utility_types::PropertiesPanelMessageHandlerData;
|
||||
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
||||
use crate::messages::portfolio::document::utility_types::layer_panel::{LayerMetadata, LayerPanelEntry, RawBuffer};
|
||||
use crate::messages::portfolio::document::utility_types::misc::DocumentMode;
|
||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentSave, FlipAxis};
|
||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, DocumentSave, FlipAxis};
|
||||
use crate::messages::portfolio::document::utility_types::vectorize_layer_metadata;
|
||||
use crate::messages::portfolio::utility_types::PersistentData;
|
||||
use crate::messages::prelude::*;
|
||||
|
|
@ -1589,11 +1582,9 @@ impl DocumentMessageHandler {
|
|||
pub fn update_document_widgets(&self, responses: &mut VecDeque<Message>) {
|
||||
let snapping_state = self.snapping_state.clone();
|
||||
let mut widgets = vec![
|
||||
WidgetHolder::new(Widget::OptionalInput(OptionalInput {
|
||||
checked: snapping_state.snapping_enabled,
|
||||
icon: "Snapping".into(),
|
||||
tooltip: "Snapping".into(),
|
||||
on_update: WidgetCallback::new(move |optional_input: &OptionalInput| {
|
||||
OptionalInput::new(snapping_state.snapping_enabled, "Snapping")
|
||||
.tooltip("Snapping")
|
||||
.on_update(move |optional_input: &OptionalInput| {
|
||||
let snapping_enabled = optional_input.checked;
|
||||
DocumentMessage::SetSnapping {
|
||||
snapping_enabled: Some(snapping_enabled),
|
||||
|
|
@ -1601,261 +1592,159 @@ impl DocumentMessageHandler {
|
|||
node_snapping: Some(snapping_state.node_snapping),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "Snapping".into(),
|
||||
text: "Select the vectors to snap to.".into(), // TODO: check whether this is an apt description
|
||||
options_widget: vec![
|
||||
})
|
||||
.widget_holder(),
|
||||
PopoverButton::new("Snapping", "Snap customization settings")
|
||||
.options_widget(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
WidgetHolder::new(Widget::CheckboxInput(CheckboxInput {
|
||||
tooltip: SnappingOptions::BoundingBoxes.to_string(),
|
||||
checked: snapping_state.bounding_box_snapping,
|
||||
on_update: WidgetCallback::new(move |input: &CheckboxInput| {
|
||||
CheckboxInput::new(snapping_state.bounding_box_snapping)
|
||||
.tooltip(SnappingOptions::BoundingBoxes.to_string())
|
||||
.on_update(move |input: &CheckboxInput| {
|
||||
DocumentMessage::SetSnapping {
|
||||
snapping_enabled: None,
|
||||
bounding_box_snapping: Some(input.checked),
|
||||
node_snapping: None,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: SnappingOptions::BoundingBoxes.to_string(),
|
||||
table_align: false,
|
||||
min_width: 60,
|
||||
..Default::default()
|
||||
})),
|
||||
// adds appropriate space between row elements
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Vertical,
|
||||
separator_type: SeparatorType::Related,
|
||||
})),
|
||||
})
|
||||
.widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
TextLabel::new(SnappingOptions::BoundingBoxes.to_string()).table_align(false).min_width(60).widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
WidgetHolder::new(Widget::CheckboxInput(CheckboxInput {
|
||||
checked: self.snapping_state.node_snapping,
|
||||
tooltip: SnappingOptions::Points.to_string(),
|
||||
on_update: WidgetCallback::new(|input: &CheckboxInput| {
|
||||
CheckboxInput::new(self.snapping_state.node_snapping)
|
||||
.tooltip(SnappingOptions::Points.to_string())
|
||||
.on_update(|input: &CheckboxInput| {
|
||||
DocumentMessage::SetSnapping {
|
||||
snapping_enabled: None,
|
||||
bounding_box_snapping: None,
|
||||
node_snapping: Some(input.checked),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: SnappingOptions::Points.to_string(),
|
||||
table_align: false,
|
||||
min_width: 60,
|
||||
..Default::default()
|
||||
})),
|
||||
})
|
||||
.widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
TextLabel::new(SnappingOptions::Points.to_string()).table_align(false).min_width(60).widget_holder(),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::OptionalInput(OptionalInput {
|
||||
checked: true,
|
||||
icon: "Grid".into(),
|
||||
tooltip: "Grid".into(),
|
||||
on_update: WidgetCallback::new(|_| DialogMessage::RequestComingSoonDialog { issue: Some(318) }.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "Grid".into(),
|
||||
text: "Coming soon".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::OptionalInput(OptionalInput {
|
||||
checked: self.overlays_visible,
|
||||
icon: "Overlays".into(),
|
||||
tooltip: "Overlays".into(),
|
||||
on_update: WidgetCallback::new(|optional_input: &OptionalInput| DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked }.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "Overlays".into(),
|
||||
text: "Coming soon".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::RadioInput(RadioInput {
|
||||
selected_index: match self.view_mode {
|
||||
ViewMode::Normal => 0,
|
||||
_ => 1,
|
||||
},
|
||||
entries: vec![
|
||||
RadioEntryData {
|
||||
value: "normal".into(),
|
||||
icon: "ViewModeNormal".into(),
|
||||
tooltip: "View Mode: Normal".into(),
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::SetViewMode { view_mode: ViewMode::Normal }.into()),
|
||||
..RadioEntryData::default()
|
||||
},
|
||||
RadioEntryData {
|
||||
value: "outline".into(),
|
||||
icon: "ViewModeOutline".into(),
|
||||
tooltip: "View Mode: Outline".into(),
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::SetViewMode { view_mode: ViewMode::Outline }.into()),
|
||||
..RadioEntryData::default()
|
||||
},
|
||||
RadioEntryData {
|
||||
value: "pixels".into(),
|
||||
icon: "ViewModePixels".into(),
|
||||
tooltip: "View Mode: Pixels".into(),
|
||||
on_update: WidgetCallback::new(|_| DialogMessage::RequestComingSoonDialog { issue: Some(320) }.into()),
|
||||
..RadioEntryData::default()
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "View Mode".into(),
|
||||
text: "Coming soon".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Section,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
size: 24,
|
||||
icon: "ZoomIn".into(),
|
||||
tooltip: "Zoom In".into(),
|
||||
tooltip_shortcut: action_keys!(NavigationMessageDiscriminant::IncreaseCanvasZoom),
|
||||
on_update: WidgetCallback::new(|_| NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
size: 24,
|
||||
icon: "ZoomOut".into(),
|
||||
tooltip: "Zoom Out".into(),
|
||||
tooltip_shortcut: action_keys!(NavigationMessageDiscriminant::DecreaseCanvasZoom),
|
||||
on_update: WidgetCallback::new(|_| NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
size: 24,
|
||||
icon: "ZoomReset".into(),
|
||||
tooltip: "Zoom to 100%".into(),
|
||||
tooltip_shortcut: action_keys!(DocumentMessageDiscriminant::ZoomCanvasTo100Percent),
|
||||
on_update: WidgetCallback::new(|_| NavigationMessage::SetCanvasZoom { zoom_factor: 1. }.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Related,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: "%".into(),
|
||||
value: Some(self.navigation_handler.snapped_scale() * 100.),
|
||||
min: Some(0.000001),
|
||||
max: Some(1000000.),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
])
|
||||
.widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
OptionalInput::new(true, "Grid")
|
||||
.tooltip("Grid")
|
||||
.on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(318) }.into())
|
||||
.widget_holder(),
|
||||
PopoverButton::new("Grid", "Coming soon").widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
OptionalInput::new(self.overlays_visible, "Overlays")
|
||||
.tooltip("Overlays")
|
||||
.on_update(|optional_input: &OptionalInput| DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked }.into())
|
||||
.widget_holder(),
|
||||
PopoverButton::new("Overlays", "Coming soon").widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
RadioInput::new(vec![
|
||||
RadioEntryData::default()
|
||||
.value("normal")
|
||||
.icon("ViewModeNormal")
|
||||
.tooltip("View Mode: Normal")
|
||||
.on_update(|_| DocumentMessage::SetViewMode { view_mode: ViewMode::Normal }.into()),
|
||||
RadioEntryData::default()
|
||||
.value("outline")
|
||||
.icon("ViewModeOutline")
|
||||
.tooltip("View Mode: Outline")
|
||||
.on_update(|_| DocumentMessage::SetViewMode { view_mode: ViewMode::Outline }.into()),
|
||||
RadioEntryData::default()
|
||||
.value("pixels")
|
||||
.icon("ViewModePixels")
|
||||
.tooltip("View Mode: Pixels")
|
||||
.on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(320) }.into()),
|
||||
])
|
||||
.selected_index(match self.view_mode {
|
||||
ViewMode::Normal => 0,
|
||||
_ => 1,
|
||||
})
|
||||
.widget_holder(),
|
||||
PopoverButton::new("View Mode", "Coming soon").widget_holder(),
|
||||
WidgetHolder::section_separator(),
|
||||
IconButton::new("ZoomIn", 24)
|
||||
.tooltip("Zoom In")
|
||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::IncreaseCanvasZoom))
|
||||
.on_update(|_| NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }.into())
|
||||
.widget_holder(),
|
||||
IconButton::new("ZoomOut", 24)
|
||||
.tooltip("Zoom Out")
|
||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::DecreaseCanvasZoom))
|
||||
.on_update(|_| NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }.into())
|
||||
.widget_holder(),
|
||||
IconButton::new("ZoomReset", 24)
|
||||
.tooltip("Zoom to 100%")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ZoomCanvasTo100Percent))
|
||||
.on_update(|_| NavigationMessage::SetCanvasZoom { zoom_factor: 1. }.into())
|
||||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
NumberInput::new(Some(self.navigation_handler.snapped_scale() * 100.))
|
||||
.unit("%")
|
||||
.min(0.000001)
|
||||
.max(1000000.)
|
||||
.mode_increment()
|
||||
.on_update(|number_input: &NumberInput| {
|
||||
NavigationMessage::SetCanvasZoom {
|
||||
zoom_factor: number_input.value.unwrap() / 100.,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
increment_behavior: NumberInputIncrementBehavior::Callback,
|
||||
increment_callback_decrease: WidgetCallback::new(|_| NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }.into()),
|
||||
increment_callback_increase: WidgetCallback::new(|_| NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }.into()),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
})
|
||||
.increment_behavior(NumberInputIncrementBehavior::Callback)
|
||||
.increment_callback_decrease(|_| NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }.into())
|
||||
.increment_callback_increase(|_| NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }.into())
|
||||
.widget_holder(),
|
||||
];
|
||||
let rotation_value = self.navigation_handler.snapped_angle() / (std::f64::consts::PI / 180.);
|
||||
if rotation_value.abs() > 0.00001 {
|
||||
widgets.extend([
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Related,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: "°".into(),
|
||||
value: Some(rotation_value),
|
||||
step: 15.,
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
WidgetHolder::related_separator(),
|
||||
NumberInput::new(Some(rotation_value))
|
||||
.unit("°")
|
||||
.step(15.)
|
||||
.on_update(|number_input: &NumberInput| {
|
||||
NavigationMessage::SetCanvasRotation {
|
||||
angle_radians: number_input.value.unwrap() * (std::f64::consts::PI / 180.),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
})
|
||||
.widget_holder(),
|
||||
]);
|
||||
}
|
||||
widgets.extend([
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Related,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "Canvas Navigation".into(),
|
||||
text: "Interactive options in this popover menu are coming soon.\nZoom with Shift + MMB Drag or Ctrl + Scroll Wheel Roll.\nRotate with Ctrl + MMB Drag.".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::related_separator(),
|
||||
PopoverButton::new(
|
||||
"Canvas Navigation",
|
||||
"Interactive options in this popover menu are coming soon.\nZoom with Shift + MMB Drag or Ctrl + Scroll Wheel Roll.\nRotate with Ctrl + MMB Drag.",
|
||||
)
|
||||
.widget_holder(),
|
||||
]);
|
||||
let document_bar_layout = WidgetLayout::new(vec![LayoutGroup::Row { widgets }]);
|
||||
|
||||
let document_mode_layout = WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
WidgetHolder::new(Widget::DropdownInput(DropdownInput {
|
||||
entries: vec![vec![
|
||||
DropdownEntryData {
|
||||
label: DocumentMode::DesignMode.to_string(),
|
||||
icon: DocumentMode::DesignMode.icon_name(),
|
||||
..DropdownEntryData::default()
|
||||
},
|
||||
DropdownEntryData {
|
||||
label: DocumentMode::SelectMode.to_string(),
|
||||
icon: DocumentMode::SelectMode.icon_name(),
|
||||
on_update: WidgetCallback::new(|_| DialogMessage::RequestComingSoonDialog { issue: Some(330) }.into()),
|
||||
..DropdownEntryData::default()
|
||||
},
|
||||
DropdownEntryData {
|
||||
label: DocumentMode::GuideMode.to_string(),
|
||||
icon: DocumentMode::GuideMode.icon_name(),
|
||||
on_update: WidgetCallback::new(|_| DialogMessage::RequestComingSoonDialog { issue: Some(331) }.into()),
|
||||
..DropdownEntryData::default()
|
||||
},
|
||||
]],
|
||||
selected_index: Some(self.document_mode as u32),
|
||||
draw_icon: true,
|
||||
interactive: false, // TODO: set to true when dialogs are not spawned
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Section,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
DropdownInput::new(
|
||||
vec![vec![
|
||||
DropdownEntryData::new(DocumentMode::DesignMode.to_string()).icon(DocumentMode::DesignMode.icon_name()),
|
||||
DropdownEntryData::new(DocumentMode::SelectMode.to_string())
|
||||
.icon(DocumentMode::SelectMode.icon_name())
|
||||
.on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(330) }.into()),
|
||||
DropdownEntryData::new(DocumentMode::GuideMode.to_string())
|
||||
.icon(DocumentMode::GuideMode.icon_name())
|
||||
.on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(331) }.into()),
|
||||
]])
|
||||
.selected_index( Some(self.document_mode as u32))
|
||||
.draw_icon( true)
|
||||
.interactive( false) // TODO: set to true when dialogs are not spawned
|
||||
.widget_holder(),
|
||||
WidgetHolder::section_separator(),
|
||||
],
|
||||
}]);
|
||||
|
||||
|
|
@ -1914,11 +1803,10 @@ impl DocumentMessageHandler {
|
|||
.map(|modes| {
|
||||
modes
|
||||
.iter()
|
||||
.map(|mode| DropdownEntryData {
|
||||
label: mode.to_string(),
|
||||
value: mode.to_string(),
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::SetBlendModeForSelectedLayers { blend_mode: *mode }.into()),
|
||||
..Default::default()
|
||||
.map(|mode| {
|
||||
DropdownEntryData::new(mode.to_string())
|
||||
.value(mode.to_string())
|
||||
.on_update(|_| DocumentMessage::SetBlendModeForSelectedLayers { blend_mode: *mode }.into())
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
|
|
@ -1926,57 +1814,41 @@ impl DocumentMessageHandler {
|
|||
|
||||
let layer_tree_options = WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
WidgetHolder::new(Widget::DropdownInput(DropdownInput {
|
||||
entries: blend_mode_menu_entries,
|
||||
selected_index: blend_mode.map(|blend_mode| blend_mode as u32),
|
||||
disabled: blend_mode.is_none() && !blend_mode_is_mixed,
|
||||
draw_icon: false,
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Related,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
label: "Opacity".into(),
|
||||
unit: "%".into(),
|
||||
display_decimal_places: 2,
|
||||
disabled: opacity.is_none() && !opacity_is_mixed,
|
||||
value: opacity.map(|opacity| opacity * 100.),
|
||||
min: Some(0.),
|
||||
max: Some(100.),
|
||||
range_min: Some(0.),
|
||||
range_max: Some(100.),
|
||||
mode: NumberInputMode::Range,
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
DropdownInput::new(blend_mode_menu_entries)
|
||||
.selected_index(blend_mode.map(|blend_mode| blend_mode as u32))
|
||||
.disabled(blend_mode.is_none() && !blend_mode_is_mixed)
|
||||
.draw_icon(false)
|
||||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
NumberInput::new(opacity.map(|opacity| opacity * 100.))
|
||||
.label("Opacity")
|
||||
.unit("%")
|
||||
.display_decimal_places(2)
|
||||
.disabled(opacity.is_none() && !opacity_is_mixed)
|
||||
.min(0.)
|
||||
.max(100.)
|
||||
.range_min(Some(0.))
|
||||
.range_max(Some(100.))
|
||||
.mode(NumberInputMode::Range)
|
||||
.on_update(|number_input: &NumberInput| {
|
||||
if let Some(value) = number_input.value {
|
||||
DocumentMessage::SetOpacityForSelectedLayers { opacity: value / 100. }.into()
|
||||
} else {
|
||||
Message::NoOp
|
||||
}
|
||||
}),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Section,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "Folder".into(),
|
||||
tooltip: "New Folder".into(),
|
||||
tooltip_shortcut: action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::CreateEmptyFolder { container_path: vec![] }.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "Trash".into(),
|
||||
tooltip: "Delete Selected".into(),
|
||||
tooltip_shortcut: action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::DeleteSelectedLayers.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
})
|
||||
.widget_holder(),
|
||||
WidgetHolder::section_separator(),
|
||||
IconButton::new("Folder", 24)
|
||||
.tooltip("New Folder")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
||||
.on_update(|_| DocumentMessage::CreateEmptyFolder { container_path: vec![] }.into())
|
||||
.widget_holder(),
|
||||
IconButton::new("Trash", 24)
|
||||
.tooltip("Delete Selected")
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
||||
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
|
||||
.widget_holder(),
|
||||
],
|
||||
}]);
|
||||
|
||||
|
|
|
|||
|
|
@ -245,12 +245,10 @@ impl NodeGraphMessageHandler {
|
|||
|
||||
// Don't show stop previewing button on the original output node
|
||||
if !(is_output && network.previous_outputs_contain(node_id).unwrap_or(true)) {
|
||||
let output_button = WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: if is_output { "End Preview" } else { "Preview" }.to_string(),
|
||||
tooltip: if is_output { "Restore preview to Output node" } else { "Preview node" }.to_string() + " (Shortcut: Alt-click node)",
|
||||
on_update: WidgetCallback::new(move |_| NodeGraphMessage::TogglePreview { node_id }.into()),
|
||||
..Default::default()
|
||||
}));
|
||||
let output_button = TextButton::new(if is_output { "End Preview" } else { "Preview" })
|
||||
.tooltip(if is_output { "Restore preview to Output node" } else { "Preview node" }.to_string() + " (Shortcut: Alt-click node)")
|
||||
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
||||
.widget_holder();
|
||||
widgets.push(output_button);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +1,6 @@
|
|||
use crate::messages::layout::utility_types::layout_widget::WidgetCallback;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, IconButton, RadioEntryData, RadioInput, TextLabel, WidgetHolder};
|
||||
use crate::messages::prelude::Message;
|
||||
|
||||
use graphene_core::Color;
|
||||
|
||||
|
|
@ -12,6 +13,7 @@ pub enum ToolColorType {
|
|||
Custom,
|
||||
}
|
||||
|
||||
/// Color selector widgets seen in [`LayoutTarget::ToolOptions`] bar.
|
||||
pub struct ToolColorOptions {
|
||||
pub custom_color: Option<Color>,
|
||||
pub primary_working_color: Option<Color>,
|
||||
|
|
@ -62,19 +64,19 @@ impl ToolColorOptions {
|
|||
&self,
|
||||
label_text: impl Into<String>,
|
||||
color_allow_none: bool,
|
||||
reset_callback: WidgetCallback<IconButton>,
|
||||
reset_callback: impl Fn(&IconButton) -> Message + 'static + Send + Sync,
|
||||
radio_callback: fn(ToolColorType) -> WidgetCallback<()>,
|
||||
color_callback: WidgetCallback<ColorInput>,
|
||||
color_callback: impl Fn(&ColorInput) -> Message + 'static + Send + Sync,
|
||||
) -> Vec<WidgetHolder> {
|
||||
let mut widgets = vec![TextLabel::new(label_text).widget_holder()];
|
||||
|
||||
if !color_allow_none {
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
} else {
|
||||
let mut reset = IconButton::new("CloseX", 12)
|
||||
let reset = IconButton::new("CloseX", 12)
|
||||
.disabled(self.custom_color.is_none() && self.color_type == ToolColorType::Custom)
|
||||
.tooltip("Clear Color");
|
||||
reset.on_update = reset_callback;
|
||||
.tooltip("Clear Color")
|
||||
.on_update(reset_callback);
|
||||
|
||||
widgets.push(WidgetHolder::related_separator());
|
||||
widgets.push(reset.widget_holder());
|
||||
|
|
@ -97,8 +99,7 @@ impl ToolColorOptions {
|
|||
widgets.push(radio);
|
||||
widgets.push(WidgetHolder::related_separator());
|
||||
|
||||
let mut color_input = ColorInput::new(self.active_color()).allow_none(color_allow_none);
|
||||
color_input.on_update = color_callback;
|
||||
let color_input = ColorInput::new(self.active_color()).allow_none(color_allow_none).on_update(color_callback);
|
||||
widgets.push(color_input.widget_holder());
|
||||
|
||||
widgets
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
|||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::portfolio::document::node_graph::transform_utils::get_current_transform;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
|
|
@ -190,9 +189,9 @@ impl PropertyHolder for BrushTool {
|
|||
widgets.append(&mut self.options.color.create_widgets(
|
||||
"Color",
|
||||
false,
|
||||
WidgetCallback::new(|_| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Color(None)).into()),
|
||||
|_| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Color(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::ColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Color(color.value)).into()),
|
||||
|color: &ColorInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Color(color.value)).into(),
|
||||
));
|
||||
|
||||
widgets.push(WidgetHolder::related_separator());
|
||||
|
|
@ -255,10 +254,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for BrushTo
|
|||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,9 +96,9 @@ impl PropertyHolder for EllipseTool {
|
|||
let mut widgets = self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColor(None)).into()),
|
||||
|_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColor(color.value)).into()),
|
||||
|color: &ColorInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColor(color.value)).into(),
|
||||
);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
|
@ -106,9 +106,9 @@ impl PropertyHolder for EllipseTool {
|
|||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColor(None)).into()),
|
||||
|_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
|color: &ColorInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColor(color.value)).into(),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
|
@ -140,10 +140,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Ellipse
|
|||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
|
|
@ -102,9 +100,9 @@ impl PropertyHolder for FreehandTool {
|
|||
let mut widgets = self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(None)).into()),
|
||||
|_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(color.value)).into()),
|
||||
|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(color.value)).into(),
|
||||
);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
|
@ -112,9 +110,9 @@ impl PropertyHolder for FreehandTool {
|
|||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColor(None)).into()),
|
||||
|_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColor(color.value)).into(),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
|
@ -146,10 +144,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Freehan
|
|||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ use crate::application::generate_uuid;
|
|||
use crate::consts::{COLOR_ACCENT, LINE_ROTATE_SNAP_ANGLE, MANIPULATOR_GROUP_MARKER_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE};
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{RadioEntryData, RadioInput};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ use crate::consts::LINE_ROTATE_SNAP_ANGLE;
|
|||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
|
|
@ -97,9 +95,9 @@ impl PropertyHolder for LineTool {
|
|||
let mut widgets = self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(None)).into()),
|
||||
|_| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
|color: &ColorInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(color.value)).into(),
|
||||
);
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
|
@ -124,10 +122,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for LineToo
|
|||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::vec;
|
|||
use crate::consts::{DRAG_THRESHOLD, SELECTION_THRESHOLD, SELECTION_TOLERANCE};
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
|
||||
use crate::messages::tool::common_functionality::shape_editor::{ManipulatorPointInfo, OpposingHandleLengths, ShapeState};
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
use crate::consts::LINE_ROTATE_SNAP_ANGLE;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::node_graph::VectorDataModification;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
|
||||
|
||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
|
@ -122,9 +119,9 @@ impl PropertyHolder for PenTool {
|
|||
let mut widgets = self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(None)).into()),
|
||||
|_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(color.value)).into()),
|
||||
|color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(color.value)).into(),
|
||||
);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
|
@ -132,9 +129,9 @@ impl PropertyHolder for PenTool {
|
|||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(None)).into()),
|
||||
|_| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
|color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(color.value)).into(),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
|
@ -166,10 +163,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PenTool
|
|||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, NumberInput, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
|
|
@ -83,9 +82,9 @@ impl PropertyHolder for RectangleTool {
|
|||
let mut widgets = self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColor(None)).into()),
|
||||
|_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColor(color.value)).into()),
|
||||
|color: &ColorInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColor(color.value)).into(),
|
||||
);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
|
@ -93,9 +92,9 @@ impl PropertyHolder for RectangleTool {
|
|||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColor(None)).into()),
|
||||
|_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
|color: &ColorInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColor(color.value)).into(),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
|
@ -127,10 +126,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Rectang
|
|||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,8 @@ use crate::consts::{ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE};
|
|||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::assist_widgets::{PivotAssist, PivotPosition};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::{IconButton, PopoverButton};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{DropdownEntryData, DropdownInput};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis};
|
||||
use crate::messages::portfolio::document::utility_types::transformation::Selected;
|
||||
use crate::messages::prelude::*;
|
||||
|
|
@ -222,11 +219,7 @@ impl PropertyHolder for SelectTool {
|
|||
.on_update(|_| SelectToolMessage::FlipVertical.into())
|
||||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "Flip".into(),
|
||||
text: "Coming soon".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
PopoverButton::new("Flip", "Coming soon").widget_holder(),
|
||||
WidgetHolder::section_separator(),
|
||||
IconButton::new("BooleanUnion", 24)
|
||||
.tooltip("Boolean Union (coming soon)")
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, NumberInput, RadioEntryData, RadioInput, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
|
|
@ -135,9 +134,9 @@ impl PropertyHolder for ShapeTool {
|
|||
widgets.append(&mut self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::FillColor(None)).into()),
|
||||
|_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::FillColor(color.value)).into()),
|
||||
|color: &ColorInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::FillColor(color.value)).into(),
|
||||
));
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
|
@ -145,9 +144,9 @@ impl PropertyHolder for ShapeTool {
|
|||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::StrokeColor(None)).into()),
|
||||
|_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
|color: &ColorInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::StrokeColor(color.value)).into(),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
|
@ -180,10 +179,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for ShapeTo
|
|||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
use crate::consts::DRAG_THRESHOLD;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
|
|
@ -105,9 +103,9 @@ impl PropertyHolder for SplineTool {
|
|||
let mut widgets = self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(None)).into()),
|
||||
|_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(color.value)).into()),
|
||||
|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(color.value)).into(),
|
||||
);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
|
@ -115,9 +113,9 @@ impl PropertyHolder for SplineTool {
|
|||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(None)).into()),
|
||||
|_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(color.value)).into(),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
|
@ -149,10 +147,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SplineT
|
|||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ use crate::application::generate_uuid;
|
|||
use crate::consts::{COLOR_ACCENT, SELECTION_TOLERANCE};
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{ColorInput, FontInput, NumberInput};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::node_graph::new_text_network;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
|
|
@ -99,34 +98,26 @@ impl ToolMetadata for TextTool {
|
|||
}
|
||||
|
||||
fn create_text_widgets(tool: &TextTool) -> Vec<WidgetHolder> {
|
||||
let font = FontInput {
|
||||
is_style_picker: false,
|
||||
font_family: tool.options.font_name.clone(),
|
||||
font_style: tool.options.font_style.clone(),
|
||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
||||
let font = FontInput::new(&tool.options.font_name, &tool.options.font_style)
|
||||
.is_style_picker(false)
|
||||
.on_update(|font_input: &FontInput| {
|
||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
||||
family: font_input.font_family.clone(),
|
||||
style: font_input.font_style.clone(),
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
.widget_holder();
|
||||
let style = FontInput {
|
||||
is_style_picker: true,
|
||||
font_family: tool.options.font_name.clone(),
|
||||
font_style: tool.options.font_style.clone(),
|
||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
||||
})
|
||||
.widget_holder();
|
||||
let style = FontInput::new(&tool.options.font_name, &tool.options.font_style)
|
||||
.is_style_picker(true)
|
||||
.on_update(|font_input: &FontInput| {
|
||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
||||
family: font_input.font_family.clone(),
|
||||
style: font_input.font_style.clone(),
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
.widget_holder();
|
||||
})
|
||||
.widget_holder();
|
||||
let size = NumberInput::new(Some(tool.options.font_size as f64))
|
||||
.unit(" px")
|
||||
.label("Size")
|
||||
|
|
@ -146,9 +137,9 @@ impl PropertyHolder for TextTool {
|
|||
widgets.append(&mut self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(None)).into()),
|
||||
|_| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(color.value)).into()),
|
||||
|color: &ColorInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(color.value)).into(),
|
||||
));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
|
|
@ -177,10 +168,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for TextToo
|
|||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,8 @@ use crate::messages::broadcast::BroadcastMessage;
|
|||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, LayoutKeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::IconButton;
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::SwatchPairInput;
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::{Separator, SeparatorDirection, SeparatorType};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::node_graph_executor::NodeGraphExecutor;
|
||||
|
||||
|
|
@ -144,29 +141,20 @@ impl DocumentToolData {
|
|||
pub fn update_working_colors(&self, responses: &mut VecDeque<Message>) {
|
||||
let layout = WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::SwatchPairInput(SwatchPairInput {
|
||||
primary: self.primary_color,
|
||||
secondary: self.secondary_color,
|
||||
}))],
|
||||
widgets: vec![SwatchPairInput::new(self.primary_color, self.secondary_color).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
size: 16,
|
||||
icon: "Swap".into(),
|
||||
tooltip: "Swap".into(),
|
||||
tooltip_shortcut: action_keys!(ToolMessageDiscriminant::SwapColors),
|
||||
on_update: WidgetCallback::new(|_| ToolMessage::SwapColors.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
size: 16,
|
||||
icon: "WorkingColors".into(),
|
||||
tooltip: "Reset".into(),
|
||||
tooltip_shortcut: action_keys!(ToolMessageDiscriminant::ResetColors),
|
||||
on_update: WidgetCallback::new(|_| ToolMessage::ResetColors.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
IconButton::new("Swap", 16)
|
||||
.tooltip("Swap")
|
||||
.tooltip_shortcut(action_keys!(ToolMessageDiscriminant::SwapColors))
|
||||
.on_update(|_| ToolMessage::SwapColors.into())
|
||||
.widget_holder(),
|
||||
IconButton::new("WorkingColors", 16)
|
||||
.tooltip("Reset")
|
||||
.tooltip_shortcut(action_keys!(ToolMessageDiscriminant::ResetColors))
|
||||
.on_update(|_| ToolMessage::ResetColors.into())
|
||||
.widget_holder(),
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
|
@ -259,36 +247,29 @@ impl PropertyHolder for ToolData {
|
|||
.iter()
|
||||
.map(|tool_group| tool_group.iter().map(|tool_availability| {
|
||||
match tool_availability {
|
||||
ToolAvailability::Available(tool) => ToolEntry {
|
||||
tooltip: tool.tooltip(),
|
||||
tooltip_shortcut: action_keys!(tool_type_to_activate_tool_message(tool.tool_type())),
|
||||
icon_name: tool.icon_name(),
|
||||
tool_type: tool.tool_type(),
|
||||
},
|
||||
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())))
|
||||
|
||||
,
|
||||
ToolAvailability::ComingSoon(tool) => tool.clone(),
|
||||
}
|
||||
}).collect::<Vec<_>>())
|
||||
.flat_map(|group| {
|
||||
let separator = std::iter::once(WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Vertical,
|
||||
separator_type: SeparatorType::Section,
|
||||
})));
|
||||
let separator = std::iter::once(Separator::new(SeparatorDirection::Vertical, SeparatorType::Section).widget_holder());
|
||||
let buttons = group.into_iter().map(|ToolEntry { tooltip, tooltip_shortcut, tool_type, icon_name }| {
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: icon_name,
|
||||
size: 32,
|
||||
disabled: false,
|
||||
active: self.active_tool_type == tool_type,
|
||||
tooltip: tooltip.clone(),
|
||||
tooltip_shortcut,
|
||||
on_update: WidgetCallback::new(move |_| {
|
||||
IconButton::new(icon_name, 32)
|
||||
.disabled( false)
|
||||
.active( self.active_tool_type == tool_type)
|
||||
.tooltip( tooltip.clone())
|
||||
.tooltip_shortcut(tooltip_shortcut)
|
||||
.on_update(move |_| {
|
||||
if !tooltip.contains("Coming Soon") {
|
||||
ToolMessage::ActivateTool { tool_type }.into()
|
||||
} else {
|
||||
DialogMessage::RequestComingSoonDialog { issue: None }.into()
|
||||
}
|
||||
}),
|
||||
}))
|
||||
}).widget_holder()
|
||||
});
|
||||
|
||||
separator.chain(buttons)
|
||||
|
|
@ -303,12 +284,15 @@ impl PropertyHolder for ToolData {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default, WidgetBuilder)]
|
||||
#[widget_builder(not_widget_holder)]
|
||||
pub struct ToolEntry {
|
||||
#[widget_builder(constructor)]
|
||||
pub tool_type: ToolType,
|
||||
#[widget_builder(constructor)]
|
||||
pub icon_name: String,
|
||||
pub tooltip: String,
|
||||
pub tooltip_shortcut: Option<ActionKeys>,
|
||||
pub icon_name: String,
|
||||
pub tool_type: ToolType,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -346,9 +330,10 @@ impl ToolFsmState {
|
|||
}
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default, specta::Type)]
|
||||
pub enum ToolType {
|
||||
// General tool group
|
||||
#[default]
|
||||
Select,
|
||||
Artboard,
|
||||
Navigate,
|
||||
|
|
@ -412,36 +397,11 @@ fn list_tools_in_groups() -> Vec<Vec<ToolAvailability>> {
|
|||
ToolAvailability::Available(Box::<frame_tool::FrameTool>::default()),
|
||||
ToolAvailability::Available(Box::<imaginate_tool::ImaginateTool>::default()),
|
||||
ToolAvailability::Available(Box::<brush_tool::BrushTool>::default()),
|
||||
ToolAvailability::ComingSoon(ToolEntry {
|
||||
tool_type: ToolType::Heal,
|
||||
icon_name: "RasterHealTool".into(),
|
||||
tooltip: "Coming Soon: Heal Tool (J)".into(),
|
||||
tooltip_shortcut: None,
|
||||
}),
|
||||
ToolAvailability::ComingSoon(ToolEntry {
|
||||
tool_type: ToolType::Clone,
|
||||
icon_name: "RasterCloneTool".into(),
|
||||
tooltip: "Coming Soon: Clone Tool (C)".into(),
|
||||
tooltip_shortcut: None,
|
||||
}),
|
||||
ToolAvailability::ComingSoon(ToolEntry {
|
||||
tool_type: ToolType::Patch,
|
||||
icon_name: "RasterPatchTool".into(),
|
||||
tooltip: "Coming Soon: Patch Tool".into(),
|
||||
tooltip_shortcut: None,
|
||||
}),
|
||||
ToolAvailability::ComingSoon(ToolEntry {
|
||||
tool_type: ToolType::Detail,
|
||||
icon_name: "RasterDetailTool".into(),
|
||||
tooltip: "Coming Soon: Detail Tool (D)".into(),
|
||||
tooltip_shortcut: None,
|
||||
}),
|
||||
ToolAvailability::ComingSoon(ToolEntry {
|
||||
tool_type: ToolType::Relight,
|
||||
icon_name: "RasterRelightTool".into(),
|
||||
tooltip: "Coming Soon: Relight Tool (O)".into(),
|
||||
tooltip_shortcut: None,
|
||||
}),
|
||||
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)")),
|
||||
],
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue