Rename the SpectrumInput widget to SliderInput (#4541)

Rename the 'SpectrumInput' widget to 'SliderInput'
This commit is contained in:
Keavon Chambers
2026-09-15 20:12:31 -07:00
committed by GitHub
parent 358eb317d2
commit eb4a122322
9 changed files with 127 additions and 127 deletions

View File

@@ -1,4 +1,4 @@
use crate::messages::layout::utility_types::widgets::input_widgets::{SpectrumInputUpdate, VisualColorPickersInputUpdate};
use crate::messages::layout::utility_types::widgets::input_widgets::{SliderInputUpdate, VisualColorPickersInputUpdate};
use crate::messages::prelude::*;
use graphene_std::vector::style::{FillChoice, GradientHueDirection, GradientInterpolation, GradientSpace, GradientSpread};
@@ -45,8 +45,8 @@ pub enum ColorPickerMessage {
/// Swap the current "new" color with the captured "old" color.
SwapNewWithOld,
/// `SpectrumInput` change: marker move/insert/delete, midpoint move/reset, or active marker selection changed.
GradientUpdate { update: SpectrumInputUpdate },
/// `SliderInput` change: marker move/insert/delete, midpoint move/reset, or active marker selection changed.
GradientUpdate { update: SliderInputUpdate },
/// Gradient spread choice from the gradient "Ends" selection.
SetGradientSpread { gradient_spread: GradientSpread },
/// Gradient cyclic choice: whether the stops wrap as a cycle, from the "Cyclic" checkbox.

View File

@@ -1,6 +1,6 @@
use crate::messages::color_picker::color_picker_message::{HsvChannel, RgbChannel};
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::layout::utility_types::widgets::input_widgets::{ColorPresetsInputUpdate, SpectrumInputUpdate, SpectrumMarker, VisualColorPickersInputUpdate};
use crate::messages::layout::utility_types::widgets::input_widgets::{ColorPresetsInputUpdate, SliderInputUpdate, SliderMarker, VisualColorPickersInputUpdate};
use crate::messages::prelude::*;
use graphene_std::Color;
use graphene_std::color::SRGBA8;
@@ -377,10 +377,10 @@ impl ColorPickerMessageHandler {
});
}
/// Apply an incoming `SpectrumInput` intent to the gradient state and broadcast the result.
fn apply_gradient_update(&mut self, update: SpectrumInputUpdate, responses: &mut VecDeque<Message>) {
/// Apply an incoming `SliderInput` intent to the gradient state and broadcast the result.
fn apply_gradient_update(&mut self, update: SliderInputUpdate, responses: &mut VecDeque<Message>) {
// Active marker selection is the one update that doesn't mutate the gradient
if let SpectrumInputUpdate::ActiveMarker {
if let SliderInputUpdate::ActiveMarker {
active_marker_index,
active_marker_is_midpoint,
} = update
@@ -401,19 +401,19 @@ impl ColorPickerMessageHandler {
let Some(mut gradient) = self.gradient.clone() else { return };
match update {
SpectrumInputUpdate::MoveMarker { index, position } => {
SliderInputUpdate::MoveMarker { index, position } => {
let new_index = gradient.move_stop(index as usize, position, self.gradient_cyclic);
if Some(index) == self.active_marker_index {
self.active_marker_index = Some(new_index as u32);
}
}
SpectrumInputUpdate::MoveMidpoint { index, position } => {
SliderInputUpdate::MoveMidpoint { index, position } => {
if (index as usize) >= gradient.len() {
return;
}
gradient.set_midpoint(index as usize, position.clamp(MIN_MIDPOINT, MAX_MIDPOINT));
}
SpectrumInputUpdate::InsertMarker { position } => {
SliderInputUpdate::InsertMarker { position } => {
let new_index = gradient.insert_stop(position, self.gradient_settings());
self.active_marker_index = Some(new_index as u32);
self.active_marker_is_midpoint = false;
@@ -422,7 +422,7 @@ impl ColorPickerMessageHandler {
self.snapshot_old();
}
}
SpectrumInputUpdate::InsertDuplicate { index, position } => {
SliderInputUpdate::InsertDuplicate { index, position } => {
let source = index as usize;
let Some(insert_index) = gradient.duplicate_stop(source, position, self.gradient_cyclic) else {
return;
@@ -432,7 +432,7 @@ impl ColorPickerMessageHandler {
self.active_marker_index = Some(dragged_index as u32);
self.active_marker_is_midpoint = false;
}
SpectrumInputUpdate::RemoveDuplicate { index } => {
SliderInputUpdate::RemoveDuplicate { index } => {
let anchor = index as usize;
if anchor >= gradient.len() || gradient.len() <= 2 {
return;
@@ -449,7 +449,7 @@ impl ColorPickerMessageHandler {
self.active_marker_index = Some(active - 1);
}
}
SpectrumInputUpdate::DeleteMarker { index } => {
SliderInputUpdate::DeleteMarker { index } => {
// Enforce minimum stop count. The gradient editor needs at least 2 stops to remain meaningful.
if gradient.len() <= 2 || (index as usize) >= gradient.len() {
return;
@@ -463,10 +463,10 @@ impl ColorPickerMessageHandler {
self.snapshot_old();
}
}
SpectrumInputUpdate::ResetMidpoint { index } => {
SliderInputUpdate::ResetMidpoint { index } => {
gradient.reset_midpoint(index as usize);
}
SpectrumInputUpdate::ResetMarker { index } => {
SliderInputUpdate::ResetMarker { index } => {
let i = index as usize;
let count = gradient.len();
if i >= count {
@@ -483,7 +483,7 @@ impl ColorPickerMessageHandler {
self.active_marker_index = Some(new_index as u32);
}
}
SpectrumInputUpdate::ActiveMarker { .. } => unreachable!("handled above"),
SliderInputUpdate::ActiveMarker { .. } => unreachable!("handled above"),
}
responses.add(FrontendMessage::ColorPickerColorChanged {
@@ -514,10 +514,10 @@ impl ColorPickerMessageHandler {
if let Some(gradient) = &self.gradient {
// For gradient editing, the markers' handle colors mirror their gradient stop colors
let markers = (0..gradient.len())
.filter_map(|i| Some(SpectrumMarker::new(gradient.position(i, self.gradient_cyclic), gradient.midpoint(i), gradient.color(i)?)))
.filter_map(|i| Some(SliderMarker::new(gradient.position(i, self.gradient_cyclic), gradient.midpoint(i), gradient.color(i)?)))
.collect();
let mut row_widgets = vec![
SpectrumInput::new(GradientStops::from(gradient))
SliderInput::new(GradientStops::from(gradient))
.track_space(self.gradient_space)
.track_cyclic(self.gradient_cyclic)
.track_hue_direction(self.gradient_hue_direction)
@@ -531,7 +531,7 @@ impl ColorPickerMessageHandler {
.allow_reorder(true)
.allow_select(true)
.disabled(self.disabled)
.on_update(|update: &SpectrumInputUpdate| ColorPickerMessage::GradientUpdate { update: update.clone() }.into())
.on_update(|update: &SliderInputUpdate| ColorPickerMessage::GradientUpdate { update: update.clone() }.into())
.widget_instance(),
];
@@ -556,12 +556,12 @@ impl ColorPickerMessageHandler {
return Message::NoOp;
};
let update = if is_midpoint {
SpectrumInputUpdate::MoveMidpoint {
SliderInputUpdate::MoveMidpoint {
index: captured_index,
position: new_value / 100.,
}
} else {
SpectrumInputUpdate::MoveMarker {
SliderInputUpdate::MoveMarker {
index: captured_index,
position: new_value / 100.,
}

View File

@@ -255,20 +255,20 @@ impl LayoutMessageHandler {
responses.add(callback_message);
}
Widget::SpectrumInput(spectrum_input) => {
Widget::SliderInput(slider_input) => {
let callback_message = match action {
WidgetValueAction::Commit => (spectrum_input.on_commit.callback)(&()),
WidgetValueAction::Commit => (slider_input.on_commit.callback)(&()),
WidgetValueAction::Update => {
let Ok(update) = serde_json::from_value::<SpectrumInputUpdate>(value) else {
warn!("SpectrumInput update was not able to be parsed as SpectrumInputUpdate");
let Ok(update) = serde_json::from_value::<SliderInputUpdate>(value) else {
warn!("SliderInput update was not able to be parsed as SliderInputUpdate");
return;
};
// Don't mutate the stored widget here: leaving its old values lets the layout diff detect a change
// when the new layout is rebuilt with the updated state. Otherwise the frontend's stored layout
// keeps stale values for `activeMarkerIndex`, etc., and any other widget's diff (e.g. the position
// NumberInput) will trigger Svelte to re-spread those stale props onto SpectrumInput, clobbering
// NumberInput) will trigger Svelte to re-spread those stale props onto SliderInput, clobbering
// its local `activeMarkerIndex` and making subsequent drags target the wrong stop.
(spectrum_input.on_update.callback)(&update)
(slider_input.on_update.callback)(&update)
}
};
@@ -565,20 +565,20 @@ fn populate_computed_display_fields(layout: &mut Layout) {
})
.collect();
}
Widget::SpectrumInput(spectrum_input) => {
Widget::SliderInput(slider_input) => {
// The track strip spans exactly 0 to 1, which no spread affects, so the widget carries no spread of its own
let settings = graphene_std::vector::style::GradientSettings {
spread: Default::default(),
cyclic: spectrum_input.track_cyclic,
space: spectrum_input.track_space,
hue_direction: spectrum_input.track_hue_direction,
interpolation: spectrum_input.track_interpolation,
cyclic: slider_input.track_cyclic,
space: slider_input.track_space,
hue_direction: slider_input.track_hue_direction,
interpolation: slider_input.track_interpolation,
};
let track_gradient = graphene_std::vector::style::Gradient::from(&spectrum_input.track);
spectrum_input.track_samples = track_gradient
let track_gradient = graphene_std::vector::style::Gradient::from(&slider_input.track);
slider_input.track_samples = track_gradient
.interpolated_samples_or_black(settings)
.into_iter()
.map(|(position, color, _)| SpectrumSample::new(position, color))
.map(|(position, color, _)| SliderSample::new(position, color))
.collect();
// The end caps sample the track's boundary colors, which a cyclic wrap makes the wrapped interval's boundary-crossing color rather than the outermost stops'
let track_evaluator = track_gradient.evaluator(settings);
@@ -586,8 +586,8 @@ fn populate_computed_display_fields(layout: &mut Layout) {
let color = track_evaluator.evaluate(t);
SRGBA8::from(color).to_css_hex()
};
spectrum_input.track_start_css = cap(0.);
spectrum_input.track_end_css = cap(1.);
slider_input.track_start_css = cap(0.);
slider_input.track_end_css = cap(1.);
}
Widget::ColorComparisonInput(comparison) => {
let contrasting = |color: Option<SRGBA8>| color.map_or(SRGBA8::BLACK, |color| color.contrasting_text_color()).to_css_hex();

View File

@@ -470,7 +470,7 @@ impl LayoutGroup {
| Widget::ParameterExposeButton(_)
| Widget::ColorComparisonInput(_)
| Widget::ColorPresetsInput(_)
| Widget::SpectrumInput(_)
| Widget::SliderInput(_)
| Widget::TransferCurveInput(_)
| Widget::VisualColorPickersInput(_) => continue,
};
@@ -823,7 +823,7 @@ pub enum Widget {
PopoverButton(PopoverButton),
RadioInput(RadioInput),
Separator(Separator),
SpectrumInput(SpectrumInput),
SliderInput(SliderInput),
TextAreaInput(TextAreaInput),
TextButton(TextButton),
TextInput(TextInput),
@@ -888,7 +888,7 @@ impl DiffUpdate {
| Widget::WorkingColorsInput(_)
| Widget::ColorComparisonInput(_)
| Widget::ColorPresetsInput(_)
| Widget::SpectrumInput(_)
| Widget::SliderInput(_)
| Widget::TransferCurveInput(_)
| Widget::VisualColorPickersInput(_) => None,
};

View File

@@ -624,7 +624,7 @@ pub enum TransferCurveInputUpdate {
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder)]
#[derivative(Debug, PartialEq, Default)]
pub struct SpectrumInput {
pub struct SliderInput {
// Content
/// The colored gradient drawn behind the markers (display-only, caller-owned).
#[widget_builder(constructor)]
@@ -644,7 +644,7 @@ pub struct SpectrumInput {
/// Straight-alpha samples the frontend draws as the stops of an SVG gradient filling the track strip. Auto-populated from `track` at layout-send time.
#[serde(rename = "trackSamples")]
#[widget_builder(skip)]
pub track_samples: Vec<SpectrumSample>,
pub track_samples: Vec<SliderSample>,
/// Hex string for the track strip's leftmost solid-color end-cap. Auto-populated by evaluating `track` at position 0.
#[serde(rename = "trackStartCSS")]
#[widget_builder(skip)]
@@ -654,7 +654,7 @@ pub struct SpectrumInput {
#[widget_builder(skip)]
pub track_end_css: String,
/// The handles the user can drag along the track. Their handle colors are caller-owned (e.g., for a gradient editor they follow the stop colors, for a "Shadows/Midpoints/Highlights" widget they're hardcoded).
pub markers: Vec<SpectrumMarker>,
pub markers: Vec<SliderMarker>,
#[serde(rename = "activeMarkerIndex")]
pub active_marker_index: Option<u32>,
#[serde(rename = "activeMarkerIsMidpoint")]
@@ -689,7 +689,7 @@ pub struct SpectrumInput {
// Callbacks
#[serde(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
pub on_update: WidgetCallback<SpectrumInputUpdate>,
pub on_update: WidgetCallback<SliderInputUpdate>,
#[serde(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
pub on_commit: WidgetCallback<()>,
@@ -697,12 +697,12 @@ pub struct SpectrumInput {
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct SpectrumMarker {
pub struct SliderMarker {
/// Position along the track, normally 0..1. A shifted or stretched non-cyclic ramp can push it outside, where it is not drawn.
position: f64,
/// Midpoint (0..1) of the interval to the next marker, used only with `show_midpoints`. The last marker's midpoint spans the wrap of a cyclic track, or is otherwise ignored.
midpoint: f64,
/// CSS color string for the marker handle's fill. Set via `SpectrumMarker::new` from a linear [`Color`],
/// CSS color string for the marker handle's fill. Set via `SliderMarker::new` from a linear [`Color`],
/// discarding any transparency so the handle always shows the RGB that steers the interpolation.
#[serde(rename = "handleColorCSS")]
handle_color_css: String,
@@ -717,7 +717,7 @@ pub struct SpectrumMarker {
between_neighbors: bool,
}
impl SpectrumMarker {
impl SliderMarker {
pub fn new(position: f64, midpoint: f64, handle_color: Color) -> Self {
let handle_color_css = format!("#{}", SRGBA8::from(handle_color).to_rgb_hex());
Self {
@@ -748,8 +748,8 @@ impl SpectrumMarker {
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct SpectrumSample {
/// Position (0..1) of the sample along the spectrum track, drawn as the SVG stop's `offset`.
pub struct SliderSample {
/// Position (0..1) of the sample along the slider track, drawn as the SVG stop's `offset`.
position: f64,
/// `#rrggbb` hex of the sample's color, drawn as the SVG stop's `stop-color`.
color: String,
@@ -757,7 +757,7 @@ pub struct SpectrumSample {
alpha: f32,
}
impl SpectrumSample {
impl SliderSample {
pub fn new(position: f64, color: Color) -> Self {
Self {
position,
@@ -769,7 +769,7 @@ impl SpectrumSample {
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum SpectrumInputUpdate {
pub enum SliderInputUpdate {
MoveMarker {
index: u32,
position: f64,

View File

@@ -1346,12 +1346,12 @@ pub fn query_assign_colors_randomize(node_id: NodeId, context: &NodePropertiesCo
})
}
/// 2-stop black-to-white gradient track for spectrum sliders that map a value to a grayscale axis.
/// 2-stop black-to-white gradient track for sliders that map a value to a grayscale axis.
fn bw_track() -> Gradient {
Gradient::from(vec![Color::BLACK, Color::WHITE])
}
/// 3-stop black-to-color-to-white gradient track for spectrum sliders that map a value to a hue's full luminance range.
/// 3-stop black-to-color-to-white gradient track for sliders that map a value to a hue's full luminance range.
fn color_track(color: Color) -> Gradient {
Gradient::from(vec![Color::BLACK, color, Color::WHITE])
}
@@ -1369,7 +1369,7 @@ pub(crate) fn brightness_contrast_properties(node_id: NodeId, context: &mut Node
let brightness_min = if use_classic_value { -100. } else { -150. };
let brightness_max = if use_classic_value { 100. } else { 150. };
let brightness = spectrum_slider_row(
let brightness = gradient_slider_row(
node_id,
context,
BrightnessInput,
@@ -1385,7 +1385,7 @@ pub(crate) fn brightness_contrast_properties(node_id: NodeId, context: &mut Node
let zero_position = -contrast_min / (100. - contrast_min);
let mut contrast_track = Gradient::from(vec![Color::MIDDLE_GRAY, Color::BLACK, Color::MIDDLE_GRAY]);
contrast_track.set_positions(&[0., zero_position, 1.]);
let contrast = spectrum_slider_row(
let contrast = gradient_slider_row(
node_id,
context,
ContrastInput,
@@ -1402,7 +1402,7 @@ pub(crate) fn brightness_contrast_properties(node_id: NodeId, context: &mut Node
let mut layout = vec![brightness, contrast, LayoutGroup::row(use_classic)];
if use_classic_value {
let number_input = NumberInput::default().mode_increment().min(0.).max(255.);
layout.push(spectrum_slider_row(node_id, context, ClassicPivotInput, bw_track(), Color::WHITE, 0., 255., 127., number_input));
layout.push(gradient_slider_row(node_id, context, ClassicPivotInput, bw_track(), Color::WHITE, 0., 255., 127., number_input));
}
layout
@@ -1481,22 +1481,22 @@ pub(crate) fn levels_properties(node_id: NodeId, context: &mut NodePropertiesCon
};
let input_range_params = [
SpectrumSectionParam::new(shadows, Color::BLACK, 0., MarkerScale::Percent),
SpectrumSectionParam::new(midtones, Color::MIDDLE_GRAY, 1., MarkerScale::Gamma).between_neighbors(),
SpectrumSectionParam::new(highlights, Color::WHITE, 100., MarkerScale::Percent),
SliderSectionParam::new(shadows, Color::BLACK, 0., MarkerScale::Percent),
SliderSectionParam::new(midtones, Color::MIDDLE_GRAY, 1., MarkerScale::Gamma).between_neighbors(),
SliderSectionParam::new(highlights, Color::WHITE, 100., MarkerScale::Percent),
];
let output_range_params = [
SpectrumSectionParam::new(output_minimums, Color::BLACK, 0., MarkerScale::Percent),
SpectrumSectionParam::new(output_maximums, Color::WHITE, 100., MarkerScale::Percent),
SliderSectionParam::new(output_minimums, Color::BLACK, 0., MarkerScale::Percent),
SliderSectionParam::new(output_maximums, Color::WHITE, 100., MarkerScale::Percent),
];
let mut layout = vec![channel];
build_shared_spectrum_section(node_id, context, &bw_track(), &input_range_params, &mut layout);
build_shared_spectrum_section(node_id, context, &bw_track(), &output_range_params, &mut layout);
build_shared_slider_section(node_id, context, &bw_track(), &input_range_params, &mut layout);
build_shared_slider_section(node_id, context, &bw_track(), &output_range_params, &mut layout);
layout
}
/// How a shared spectrum marker's value maps onto its track.
/// How a shared slider marker's value maps onto its track.
#[derive(Clone, Copy)]
enum MarkerScale {
/// A 0..100 percentage, placed linearly.
@@ -1545,8 +1545,8 @@ impl MarkerScale {
}
}
/// One parameter of a shared spectrum section and how its marker sits on the track.
struct SpectrumSectionParam {
/// One parameter of a shared slider section and how its marker sits on the track.
struct SliderSectionParam {
parameter: ParameterRef,
handle_color: Color,
/// The value a double-click resets to.
@@ -1560,7 +1560,7 @@ struct SpectrumSectionParam {
between_neighbors: bool,
}
impl SpectrumSectionParam {
impl SliderSectionParam {
fn new(parameter: impl Into<ParameterRef>, handle_color: Color, default_value: f64, scale: MarkerScale) -> Self {
Self {
parameter: parameter.into(),
@@ -1589,10 +1589,10 @@ impl SpectrumSectionParam {
}
}
/// Append a section of related parameters as rows: a shared spectrum over `track` (with one marker per non-exposed parameter) sits on the first non-exposed row
/// Append a section of related parameters as rows: a shared slider over `track` (with one marker per non-exposed parameter) sits on the first non-exposed row
/// alongside its 60px number input, and the remaining non-exposed rows show only their 60px number input. Exposed parameters render as the standard exposed-row display.
/// Marker positions are clamped to non-decreasing display order so they never visually cross even if the underlying values do.
fn build_shared_spectrum_section(node_id: NodeId, context: &mut NodePropertiesContext, track: &Gradient, params: &[SpectrumSectionParam], layout: &mut Vec<LayoutGroup>) {
fn build_shared_slider_section(node_id: NodeId, context: &mut NodePropertiesContext, track: &Gradient, params: &[SliderSectionParam], layout: &mut Vec<LayoutGroup>) {
// Snapshot exposure and values before the mutable-borrow loop
let exposure_and_value: Vec<(bool, f64)> = match get_document_node(node_id, context) {
Ok(document_node) => params
@@ -1608,7 +1608,7 @@ fn build_shared_spectrum_section(node_id: NodeId, context: &mut NodePropertiesCo
})
.collect(),
Err(err) => {
log::error!("Could not get document node in build_shared_spectrum_section: {err}");
log::error!("Could not get document node in build_shared_slider_section: {err}");
return;
}
};
@@ -1655,12 +1655,12 @@ fn build_shared_spectrum_section(node_id: NodeId, context: &mut NodePropertiesCo
}
}
let spectrum_markers: Vec<SpectrumMarker> = marker_positions
let slider_markers: Vec<SliderMarker> = marker_positions
.iter()
.zip(&marker_colors_and_links)
.zip(&marker_between)
.map(|((&position, &(handle_color, paired, dashed)), &between)| {
let mut marker = SpectrumMarker::new(position, 0.5, handle_color);
let mut marker = SliderMarker::new(position, 0.5, handle_color);
if paired {
marker = marker.pair_with_next();
}
@@ -1674,11 +1674,11 @@ fn build_shared_spectrum_section(node_id: NodeId, context: &mut NodePropertiesCo
})
.collect();
// Build the shared spectrum widget (placed on the first non-exposed row)
let spectrum_widget = (!spectrum_markers.is_empty()).then(|| {
SpectrumInput::new(GradientStops::from(track))
// Build the shared slider widget (placed on the first non-exposed row)
let slider_widget = (!slider_markers.is_empty()).then(|| {
SliderInput::new(GradientStops::from(track))
.track_space(GradientSpace::RgbGamma)
.markers(spectrum_markers)
.markers(slider_markers)
.show_midpoints(false)
.allow_insert(false)
.allow_delete(false)
@@ -1691,9 +1691,9 @@ fn build_shared_spectrum_section(node_id: NodeId, context: &mut NodePropertiesCo
let marker_scales = marker_scales.clone();
let marker_positions = marker_positions.clone();
let marker_between = marker_between.clone();
move |update: &SpectrumInputUpdate| {
move |update: &SliderInputUpdate| {
let i = match update {
SpectrumInputUpdate::MoveMarker { index, .. } | SpectrumInputUpdate::ResetMarker { index } => *index as usize,
SliderInputUpdate::MoveMarker { index, .. } | SliderInputUpdate::ResetMarker { index } => *index as usize,
_ => return Message::NoOp,
};
let (Some(&input_index), Some(&scale), Some(&between), Some(&default_position)) =
@@ -1709,17 +1709,17 @@ fn build_shared_spectrum_section(node_id: NodeId, context: &mut NodePropertiesCo
let right = (i + 1..marker_positions.len()).find(|&j| bounding(j)).map_or(1., |j| marker_positions[j]);
let scale_position = match update {
SpectrumInputUpdate::MoveMarker { position, .. } if between => {
SliderInputUpdate::MoveMarker { position, .. } if between => {
let span = right - left;
if span <= f64::EPSILON {
return Message::NoOp;
}
((position - left) / span).clamp(0., 1.)
}
SpectrumInputUpdate::MoveMarker { position, .. } => *position,
SliderInputUpdate::MoveMarker { position, .. } => *position,
// A default that would cross a neighbor falls back to the midpoint between them
SpectrumInputUpdate::ResetMarker { .. } if between || cyclic || (left..=right).contains(&default_position) => default_position,
SpectrumInputUpdate::ResetMarker { .. } => (left + right) / 2.,
SliderInputUpdate::ResetMarker { .. } if between || cyclic || (left..=right).contains(&default_position) => default_position,
SliderInputUpdate::ResetMarker { .. } => (left + right) / 2.,
_ => return Message::NoOp,
};
NodeGraphMessage::SetInputValue {
@@ -1733,9 +1733,9 @@ fn build_shared_spectrum_section(node_id: NodeId, context: &mut NodePropertiesCo
.on_commit(commit_value)
.widget_instance()
});
let spectrum_owner = marker_input_indices.first().copied();
let slider_owner = marker_input_indices.first().copied();
// One row per parameter: first non-exposed carries the shared spectrum, others get just a number input
// One row per parameter: first non-exposed carries the shared slider, others get just a number input
for (i, param) in params.iter().enumerate() {
let (exposed, current) = exposure_and_value[i];
let input_index = param.parameter.input_index;
@@ -1748,10 +1748,10 @@ fn build_shared_spectrum_section(node_id: NodeId, context: &mut NodePropertiesCo
let mut row = start_widgets(&ParameterWidgetsInfo::at_index(node_id, input_index, true, context));
row.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
if Some(input_index) == spectrum_owner
&& let Some(spectrum) = &spectrum_widget
if Some(input_index) == slider_owner
&& let Some(slider) = &slider_widget
{
row.push(spectrum.clone());
row.push(slider.clone());
row.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
}
@@ -1898,7 +1898,7 @@ pub(crate) fn hue_saturation_properties(node_id: NodeId, context: &mut NodePrope
let mut layout = vec![enum_choice::<HueSaturationRange>().for_socket(range_info).disabled(colorize_value).property_row()];
layout.extend([
spectrum_slider_row(
gradient_slider_row(
node_id,
context,
hue,
@@ -1909,7 +1909,7 @@ pub(crate) fn hue_saturation_properties(node_id: NodeId, context: &mut NodePrope
hue_default,
NumberInput::default().mode_increment().unit("°").min(hue_min).max(hue_max),
),
spectrum_slider_row(
gradient_slider_row(
node_id,
context,
saturation,
@@ -1920,7 +1920,7 @@ pub(crate) fn hue_saturation_properties(node_id: NodeId, context: &mut NodePrope
saturation_default,
NumberInput::default().mode_increment().unit("%").min(saturation_min).max(100.),
),
spectrum_slider_row(
gradient_slider_row(
node_id,
context,
lightness,
@@ -1937,12 +1937,12 @@ pub(crate) fn hue_saturation_properties(node_id: NodeId, context: &mut NodePrope
if !colorize_value && let (Some(values), Some(defaults)) = (range_values, range_defaults) {
let [falloff_start, range_start, range_end, falloff_end] = values;
let params = [
SpectrumSectionParam::new(falloff_start, Color::WHITE, defaults[0], MarkerScale::Degrees).pair_with_next(),
SpectrumSectionParam::new(range_start, Color::WHITE, defaults[1], MarkerScale::Degrees).dash_to_next(),
SpectrumSectionParam::new(range_end, Color::WHITE, defaults[2], MarkerScale::Degrees).pair_with_next(),
SpectrumSectionParam::new(falloff_end, Color::WHITE, defaults[3], MarkerScale::Degrees),
SliderSectionParam::new(falloff_start, Color::WHITE, defaults[0], MarkerScale::Degrees).pair_with_next(),
SliderSectionParam::new(range_start, Color::WHITE, defaults[1], MarkerScale::Degrees).dash_to_next(),
SliderSectionParam::new(range_end, Color::WHITE, defaults[2], MarkerScale::Degrees).pair_with_next(),
SliderSectionParam::new(falloff_end, Color::WHITE, defaults[3], MarkerScale::Degrees),
];
build_shared_spectrum_section(node_id, context, &hue_track, &params, &mut layout);
build_shared_slider_section(node_id, context, &hue_track, &params, &mut layout);
}
let colorize = bool_widget(ParameterWidgetsInfo::new(node_id, ColorizeInput, true, context), CheckboxInput::default());
@@ -1951,7 +1951,7 @@ pub(crate) fn hue_saturation_properties(node_id: NodeId, context: &mut NodePrope
layout
}
/// A single-marker `SpectrumInput` over `track` driving the number at `input_index`: the marker sits at `position`, double-click
/// A single-marker `SliderInput` over `track` driving the number at `input_index`: the marker sits at `position`, double-click
/// returns it to `default_position`, and each move sets the input to `value_at` the new position.
fn value_slider(
node_id: NodeId,
@@ -1961,18 +1961,18 @@ fn value_slider(
position: f64,
default_position: Option<f64>,
value_at: impl Fn(f64) -> TaggedValue + 'static + Send + Sync,
) -> SpectrumInput {
SpectrumInput::new(track)
) -> SliderInput {
SliderInput::new(track)
.track_space(GradientSpace::RgbGamma)
.markers(vec![SpectrumMarker::new(position, 0.5, handle_color)])
.markers(vec![SliderMarker::new(position, 0.5, handle_color)])
.show_midpoints(false)
.allow_insert(false)
.allow_delete(false)
.allow_reorder(false)
.on_update(move |update: &SpectrumInputUpdate| {
.on_update(move |update: &SliderInputUpdate| {
let new_position = match update {
SpectrumInputUpdate::MoveMarker { index: 0, position } => Some(*position),
SpectrumInputUpdate::ResetMarker { index: 0 } => default_position,
SliderInputUpdate::MoveMarker { index: 0, position } => Some(*position),
SliderInputUpdate::ResetMarker { index: 0 } => default_position,
_ => None,
};
let Some(new_position) = new_position else { return Message::NoOp };
@@ -2048,8 +2048,8 @@ pub(crate) fn range_slider_widget(parameter_widgets_info: ParameterWidgetsInfo,
)
}
/// Build a row with a single-marker `SpectrumInput` and a 60px `NumberInput`. The marker maps `value_min..value_max` to position 0..1, and double-click resets to `default_value`.
fn spectrum_slider_row(
/// Build a row with a single-marker `SliderInput` over `track` and a 60px `NumberInput`. The marker maps `value_min..value_max` to position 0..1, and double-click resets to `default_value`.
fn gradient_slider_row(
node_id: NodeId,
context: &mut NodePropertiesContext,
parameter: impl Into<ParameterRef>,
@@ -2069,7 +2069,7 @@ fn spectrum_slider_row(
.and_then(|input| input.as_non_exposed_value())
.and_then(|tagged| if let TaggedValue::F32(value) = tagged { Some(*value as f64) } else { None });
// Only add the spectrum and number widgets when the input is not exposed
// Only add the slider and number widgets when the input is not exposed
if let Some(current) = current {
let slider = SliderRange {
min: value_min,
@@ -2116,12 +2116,12 @@ pub(crate) fn threshold_properties(node_id: NodeId, context: &mut NodeProperties
use graphene_std::raster::threshold::*;
let params = [
SpectrumSectionParam::new(MinLuminanceInput, Color::WHITE, 50., MarkerScale::Percent).dash_to_next(),
SpectrumSectionParam::new(MaxLuminanceInput, Color::WHITE, 100., MarkerScale::Percent),
SliderSectionParam::new(MinLuminanceInput, Color::WHITE, 50., MarkerScale::Percent).dash_to_next(),
SliderSectionParam::new(MaxLuminanceInput, Color::WHITE, 100., MarkerScale::Percent),
];
let mut layout = Vec::with_capacity(2);
build_shared_spectrum_section(node_id, context, &bw_track(), &params, &mut layout);
build_shared_slider_section(node_id, context, &bw_track(), &params, &mut layout);
layout
}
@@ -2179,7 +2179,7 @@ pub(crate) fn color_balance_properties(node_id: NodeId, context: &mut NodeProper
let mut layout = vec![tone];
for (parameter, track) in parameters.into_iter().zip(tracks) {
layout.push(spectrum_slider_row(node_id, context, parameter, track, Color::WHITE, -100., 100., 0., number_input.clone()));
layout.push(gradient_slider_row(node_id, context, parameter, track, Color::WHITE, -100., 100., 0., number_input.clone()));
}
layout.push(LayoutGroup::row(preserve_luminosity));
@@ -2204,7 +2204,7 @@ pub(crate) fn black_and_white_properties(node_id: NodeId, context: &mut NodeProp
(MagentasInput.into(), Color::MAGENTA, 80.),
];
for (parameter, color, default) in params {
layout.push(spectrum_slider_row(
layout.push(gradient_slider_row(
node_id,
context,
parameter.clone(),
@@ -2268,7 +2268,7 @@ pub(crate) fn channel_mixer_properties(node_id: NodeId, context: &mut NodeProper
layout.push(output_channel);
}
for (i, (parameter, &default)) in parameters.into_iter().zip(defaults.iter()).enumerate() {
layout.push(spectrum_slider_row(
layout.push(gradient_slider_row(
node_id,
context,
parameter,
@@ -2329,7 +2329,7 @@ pub(crate) fn selective_color_properties(node_id: NodeId, context: &mut NodeProp
let mut layout = vec![colors];
for (i, parameter) in parameters.into_iter().enumerate() {
layout.push(spectrum_slider_row(node_id, context, parameter, tracks[i].clone(), Color::WHITE, -100., 100., 0., number_input.clone()));
layout.push(gradient_slider_row(node_id, context, parameter, tracks[i].clone(), Color::WHITE, -100., 100., 0., number_input.clone()));
}
layout.push(mode);

View File

@@ -83,7 +83,7 @@
.pickers-and-gradient .widget-span {
--row-height: 24px;
&:has(.spectrum-input) {
&:has(.slider-input) {
margin-top: 16px;
.number-input {

View File

@@ -15,7 +15,7 @@
import NumberInput from "/src/components/widgets/inputs/NumberInput.svelte";
import RadioInput from "/src/components/widgets/inputs/RadioInput.svelte";
import ReferencePointInput from "/src/components/widgets/inputs/ReferencePointInput.svelte";
import SpectrumInput from "/src/components/widgets/inputs/SpectrumInput.svelte";
import SliderInput from "/src/components/widgets/inputs/SliderInput.svelte";
import TextAreaInput from "/src/components/widgets/inputs/TextAreaInput.svelte";
import TextInput from "/src/components/widgets/inputs/TextInput.svelte";
import TransferCurveInput from "/src/components/widgets/inputs/TransferCurveInput.svelte";
@@ -243,8 +243,8 @@
},
}),
},
SpectrumInput: {
component: SpectrumInput,
SliderInput: {
component: SliderInput,
getProps: (props, index) => ({
...props,
$$events: {

View File

@@ -3,22 +3,22 @@
import { preventEscapeClosingParentFloatingMenu } from "/src/components/layout/FloatingMenu.svelte";
import LayoutCol from "/src/components/layout/LayoutCol.svelte";
import LayoutRow from "/src/components/layout/LayoutRow.svelte";
import type { GradientInterpolation, SpectrumInputUpdate, SpectrumMarker, SpectrumSample } from "/wrapper/pkg/graphite_wasm_wrapper";
import type { GradientInterpolation, SliderInputUpdate, SliderMarker, SliderSample } from "/wrapper/pkg/graphite_wasm_wrapper";
const BUTTON_LEFT = 0;
const BUTTON_RIGHT = 2;
const dispatch = createEventDispatcher<{ update: SpectrumInputUpdate; dragging: boolean }>();
const dispatch = createEventDispatcher<{ update: SliderInputUpdate; dragging: boolean }>();
// Document-unique `id` for this instance's SVG gradient, referenced by its `url(#...)`
const gradientId = `spectrum-input-gradient-${String(Math.random()).substring(2)}`;
const gradientId = `slider-input-gradient-${String(Math.random()).substring(2)}`;
export let trackSamples: SpectrumSample[];
export let trackSamples: SliderSample[];
export let trackStartCSS: string;
export let trackEndCSS: string;
export let trackCyclic = false;
export let trackInterpolation: GradientInterpolation = "Linear";
export let markers: SpectrumMarker[];
export let markers: SliderMarker[];
export let activeMarkerIndex: number | undefined = 0;
export let activeMarkerIsMidpoint = false;
export let showMidpoints = true;
@@ -92,7 +92,7 @@
return WHOLE_PATHS;
}
function emit(intent: SpectrumInputUpdate) {
function emit(intent: SliderInputUpdate) {
dispatch("update", intent);
}
@@ -151,7 +151,7 @@
}
// A marker paired with its successor draws as one marker split down the middle while the two coincide (the successor drawing nothing) and as a half once apart
function markerShape(markers: SpectrumMarker[], index: number): MarkerShape {
function markerShape(markers: SliderMarker[], index: number): MarkerShape {
const marker = markers[index];
const previous = markers[index - 1];
const next = markers[index + 1];
@@ -161,7 +161,7 @@
}
// The spans from each marker passing `linked` to its successor, which on a wrapping track may cross the track's ends in two pieces
function markerSpans(markers: SpectrumMarker[], allowWrap: boolean, linked: (marker: SpectrumMarker) => boolean): { index: number; left: number; width: number }[] {
function markerSpans(markers: SliderMarker[], allowWrap: boolean, linked: (marker: SliderMarker) => boolean): { index: number; left: number; width: number }[] {
const spans: { index: number; left: number; width: number }[] = [];
markers.forEach((marker, index) => {
@@ -616,7 +616,7 @@
// Map midpoint pairs to absolute track positions for rendering the diamond markers.
// A rendered diamond's index is the index of the interval's left marker, which for the cyclic wrapped interval's diamond is the last marker.
function diamondPositions(markers: SpectrumMarker[], showMidpoints: boolean, trackCyclic: boolean, trackInterpolation: GradientInterpolation): number[] {
function diamondPositions(markers: SliderMarker[], showMidpoints: boolean, trackCyclic: boolean, trackInterpolation: GradientInterpolation): number[] {
// A stepped ramp jumps at its stops, so no midpoint has anything to bias
if (!showMidpoints || trackInterpolation === "Stepped" || markers.length < 2) return [];
const positions = markers.slice(0, -1).map((marker, i) => marker.position + marker.midpoint * (markers[i + 1].position - marker.position));
@@ -645,7 +645,7 @@
</script>
<LayoutCol
class="spectrum-input"
class="slider-input"
classes={{ narrow, disabled, "range-slider": rangeSlider }}
styles={{
"--gradient-start": trackStartCSS,
@@ -743,7 +743,7 @@
</LayoutCol>
<style lang="scss">
.spectrum-input {
.slider-input {
position: relative;
--marker-half-width: 6px;