Skip to content

Remake Eyedropper tool to sample pixel colors from viewport canvas #801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl PropertyHolder for ExportDialogMessageHandler {
})),
];

let entries = [(FileType::Svg, "SVG"), (FileType::Png, "PNG"), (FileType::Jpg, "JPG")]
let entries = [(FileType::Png, "PNG"), (FileType::Jpg, "JPG"), (FileType::Svg, "SVG")]
.into_iter()
.map(|(val, name)| RadioEntryData {
label: name.into(),
Expand Down
10 changes: 10 additions & 0 deletions editor/src/messages/frontend/frontend_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,16 @@ pub enum FrontendMessage {
size: (f64, f64),
multiplier: (f64, f64),
},
UpdateEyedropperSamplingState {
#[serde(rename = "mousePosition")]
mouse_position: Option<(f64, f64)>,
#[serde(rename = "primaryColor")]
primary_color: String,
#[serde(rename = "secondaryColor")]
secondary_color: String,
#[serde(rename = "setColorChoice")]
set_color_choice: Option<String>,
},
UpdateImageData {
#[serde(rename = "documentId")]
document_id: u64,
Expand Down
5 changes: 3 additions & 2 deletions editor/src/messages/frontend/utility_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct FrontendImageData {
pub enum MouseCursorIcon {
#[default]
Default,
None,
ZoomIn,
ZoomOut,
Grabbing,
Expand All @@ -36,17 +37,17 @@ pub enum MouseCursorIcon {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub enum FileType {
#[default]
Svg,
Png,
Jpg,
Svg,
}

impl FileType {
pub fn to_mime(self) -> &'static str {
match self {
FileType::Svg => "image/svg+xml",
FileType::Png => "image/png",
FileType::Jpg => "image/jpeg",
FileType::Svg => "image/svg+xml",
}
}
}
Expand Down
12 changes: 8 additions & 4 deletions editor/src/messages/input_mapper/default_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,12 @@ pub fn default_mapping() -> Mapping {
entry!(KeyUp(Mmb); action_dispatch=NavigateToolMessage::TransformCanvasEnd),
//
// EyedropperToolMessage
entry!(KeyDown(Lmb); action_dispatch=EyedropperToolMessage::LeftMouseDown),
entry!(KeyDown(Rmb); action_dispatch=EyedropperToolMessage::RightMouseDown),
entry!(PointerMove; action_dispatch=EyedropperToolMessage::PointerMove),
entry!(KeyDown(Lmb); action_dispatch=EyedropperToolMessage::LeftPointerDown),
entry!(KeyDown(Rmb); action_dispatch=EyedropperToolMessage::RightPointerDown),
entry!(KeyUp(Lmb); action_dispatch=EyedropperToolMessage::LeftPointerUp),
entry!(KeyUp(Rmb); action_dispatch=EyedropperToolMessage::RightPointerUp),
entry!(KeyDown(Escape); action_dispatch=EyedropperToolMessage::Abort),
//
// TextToolMessage
entry!(KeyUp(Lmb); action_dispatch=TextToolMessage::Interact),
Expand Down Expand Up @@ -170,8 +174,8 @@ pub fn default_mapping() -> Mapping {
entry!(KeyDown(Enter); action_dispatch=SplineToolMessage::Confirm),
//
// FillToolMessage
entry!(KeyDown(Lmb); action_dispatch=FillToolMessage::LeftMouseDown),
entry!(KeyDown(Rmb); action_dispatch=FillToolMessage::RightMouseDown),
entry!(KeyDown(Lmb); action_dispatch=FillToolMessage::LeftPointerDown),
entry!(KeyDown(Rmb); action_dispatch=FillToolMessage::RightPointerDown),
//
// ToolMessage
entry!(KeyDown(KeyV); action_dispatch=ToolMessage::ActivateToolSelect),
Expand Down
4 changes: 4 additions & 0 deletions editor/src/messages/input_mapper/utility_types/input_mouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ impl ViewportBounds {
pub fn center(&self) -> DVec2 {
self.bottom_right.lerp(self.top_left, 0.5)
}

pub fn in_bounds(&self, position: ViewportPosition) -> bool {
position.x >= 0. && position.y >= 0. && position.x <= self.bottom_right.x && position.y <= self.bottom_right.y
}
}

#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -910,8 +910,8 @@ fn node_section_imaginate(imaginate_layer: &ImaginateLayer, layer: &Layer, persi
\n\
Include an artist name like \"Rembrandt\" or art medium like \"watercolor\" or \"photography\" to influence the look. List multiple to meld styles.\n\
\n\
To boost the importance of a word or phrase, wrap it in quotes ending with a colon and a multiplier, for example:\n\
\"(colorless:0.7) green (ideas sleep:1.3) furiously\"
To boost (or lessen) the importance of a word or phrase, wrap it in quotes ending with a colon and a multiplier, for example:\n\
\"Colorless green ideas (sleep:1.3) furiously\"
"
.trim()
.into(),
Expand Down
4 changes: 1 addition & 3 deletions editor/src/messages/tool/tool_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
return;
}

// Send the Abort state transition to the tool
// Send the old and new tools a transition to their FSM Abort states
let mut send_abort_to_tool = |tool_type, update_hints_and_cursor: bool| {
if let Some(tool) = tool_data.tools.get_mut(&tool_type) {
if let Some(tool_abort_message) = tool.event_to_message_map().tool_abort {
Expand All @@ -82,8 +82,6 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
}
}
};

// Send the old and new tools a transition to their FSM Abort states
send_abort_to_tool(tool_type, true);
send_abort_to_tool(old_tool, false);

Expand Down
108 changes: 78 additions & 30 deletions editor/src/messages/tool/tool_messages/eyedropper_tool.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
use crate::consts::SELECTION_TOLERANCE;
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::PropertyHolder;
use crate::messages::prelude::*;
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
use crate::messages::tool::utility_types::{DocumentToolData, EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};

use graphene::intersection::Quad;
use graphene::layers::layer_info::LayerDataType;

use glam::DVec2;
use serde::{Deserialize, Serialize};

#[derive(Default)]
Expand All @@ -27,8 +22,11 @@ pub enum EyedropperToolMessage {
Abort,

// Tool-specific messages
LeftMouseDown,
RightMouseDown,
LeftPointerDown,
LeftPointerUp,
PointerMove,
RightPointerDown,
RightPointerUp,
}

impl ToolMetadata for EyedropperTool {
Expand Down Expand Up @@ -67,8 +65,12 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EyedropperTo
}

advertise_actions!(EyedropperToolMessageDiscriminant;
LeftMouseDown,
RightMouseDown,
LeftPointerDown,
LeftPointerUp,
PointerMove,
RightPointerDown,
RightPointerUp,
Abort,
);
}

Expand All @@ -85,6 +87,8 @@ impl ToolTransition for EyedropperTool {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum EyedropperToolFsmState {
Ready,
SamplingPrimary,
SamplingSecondary,
}

impl Default for EyedropperToolFsmState {
Expand All @@ -104,7 +108,7 @@ impl Fsm for EyedropperToolFsmState {
self,
event: ToolMessage,
_tool_data: &mut Self::ToolData,
(document, _document_id, _global_tool_data, input, font_cache): ToolActionHandlerData,
(_document, _document_id, global_tool_data, input, _font_cache): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
Expand All @@ -113,28 +117,41 @@ impl Fsm for EyedropperToolFsmState {

if let ToolMessage::Eyedropper(event) = event {
match (self, event) {
(Ready, lmb_or_rmb) if lmb_or_rmb == LeftMouseDown || lmb_or_rmb == RightMouseDown => {
let mouse_pos = input.mouse.position;
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);

// TODO: Destroy this pyramid
if let Some(path) = document.graphene_document.intersects_quad_root(quad, font_cache).last() {
if let Ok(layer) = document.graphene_document.layer(path) {
if let LayerDataType::Shape(shape) = &layer.data {
if shape.style.fill().is_some() {
match lmb_or_rmb {
EyedropperToolMessage::LeftMouseDown => responses.push_back(ToolMessage::SelectPrimaryColor { color: shape.style.fill().color() }.into()),
EyedropperToolMessage::RightMouseDown => responses.push_back(ToolMessage::SelectSecondaryColor { color: shape.style.fill().color() }.into()),
_ => {}
}
}
}
}
// Ready -> Sampling
(Ready, mouse_down) | (Ready, mouse_down) if mouse_down == LeftPointerDown || mouse_down == RightPointerDown => {
update_cursor_preview(responses, input, global_tool_data, None);

if mouse_down == LeftPointerDown {
SamplingPrimary
} else {
SamplingSecondary
}
}
// Sampling -> Sampling
(SamplingPrimary, PointerMove) | (SamplingSecondary, PointerMove) => {
if input.viewport_bounds.in_bounds(input.mouse.position) {
update_cursor_preview(responses, input, global_tool_data, None);
} else {
disable_cursor_preview(responses);
}

self
}
// Sampling -> Ready
(SamplingPrimary, mouse_up) | (SamplingSecondary, mouse_up) if mouse_up == LeftPointerUp || mouse_up == RightPointerUp => {
let set_color_choice = if self == SamplingPrimary { "Primary".to_string() } else { "Secondary".to_string() };
update_cursor_preview(responses, input, global_tool_data, Some(set_color_choice));
disable_cursor_preview(responses);

Ready
}
// Any -> Ready
(_, Abort) => {
disable_cursor_preview(responses);

Ready
}
// Ready -> Ready
_ => self,
}
} else {
Expand All @@ -160,12 +177,43 @@ impl Fsm for EyedropperToolFsmState {
plus: false,
},
])]),
EyedropperToolFsmState::SamplingPrimary => HintData(vec![]),
EyedropperToolFsmState::SamplingSecondary => HintData(vec![]),
};

responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
}

fn update_cursor(&self, responses: &mut VecDeque<Message>) {
responses.push_back(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default }.into());
let cursor = match *self {
EyedropperToolFsmState::Ready => MouseCursorIcon::Default,
EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary => MouseCursorIcon::None,
};

responses.push_back(FrontendMessage::UpdateMouseCursor { cursor }.into());
}
}

fn disable_cursor_preview(responses: &mut VecDeque<Message>) {
responses.push_back(
FrontendMessage::UpdateEyedropperSamplingState {
mouse_position: None,
primary_color: "".into(),
secondary_color: "".into(),
set_color_choice: None,
}
.into(),
);
}

fn update_cursor_preview(responses: &mut VecDeque<Message>, input: &InputPreprocessorMessageHandler, global_tool_data: &DocumentToolData, set_color_choice: Option<String>) {
responses.push_back(
FrontendMessage::UpdateEyedropperSamplingState {
mouse_position: Some(input.mouse.position.into()),
primary_color: "#".to_string() + global_tool_data.primary_color.rgb_hex().as_str(),
secondary_color: "#".to_string() + global_tool_data.secondary_color.rgb_hex().as_str(),
set_color_choice,
}
.into(),
);
}
14 changes: 7 additions & 7 deletions editor/src/messages/tool/tool_messages/fill_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ pub enum FillToolMessage {
Abort,

// Tool-specific messages
LeftMouseDown,
RightMouseDown,
LeftPointerDown,
RightPointerDown,
}

impl ToolMetadata for FillTool {
Expand Down Expand Up @@ -68,8 +68,8 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FillTool {
}

advertise_actions!(FillToolMessageDiscriminant;
LeftMouseDown,
RightMouseDown,
LeftPointerDown,
RightPointerDown,
);
}

Expand Down Expand Up @@ -114,15 +114,15 @@ impl Fsm for FillToolFsmState {

if let ToolMessage::Fill(event) = event {
match (self, event) {
(Ready, lmb_or_rmb) if lmb_or_rmb == LeftMouseDown || lmb_or_rmb == RightMouseDown => {
(Ready, lmb_or_rmb) if lmb_or_rmb == LeftPointerDown || lmb_or_rmb == RightPointerDown => {
let mouse_pos = input.mouse.position;
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);

if let Some(path) = document.graphene_document.intersects_quad_root(quad, font_cache).last() {
let color = match lmb_or_rmb {
LeftMouseDown => global_tool_data.primary_color,
RightMouseDown => global_tool_data.secondary_color,
LeftPointerDown => global_tool_data.primary_color,
RightPointerDown => global_tool_data.secondary_color,
Abort => unreachable!(),
};
let fill = Fill::Solid(color);
Expand Down
2 changes: 2 additions & 0 deletions editor/src/messages/tool/utility_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ impl DocumentToolData {
}
.into(),
);

responses.push_back(EyedropperToolMessage::PointerMove.into());
}
}

Expand Down
Loading