mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 23:48:11 +08:00
Desktop: Add Eyedropper tool support with native Vello (#3684)
* mostly done * fix * kinda works but tilt and flip broken * fix footprint Co-authored-by: James Lindsay <78500760+0HyperCube@users.noreply.github.com> * Code review * fix cursor hiding * Remove console.log --------- Co-authored-by: James Lindsay <78500760+0HyperCube@users.noreply.github.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
James Lindsay
Keavon Chambers
parent
19e9af3d43
commit
5fd1a24f16
@@ -1,5 +1,6 @@
|
||||
use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
|
||||
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
||||
use crate::messages::frontend::utility_types::EyedropperPreviewImage;
|
||||
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::node_graph::utility_types::{
|
||||
@@ -253,6 +254,7 @@ pub enum FrontendMessage {
|
||||
multiplier: (f64, f64),
|
||||
},
|
||||
UpdateEyedropperSamplingState {
|
||||
image: Option<EyedropperPreviewImage>,
|
||||
#[serde(rename = "mousePosition")]
|
||||
mouse_position: Option<(f64, f64)>,
|
||||
#[serde(rename = "primaryColor")]
|
||||
|
||||
@@ -62,3 +62,10 @@ pub enum ExportBounds {
|
||||
Selection,
|
||||
Artboard(LayerNodeIdentifier),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub struct EyedropperPreviewImage {
|
||||
pub data: Vec<u8>,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ pub enum PortfolioMessage {
|
||||
document_id: DocumentId,
|
||||
ignore_hash: bool,
|
||||
},
|
||||
SubmitEyedropperPreviewRender,
|
||||
ToggleResetNodesToDefinitionsOnOpen,
|
||||
ToggleFocusDocument,
|
||||
ToggleDataPanelOpen,
|
||||
|
||||
@@ -1178,6 +1178,41 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
Ok(message) => responses.add_front(message),
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
PortfolioMessage::SubmitEyedropperPreviewRender => {
|
||||
use crate::consts::EYEDROPPER_PREVIEW_AREA_RESOLUTION;
|
||||
|
||||
let Some(document_id) = self.active_document_id else { return };
|
||||
let Some(document) = self.documents.get_mut(&document_id) else { return };
|
||||
|
||||
let resolution = glam::UVec2::splat(EYEDROPPER_PREVIEW_AREA_RESOLUTION);
|
||||
|
||||
let preview_offset_in_viewport = ipp.mouse.position - (glam::DVec2::splat(EYEDROPPER_PREVIEW_AREA_RESOLUTION as f64 / 2.));
|
||||
let preview_offset_in_viewport = DAffine2::from_translation(preview_offset_in_viewport);
|
||||
|
||||
let document_to_viewport = document.metadata().document_to_viewport;
|
||||
|
||||
let preview_transform = preview_offset_in_viewport.inverse() * document_to_viewport;
|
||||
let pointer_position = document_to_viewport.inverse().transform_point2(ipp.mouse.position);
|
||||
|
||||
let result = self
|
||||
.executor
|
||||
.submit_eyedropper_preview(document_id, preview_transform, pointer_position, resolution, timing_information);
|
||||
|
||||
match result {
|
||||
Err(description) => {
|
||||
responses.add(DialogMessage::DisplayDialogError {
|
||||
title: "Unable to update node graph".to_string(),
|
||||
description,
|
||||
});
|
||||
}
|
||||
Ok(message) => responses.add_front(message),
|
||||
}
|
||||
}
|
||||
#[cfg(target_family = "wasm")]
|
||||
PortfolioMessage::SubmitEyedropperPreviewRender => {
|
||||
// TODO: Currently for Wasm, this is implemented through SVG rendering but the Eyedropper tool doesn't work at all when Vello is enabled as the renderer
|
||||
}
|
||||
PortfolioMessage::ToggleFocusDocument => {
|
||||
self.focus_document = !self.focus_document;
|
||||
responses.add(MenuBarMessage::SendLayout);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::tool_prelude::*;
|
||||
use crate::messages::frontend::utility_types::EyedropperPreviewImage;
|
||||
use crate::messages::tool::utility_types::DocumentToolData;
|
||||
|
||||
#[derive(Default, ExtractField)]
|
||||
@@ -19,6 +20,8 @@ pub enum EyedropperToolMessage {
|
||||
PointerMove,
|
||||
SampleSecondaryColorBegin,
|
||||
SampleSecondaryColorEnd,
|
||||
|
||||
PreviewImage { data: Vec<u8>, width: u32, height: u32 },
|
||||
}
|
||||
|
||||
impl ToolMetadata for EyedropperTool {
|
||||
@@ -42,6 +45,17 @@ impl LayoutHolder for EyedropperTool {
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for EyedropperTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||
if let ToolMessage::Eyedropper(EyedropperToolMessage::PreviewImage { data, width, height }) = message {
|
||||
let image = EyedropperPreviewImage { data, width, height };
|
||||
|
||||
update_cursor_preview_common(responses, Some(image), context.input, context.global_tool_data, self.data.color_choice.clone());
|
||||
|
||||
if !self.data.preview {
|
||||
disable_cursor_preview(responses, &mut self.data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.fsm_state.process_event(message, &mut self.data, context, &(), responses, true);
|
||||
}
|
||||
|
||||
@@ -74,13 +88,16 @@ enum EyedropperToolFsmState {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct EyedropperToolData {}
|
||||
struct EyedropperToolData {
|
||||
preview: bool,
|
||||
color_choice: Option<String>,
|
||||
}
|
||||
|
||||
impl Fsm for EyedropperToolFsmState {
|
||||
type ToolData = EyedropperToolData;
|
||||
type ToolOptions = ();
|
||||
|
||||
fn transition(self, event: ToolMessage, _tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
||||
let ToolActionMessageContext {
|
||||
global_tool_data, input, viewport, ..
|
||||
} = tool_action_data;
|
||||
@@ -89,7 +106,7 @@ impl Fsm for EyedropperToolFsmState {
|
||||
match (self, event) {
|
||||
// Ready -> Sampling
|
||||
(EyedropperToolFsmState::Ready, mouse_down) if matches!(mouse_down, EyedropperToolMessage::SamplePrimaryColorBegin | EyedropperToolMessage::SampleSecondaryColorBegin) => {
|
||||
update_cursor_preview(responses, input, global_tool_data, None);
|
||||
update_cursor_preview(responses, tool_data, input, global_tool_data, None);
|
||||
|
||||
if mouse_down == EyedropperToolMessage::SamplePrimaryColorBegin {
|
||||
EyedropperToolFsmState::SamplingPrimary
|
||||
@@ -101,9 +118,9 @@ impl Fsm for EyedropperToolFsmState {
|
||||
(EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary, EyedropperToolMessage::PointerMove) => {
|
||||
let mouse_position = viewport.logical(input.mouse.position);
|
||||
if viewport.is_in_bounds(mouse_position + viewport.offset()) {
|
||||
update_cursor_preview(responses, input, global_tool_data, None);
|
||||
update_cursor_preview(responses, tool_data, input, global_tool_data, None);
|
||||
} else {
|
||||
disable_cursor_preview(responses);
|
||||
disable_cursor_preview(responses, tool_data);
|
||||
}
|
||||
|
||||
self
|
||||
@@ -111,14 +128,14 @@ impl Fsm for EyedropperToolFsmState {
|
||||
// Sampling -> Ready
|
||||
(EyedropperToolFsmState::SamplingPrimary, EyedropperToolMessage::SamplePrimaryColorEnd) | (EyedropperToolFsmState::SamplingSecondary, EyedropperToolMessage::SampleSecondaryColorEnd) => {
|
||||
let set_color_choice = if self == EyedropperToolFsmState::SamplingPrimary { "Primary" } else { "Secondary" }.to_string();
|
||||
update_cursor_preview(responses, input, global_tool_data, Some(set_color_choice));
|
||||
disable_cursor_preview(responses);
|
||||
update_cursor_preview(responses, tool_data, input, global_tool_data, Some(set_color_choice));
|
||||
disable_cursor_preview(responses, tool_data);
|
||||
|
||||
EyedropperToolFsmState::Ready
|
||||
}
|
||||
// Any -> Ready
|
||||
(_, EyedropperToolMessage::Abort) => {
|
||||
disable_cursor_preview(responses);
|
||||
disable_cursor_preview(responses, tool_data);
|
||||
|
||||
EyedropperToolFsmState::Ready
|
||||
}
|
||||
@@ -151,8 +168,10 @@ impl Fsm for EyedropperToolFsmState {
|
||||
}
|
||||
}
|
||||
|
||||
fn disable_cursor_preview(responses: &mut VecDeque<Message>) {
|
||||
fn disable_cursor_preview(responses: &mut VecDeque<Message>, tool_data: &mut EyedropperToolData) {
|
||||
tool_data.preview = false;
|
||||
responses.add(FrontendMessage::UpdateEyedropperSamplingState {
|
||||
image: None,
|
||||
mouse_position: None,
|
||||
primary_color: "".into(),
|
||||
secondary_color: "".into(),
|
||||
@@ -160,8 +179,42 @@ fn disable_cursor_preview(responses: &mut VecDeque<Message>) {
|
||||
});
|
||||
}
|
||||
|
||||
fn update_cursor_preview(responses: &mut VecDeque<Message>, input: &InputPreprocessorMessageHandler, global_tool_data: &DocumentToolData, set_color_choice: Option<String>) {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
fn update_cursor_preview(
|
||||
responses: &mut VecDeque<Message>,
|
||||
tool_data: &mut EyedropperToolData,
|
||||
_input: &InputPreprocessorMessageHandler,
|
||||
_global_tool_data: &DocumentToolData,
|
||||
set_color_choice: Option<String>,
|
||||
) {
|
||||
tool_data.preview = true;
|
||||
tool_data.color_choice = set_color_choice;
|
||||
responses.add(PortfolioMessage::SubmitEyedropperPreviewRender);
|
||||
}
|
||||
|
||||
#[cfg(target_family = "wasm")]
|
||||
fn update_cursor_preview(
|
||||
responses: &mut VecDeque<Message>,
|
||||
tool_data: &mut EyedropperToolData,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
global_tool_data: &DocumentToolData,
|
||||
set_color_choice: Option<String>,
|
||||
) {
|
||||
tool_data.preview = true;
|
||||
tool_data.color_choice = set_color_choice.clone();
|
||||
|
||||
update_cursor_preview_common(responses, None, input, global_tool_data, set_color_choice);
|
||||
}
|
||||
|
||||
fn update_cursor_preview_common(
|
||||
responses: &mut VecDeque<Message>,
|
||||
image: Option<EyedropperPreviewImage>,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
global_tool_data: &DocumentToolData,
|
||||
set_color_choice: Option<String>,
|
||||
) {
|
||||
responses.add(FrontendMessage::UpdateEyedropperSamplingState {
|
||||
image,
|
||||
mouse_position: Some(input.mouse.position.into()),
|
||||
primary_color: "#".to_string() + global_tool_data.primary_color.to_rgb_hex_srgb().as_str(),
|
||||
secondary_color: "#".to_string() + global_tool_data.secondary_color.to_rgb_hex_srgb().as_str(),
|
||||
|
||||
Reference in New Issue
Block a user