jackdaw_feathers 0.4.1

Internal crate for the jackdaw editor
Documentation
use bevy::picking::events::{DragEnd, DragStart, Press, Release};
use bevy::prelude::*;
use bevy::ui::UiGlobalTransform;

use super::{
    AlphaSlider, ColorPickerChangeEvent, ColorPickerCommitEvent, ColorPickerState, HsvRectangle,
    HueSlider,
};

#[derive(Component, Default)]
pub(super) struct Dragging;

pub(super) trait PickerControl: Component {
    fn picker_entity(&self) -> Entity;
    fn update_state(&self, state: &mut ColorPickerState, normalized: Vec2);
}

impl PickerControl for HsvRectangle {
    fn picker_entity(&self) -> Entity {
        self.0
    }

    fn update_state(&self, state: &mut ColorPickerState, normalized: Vec2) {
        state.saturation = (normalized.x + 0.5).clamp(0.0, 1.0);
        state.brightness = (0.5 - normalized.y).clamp(0.0, 1.0);
    }
}

impl PickerControl for HueSlider {
    fn picker_entity(&self) -> Entity {
        self.0
    }

    fn update_state(&self, state: &mut ColorPickerState, normalized: Vec2) {
        state.hue = (normalized.x + 0.5).clamp(0.0, 1.0) * 360.0;
    }
}

impl PickerControl for AlphaSlider {
    fn picker_entity(&self) -> Entity {
        self.0
    }

    fn update_state(&self, state: &mut ColorPickerState, normalized: Vec2) {
        state.alpha = (normalized.x + 0.5).clamp(0.0, 1.0);
    }
}

pub(super) fn on_control_press<C: PickerControl>(
    event: On<Pointer<Press>>,
    mut commands: Commands,
    controls: Query<(&C, &ComputedNode, &UiGlobalTransform)>,
    mut pickers: Query<&mut ColorPickerState>,
) {
    let Ok((control, computed, ui_transform)) = controls.get(event.event_target()) else {
        return;
    };
    let picker_entity = control.picker_entity();

    let cursor_pos = event.pointer_location.position / computed.inverse_scale_factor;
    let Some(normalized) = computed.normalize_point(*ui_transform, cursor_pos) else {
        return;
    };

    let Ok(mut state) = pickers.get_mut(picker_entity) else {
        return;
    };

    control.update_state(&mut state, normalized);

    commands.trigger(ColorPickerChangeEvent {
        entity: picker_entity,
        color: state.to_rgba(),
    });
}

pub(super) fn on_control_release<C: PickerControl>(
    event: On<Pointer<Release>>,
    mut commands: Commands,
    controls: Query<&C, Without<Dragging>>,
    pickers: Query<&ColorPickerState>,
) {
    let Ok(control) = controls.get(event.event_target()) else {
        return;
    };
    let picker_entity = control.picker_entity();

    if let Ok(state) = pickers.get(picker_entity) {
        commands.trigger(ColorPickerCommitEvent {
            entity: picker_entity,
            color: state.to_rgba(),
        });
    }
}

pub(super) fn on_control_drag_start<C: PickerControl>(
    event: On<Pointer<DragStart>>,
    mut commands: Commands,
    controls: Query<(&C, &ComputedNode, &UiGlobalTransform)>,
    mut pickers: Query<&mut ColorPickerState>,
) {
    let Ok((control, computed, ui_transform)) = controls.get(event.event_target()) else {
        return;
    };
    let picker_entity = control.picker_entity();

    commands.entity(event.event_target()).insert(Dragging);

    let cursor_pos = event.pointer_location.position / computed.inverse_scale_factor;
    let Some(normalized) = computed.normalize_point(*ui_transform, cursor_pos) else {
        return;
    };

    let Ok(mut state) = pickers.get_mut(picker_entity) else {
        return;
    };

    control.update_state(&mut state, normalized);

    commands.trigger(ColorPickerChangeEvent {
        entity: picker_entity,
        color: state.to_rgba(),
    });
}

pub(super) fn on_control_drag<C: PickerControl>(
    event: On<Pointer<Drag>>,
    mut commands: Commands,
    controls: Query<(&C, &ComputedNode, &UiGlobalTransform), With<Dragging>>,
    mut pickers: Query<&mut ColorPickerState>,
) {
    let Ok((control, computed, ui_transform)) = controls.get(event.event_target()) else {
        return;
    };
    let picker_entity = control.picker_entity();

    let cursor_pos = event.pointer_location.position / computed.inverse_scale_factor;
    let Some(normalized) = computed.normalize_point(*ui_transform, cursor_pos) else {
        return;
    };

    let Ok(mut state) = pickers.get_mut(picker_entity) else {
        return;
    };

    control.update_state(&mut state, normalized);

    commands.trigger(ColorPickerChangeEvent {
        entity: picker_entity,
        color: state.to_rgba(),
    });
}

pub(super) fn on_control_drag_end<C: PickerControl>(
    event: On<Pointer<DragEnd>>,
    mut commands: Commands,
    controls: Query<&C>,
    pickers: Query<&ColorPickerState>,
) {
    let Ok(control) = controls.get(event.event_target()) else {
        return;
    };
    let picker_entity = control.picker_entity();

    commands.entity(event.event_target()).remove::<Dragging>();

    if let Ok(state) = pickers.get(picker_entity) {
        commands.trigger(ColorPickerCommitEvent {
            entity: picker_entity,
            color: state.to_rgba(),
        });
    }
}