mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 01:38:11 +08:00
Add drag-and-drop support to the file picker dropdown widget (#4551)
Add drag-and-drop file ingest into file picker dropdowns
This commit is contained in:
@@ -3,6 +3,7 @@ use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::message::Message;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
|
||||
use crate::messages::portfolio::ingest::utility_types::IngestAction;
|
||||
use derivative::*;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::color::SRGBA8;
|
||||
@@ -83,6 +84,9 @@ pub struct DropdownInput {
|
||||
pub virtual_scrolling: bool,
|
||||
#[derivative(Default(value = "true"))]
|
||||
pub interactive: bool,
|
||||
// Where a file dropped on the widget is sent, which also makes the widget take dropped files
|
||||
#[serde(rename = "fileDropAction")]
|
||||
pub file_drop_action: Option<IngestAction>,
|
||||
|
||||
// Sizing
|
||||
#[serde(rename = "minWidth")]
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions:
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface};
|
||||
use crate::messages::portfolio::fonts::utility_types::FontCatalogStyle;
|
||||
use crate::messages::portfolio::ingest::utility_types::TypeFilter;
|
||||
use crate::messages::portfolio::ingest::utility_types::{IngestAction, TypeFilter};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use choice::enum_choice;
|
||||
@@ -1290,7 +1290,7 @@ pub fn font_widget(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup
|
||||
font_widgets.into_iter().chain(style_widgets.unwrap_or_default()).collect::<Vec<_>>().into()
|
||||
}
|
||||
|
||||
/// A dropdown of the document's uploaded files, led by "None" and a "Browse…" entry that uploads another file matching the given filters.
|
||||
/// A dropdown of the document's uploaded files, led by "None" and a "Browse…" entry that uploads another file matching the given filters, as dropping a file onto it also does.
|
||||
pub fn resource_widget(parameter_widgets_info: ParameterWidgetsInfo, filters: Vec<TypeFilter>) -> Vec<WidgetInstance> {
|
||||
let mut widgets = start_widgets(¶meter_widgets_info);
|
||||
|
||||
@@ -1351,6 +1351,8 @@ pub fn resource_widget(parameter_widgets_info: ParameterWidgetsInfo, filters: Ve
|
||||
]),
|
||||
}
|
||||
};
|
||||
let file_drop_action = IngestAction::resource_input(document_id, node_id, index, &filters);
|
||||
|
||||
let none = MenuListEntry::new("none")
|
||||
.label("None")
|
||||
.tooltip_description("No resource assigned to this input.")
|
||||
@@ -1386,7 +1388,10 @@ pub fn resource_widget(parameter_widgets_info: ParameterWidgetsInfo, filters: Ve
|
||||
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
DropdownInput::new(vec![vec![none, browse], file_entries]).selected_index(selected_index).widget_instance(),
|
||||
DropdownInput::new(vec![vec![none, browse], file_entries])
|
||||
.selected_index(selected_index)
|
||||
.file_drop_action(Some(file_drop_action))
|
||||
.widget_instance(),
|
||||
]);
|
||||
widgets
|
||||
}
|
||||
|
||||
@@ -33,18 +33,9 @@ impl MessageHandler<IngestMessage, IngestMessageContext> for IngestMessageHandle
|
||||
input_index,
|
||||
filters,
|
||||
} => {
|
||||
let accepted_types = filters.iter().flat_map(|filter| filter.types.iter().copied()).collect();
|
||||
let action = IngestAction::resource_input(document_id, node_id, input_index, &filters);
|
||||
|
||||
responses.add(IngestMessage::Browse {
|
||||
filters,
|
||||
multiple: false,
|
||||
action: IngestAction::ResourceInput {
|
||||
document_id,
|
||||
node_id,
|
||||
input_index: input_index as u32,
|
||||
accepted_types,
|
||||
},
|
||||
});
|
||||
responses.add(IngestMessage::Browse { filters, multiple: false, action });
|
||||
}
|
||||
IngestMessage::Browse { filters, multiple, action } => responses.add(FrontendMessage::TriggerBrowse {
|
||||
options: FileDialogOptions {
|
||||
@@ -282,6 +273,24 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn browsing_for_a_resource_input_carries_the_action_a_dropped_file_uses() {
|
||||
let filters = vec![TypeFilter::documents(), TypeFilter::raster()];
|
||||
let accepted_types = [TypeFilter::documents().types, TypeFilter::raster().types].concat();
|
||||
let dropped = IngestAction::resource_input(REQUESTING_DOCUMENT, NodeId(7), 1, &filters);
|
||||
assert_eq!(dropped, resource_input(accepted_types));
|
||||
|
||||
let mut responses = VecDeque::new();
|
||||
let message = IngestMessage::SetResourceInput {
|
||||
document_id: REQUESTING_DOCUMENT,
|
||||
node_id: NodeId(7),
|
||||
input_index: 1,
|
||||
filters,
|
||||
};
|
||||
IngestMessageHandler::default().process_message(message, &mut responses, IngestMessageContext { document_open: true });
|
||||
assert!(matches!(&responses[0], Message::Portfolio(PortfolioMessage::Ingest(IngestMessage::Browse { action, .. })) if *action == dropped));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truncated_image_only_shows_a_dialog() {
|
||||
let png = Image::new(8, 8, Color::WHITE).to_png();
|
||||
|
||||
@@ -39,6 +39,18 @@ pub enum IngestAction {
|
||||
},
|
||||
}
|
||||
|
||||
impl IngestAction {
|
||||
/// Sends a file to a node's resource input, which takes only the types that its filters list.
|
||||
pub fn resource_input(document_id: DocumentId, node_id: NodeId, input_index: usize, filters: &[TypeFilter]) -> Self {
|
||||
Self::ResourceInput {
|
||||
document_id,
|
||||
node_id,
|
||||
input_index: input_index as u32,
|
||||
accepted_types: filters.iter().flat_map(|filter| filter.types.iter().copied()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum DataType {
|
||||
GraphiteLegacy,
|
||||
|
||||
Reference in New Issue
Block a user