Skip to content
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
11 changes: 8 additions & 3 deletions editor/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,10 +359,15 @@ impl Dispatcher {
/// with a discriminant or the entire payload (depending on settings)
fn log_message(&self, message: &Message, queues: &[VecDeque<Message>], message_logging_verbosity: MessageLoggingVerbosity) {
let discriminant = MessageDiscriminant::from(message);
let is_blocked = DEBUG_MESSAGE_BLOCK_LIST.contains(&discriminant) || DEBUG_MESSAGE_ENDING_BLOCK_LIST.iter().any(|blocked_name| discriminant.local_name().ends_with(blocked_name));
let is_empty_batched = if let Message::Batched { messages } = message { messages.is_empty() } else { false };
let is_blocked =
|discriminant| DEBUG_MESSAGE_BLOCK_LIST.contains(&discriminant) || DEBUG_MESSAGE_ENDING_BLOCK_LIST.iter().any(|blocked_name| discriminant.local_name().ends_with(blocked_name));
let is_batch_all_blocked = if let Message::Batched { messages } = message {
messages.iter().all(|message| is_blocked(MessageDiscriminant::from(message)))
} else {
false
};

if !is_blocked && !is_empty_batched {
if !is_blocked(discriminant) && !is_batch_all_blocked {
match message_logging_verbosity {
MessageLoggingVerbosity::Off => {}
MessageLoggingVerbosity::Names => {
Expand Down
5 changes: 5 additions & 0 deletions editor/src/messages/frontend/frontend_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ pub enum FrontendMessage {
#[serde(rename = "documentId")]
document_id: DocumentId,
},
UpdateGradientStopColorPickerPosition {
color: Color,
x: f64,
y: f64,
},
UpdateImportsExports {
/// If the primary import is not visible, then it is None.
imports: Vec<Option<FrontendGraphOutput>>,
Expand Down
167 changes: 138 additions & 29 deletions editor/src/messages/tool/tool_messages/gradient_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::graph_modification_utils::{NodeGraphLayer, get_gradient};
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration};
use graphene_std::raster::color::Color;
use graphene_std::vector::style::{Fill, Gradient, GradientStops, GradientType};

#[derive(Default, ExtractField)]
Expand Down Expand Up @@ -38,6 +39,10 @@ pub enum GradientToolMessage {
PointerMove { constrain_axis: Key, lock_angle: Key },
PointerOutsideViewport { constrain_axis: Key, lock_angle: Key },
PointerUp,
StartTransactionForColorStop,
CommitTransactionForColorStop,
CloseStopColorPicker,
UpdateStopColor { color: Color },
UpdateOptions { options: GradientOptionsUpdate },
}

Expand All @@ -63,29 +68,59 @@ impl ToolMetadata for GradientTool {
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for GradientTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
let ToolMessage::Gradient(GradientToolMessage::UpdateOptions { options }) = message else {
self.fsm_state.process_event(message, &mut self.data, context, &self.options, responses, false);

let has_gradient = has_gradient_on_selected_layers(context.document);
if has_gradient != self.data.has_selected_gradient {
self.data.has_selected_gradient = has_gradient;
responses.add(ToolMessage::RefreshToolOptions);
match message {
ToolMessage::Gradient(GradientToolMessage::UpdateOptions { options }) => match options {
GradientOptionsUpdate::Type(gradient_type) => {
self.options.gradient_type = gradient_type;
apply_gradient_update(&mut self.data, context, responses, |g| g.gradient_type != gradient_type, |g| g.gradient_type = gradient_type);
responses.add(ToolMessage::UpdateHints);
responses.add(ToolMessage::UpdateCursor);
}
GradientOptionsUpdate::ReverseStops => {
apply_gradient_update(&mut self.data, context, responses, |_| true, |g| g.stops = g.stops.reversed());
}
GradientOptionsUpdate::ReverseDirection => {
apply_gradient_update(&mut self.data, context, responses, |_| true, |g| std::mem::swap(&mut g.start, &mut g.end));
}
},
ToolMessage::Gradient(GradientToolMessage::StartTransactionForColorStop) => {
if self.data.color_picker_transaction_open {
responses.add(DocumentMessage::EndTransaction);
}
responses.add(DocumentMessage::StartTransaction);
self.data.color_picker_transaction_open = true;
}

return;
};
match options {
GradientOptionsUpdate::Type(gradient_type) => {
self.options.gradient_type = gradient_type;
apply_gradient_update(&mut self.data, context, responses, |g| g.gradient_type != gradient_type, |g| g.gradient_type = gradient_type);
responses.add(ToolMessage::UpdateHints);
responses.add(ToolMessage::UpdateCursor);
ToolMessage::Gradient(GradientToolMessage::CommitTransactionForColorStop) => {
if self.data.color_picker_transaction_open {
responses.add(DocumentMessage::EndTransaction);
self.data.color_picker_transaction_open = false;
}
}
ToolMessage::Gradient(GradientToolMessage::UpdateStopColor { color }) => {
if let Some(stop_index) = self.data.color_picker_editing_color_stop
&& let Some(selected_gradient) = &mut self.data.selected_gradient
&& stop_index < selected_gradient.gradient.stops.color.len()
{
selected_gradient.gradient.stops.color[stop_index] = color;
selected_gradient.render_gradient(responses);
responses.add(PropertiesPanelMessage::Refresh);
}
}
GradientOptionsUpdate::ReverseStops => {
apply_gradient_update(&mut self.data, context, responses, |_| true, |g| g.stops = g.stops.reversed());
ToolMessage::Gradient(GradientToolMessage::CloseStopColorPicker) => {
if self.data.color_picker_transaction_open {
responses.add(DocumentMessage::EndTransaction);
self.data.color_picker_transaction_open = false;
}
self.data.color_picker_editing_color_stop = None;
}
GradientOptionsUpdate::ReverseDirection => {
apply_gradient_update(&mut self.data, context, responses, |_| true, |g| std::mem::swap(&mut g.start, &mut g.end));
_ => {
self.fsm_state.process_event(message, &mut self.data, context, &self.options, responses, false);

let has_gradient = has_gradient_on_selected_layers(context.document);
if has_gradient != self.data.has_selected_gradient {
self.data.has_selected_gradient = has_gradient;
responses.add(ToolMessage::RefreshToolOptions);
}
}
}
}
Expand Down Expand Up @@ -515,6 +550,8 @@ struct GradientToolData {
auto_pan_shift: DVec2,
gradient_angle: f64,
has_selected_gradient: bool,
color_picker_editing_color_stop: Option<usize>,
color_picker_transaction_open: bool,
}

impl Fsm for GradientToolFsmState {
Expand Down Expand Up @@ -723,9 +760,31 @@ impl Fsm for GradientToolFsmState {
let snap_data = SnapData::new(document, input, viewport);
tool_data.snap_manager.draw_overlays(snap_data, &mut overlay_context);

// Update color picker position if active (keeps it anchored to the stop during pan/zoom)
if let Some(stop_index) = tool_data.color_picker_editing_color_stop
&& let Some(selected_gradient) = tool_data.selected_gradient.as_ref()
&& let Some(layer) = selected_gradient.layer
{
let transform = gradient_space_transform(layer, document);
let gradient = &selected_gradient.gradient;
if stop_index < gradient.stops.position.len() {
let color = gradient.stops.color[stop_index].to_gamma_srgb();
let position = gradient.stops.position[stop_index];
let DVec2 { x, y } = transform.transform_point2(gradient.start.lerp(gradient.end, position));
responses.add(FrontendMessage::UpdateGradientStopColorPickerPosition { color, x, y });
}
}

self
}
(GradientToolFsmState::Ready { .. }, GradientToolMessage::SelectionChanged) => {
if tool_data.color_picker_editing_color_stop.is_some() {
if tool_data.color_picker_transaction_open {
responses.add(DocumentMessage::EndTransaction);
tool_data.color_picker_transaction_open = false;
}
tool_data.color_picker_editing_color_stop = None;
}
tool_data.selected_gradient = None;
GradientToolFsmState::Ready {
hovering: GradientHoverTarget::None,
Expand All @@ -737,11 +796,45 @@ impl Fsm for GradientToolFsmState {
let drag_start_viewport = document.metadata().document_to_viewport.transform_point2(tool_data.drag_start);
if input.mouse.position.distance(drag_start_viewport) <= DRAG_THRESHOLD
&& let Some(selected_gradient) = &mut tool_data.selected_gradient
&& let GradientDragTarget::Midpoint(index) = selected_gradient.dragging
{
selected_gradient.gradient.stops.midpoint[index] = 0.5;
selected_gradient.render_gradient(responses);
responses.add(PropertiesPanelMessage::Refresh);
match selected_gradient.dragging {
GradientDragTarget::Midpoint(index) => {
selected_gradient.gradient.stops.midpoint[index] = 0.5;
selected_gradient.render_gradient(responses);
responses.add(PropertiesPanelMessage::Refresh);
}
GradientDragTarget::Start | GradientDragTarget::End | GradientDragTarget::Stop(_) => {
// Find the stop index from the drag target
let stop_index = match selected_gradient.dragging {
GradientDragTarget::Stop(i) => Some(i),
GradientDragTarget::Start => selected_gradient.gradient.stops.position.iter().position(|&p| p.abs() < f64::EPSILON * 1000.),
GradientDragTarget::End => selected_gradient.gradient.stops.position.iter().position(|&p| (1. - p).abs() < f64::EPSILON * 1000.),
_ => None,
};
if let Some(stop_index) = stop_index
&& stop_index < selected_gradient.gradient.stops.color.len()
{
// Dismiss any existing color picker first
if tool_data.color_picker_editing_color_stop.is_some() && tool_data.color_picker_transaction_open {
responses.add(DocumentMessage::EndTransaction);
tool_data.color_picker_transaction_open = false;
}

let stop_pos = selected_gradient.gradient.stops.position[stop_index];
let viewport_pos = selected_gradient
.transform
.transform_point2(selected_gradient.gradient.start.lerp(selected_gradient.gradient.end, stop_pos));
let color = selected_gradient.gradient.stops.color[stop_index].to_gamma_srgb();
tool_data.color_picker_editing_color_stop = Some(stop_index);
responses.add(FrontendMessage::UpdateGradientStopColorPickerPosition {
color,
x: viewport_pos.x,
y: viewport_pos.y,
});
}
}
_ => {}
}
}
self
}
Expand Down Expand Up @@ -1178,15 +1271,21 @@ impl Fsm for GradientToolFsmState {
tool_data.selected_gradient = None;
responses.add(OverlaysMessage::Draw);

dismiss_color_stop_color_picker(tool_data, responses);

GradientToolFsmState::Ready {
hovering: GradientHoverTarget::None,
selected: GradientSelectedTarget::None,
}
}
(_, GradientToolMessage::Abort) => {
dismiss_color_stop_color_picker(tool_data, responses);

GradientToolFsmState::Ready {
hovering: GradientHoverTarget::None,
selected: GradientSelectedTarget::None,
}
}
(_, GradientToolMessage::Abort) => GradientToolFsmState::Ready {
hovering: GradientHoverTarget::None,
selected: GradientSelectedTarget::None,
},
_ => self,
}
}
Expand Down Expand Up @@ -1273,6 +1372,16 @@ impl Fsm for GradientToolFsmState {
}
}

fn dismiss_color_stop_color_picker(tool_data: &mut GradientToolData, responses: &mut VecDeque<Message>) {
if tool_data.color_picker_editing_color_stop.is_some() {
if tool_data.color_picker_transaction_open {
responses.add(DocumentMessage::EndTransaction);
tool_data.color_picker_transaction_open = false;
}
tool_data.color_picker_editing_color_stop = None;
}
}

fn detect_hover_target(mouse: DVec2, document: &DocumentMessageHandler) -> GradientHoverTarget {
let stop_tolerance = (MANIPULATOR_GROUP_MARKER_SIZE * 2.).powi(2);
let midpoint_tolerance = GRADIENT_MIDPOINT_DIAMOND_RADIUS.powi(2);
Expand Down Expand Up @@ -1380,7 +1489,7 @@ fn apply_gradient_update(
}

if transaction_started {
responses.add(DocumentMessage::AddTransaction);
responses.add(DocumentMessage::EndTransaction);
}
if let Some(selected_gradient) = &mut data.selected_gradient
&& let Some(layer) = selected_gradient.layer
Expand Down
15 changes: 11 additions & 4 deletions frontend/src/components/floating-menus/ColorPicker.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext, onDestroy, createEventDispatcher } from "svelte";
import { getContext, onDestroy, createEventDispatcher, tick } from "svelte";

import type { HSV, RGB, FillChoice, MenuDirection } from "@graphite/messages";
import { Color, contrastingOutlineFactor, Gradient } from "@graphite/messages";
Expand Down Expand Up @@ -40,7 +40,7 @@
["Magenta", "#ff00ff", "#696969"],
];

const dispatch = createEventDispatcher<{ colorOrGradient: FillChoice; startHistoryTransaction: undefined }>();
const dispatch = createEventDispatcher<{ colorOrGradient: FillChoice; startHistoryTransaction: undefined; commitHistoryTransaction: undefined }>();
const tooltip = getContext<TooltipState>("tooltip");

export let colorOrGradient: FillChoice;
Expand Down Expand Up @@ -109,10 +109,11 @@
return new Color({ h, s, v, a });
}

function watchOpen(open: boolean) {
async function watchOpen(open: boolean) {
if (open) {
setTimeout(() => hexCodeInputWidget?.focus(), 0);
} else {

await tick();
setOldHSVA(hue, saturation, value, alpha, isNone);
}
}
Expand Down Expand Up @@ -198,6 +199,7 @@
}

function onPointerUp() {
if (draggingPickerTrack) dispatch("commitHistoryTransaction");
removeEvents();
}

Expand Down Expand Up @@ -413,6 +415,10 @@
setOldHSVA(hsva.h, hsva.s, hsva.v, hsva.a, color.none);
}

export function div(): HTMLDivElement | undefined {
return self?.div();
}

onDestroy(() => {
removeEvents();
});
Expand Down Expand Up @@ -705,6 +711,7 @@

<style lang="scss" global>
.color-picker {
--widget-height: 24px;
--picker-size: 256px;
--picker-circle-radius: 6px;

Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/layout/FloatingMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@
}

const inParentFloatingMenu = Boolean(floatingMenuContainer.closest("[data-floating-menu-content]"));
if (!inParentFloatingMenu) {
const noPosition = Boolean(floatingMenuContainer.closest("[data-floating-menu-no-position]"));
if (!inParentFloatingMenu && !noPosition) {
// Required to correctly position content when scrolled (it has a `position: fixed` to prevent clipping)
// We use `.style` on a div (instead of a style DOM attribute binding) because the binding causes the `afterUpdate()` hook to call the function we're in recursively forever
let tailOffset = 0;
Expand Down Expand Up @@ -322,7 +323,7 @@

// POINTER STRAY
// Close the floating menu if the pointer has strayed far enough from its bounds (and it's not hovering over its own spawner)
const notHoveringOverOwnSpawner = ownSpawner !== targetSpawner;
const notHoveringOverOwnSpawner = ownSpawner !== targetSpawner || (ownSpawner === undefined && targetSpawner === undefined);
if (strayCloses && notHoveringOverOwnSpawner && isPointerEventOutsideFloatingMenu(e, POINTER_STRAY_DISTANCE)) {
// TODO: Extend this rectangle bounds check to all submenu bounds up the DOM tree since currently submenus disappear
// TODO: with zero stray distance if the cursor is further than the stray distance from only the top-level menu
Expand Down
Loading