1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use super::{select::*, *};
use bevy::{prelude::*, render::color::Color};

#[derive(Debug)]
pub struct PickHighlightParams {
    hover_color: Color,
    selection_color: Color,
}

impl PickHighlightParams {
    pub fn hover_color_mut(&mut self) -> &mut Color {
        &mut self.hover_color
    }
    pub fn selection_color_mut(&mut self) -> &mut Color {
        &mut self.selection_color
    }
    pub fn set_hover_color(&mut self, color: Color) {
        self.hover_color = color;
    }
    pub fn set_selection_color(&mut self, color: Color) {
        self.selection_color = color;
    }
}

impl Default for PickHighlightParams {
    fn default() -> Self {
        PickHighlightParams {
            hover_color: Color::rgb(0.3, 0.5, 0.8),
            selection_color: Color::rgb(0.3, 0.8, 0.5),
        }
    }
}

/// Meshes with `HighlightablePickMesh` will be highlighted when hovered over.
/// If the mesh also has the `SelectablePickMesh` component, it will highlight when selected.
#[derive(Debug)]
pub struct HighlightablePickMesh {
    // Stores the initial color of the mesh material prior to selecting/hovering
    initial_color: Option<Color>,
    // The pick group to use for determining highlight state
    group: Group,
}

impl HighlightablePickMesh {
    pub fn new(group: Group) -> Self {
        HighlightablePickMesh {
            group,
            ..Default::default()
        }
    }
}

impl Default for HighlightablePickMesh {
    fn default() -> Self {
        HighlightablePickMesh {
            initial_color: None,
            group: Group::default(),
        }
    }
}

/// Applies highlight an selection color to HighlightablePickMesh entities. Uses the group specified
/// in the component.
pub fn pick_highlighting(
    // Resources
    mut materials: ResMut<Assets<StandardMaterial>>,
    highlight_params: Res<PickHighlightParams>,
    // Queries
    mut query_selected: Query<(
        &mut HighlightablePickMesh,
        Option<&SelectablePickMesh>, // Optional to work with non-selectable entities
        &Handle<StandardMaterial>,
        &InteractableMesh,
    )>,
) {
    for (mut highlightable, selectable, material_handle, interactable) in
        &mut query_selected.iter_mut()
    {
        let group = highlightable.group;
        let hovered = *interactable.hover(&group).unwrap();
        let current_color = &mut materials.get_mut(material_handle).unwrap().albedo;
        // If the initial color hasn't been set, we should set it now.
        let initial_color = match highlightable.initial_color {
            None => {
                highlightable.initial_color = Some(*current_color);
                *current_color
            }
            Some(color) => color,
        };
        // When the color is no longer highlighted, the new color depends on selection state. If the
        // entity is selected, the color should be selection color, otherwise it should be the
        // entity's initial color.
        let unhighlight_color = match selectable {
            Some(selectable) => {
                if selectable.selected(&group) {
                    highlight_params.selection_color
                } else {
                    initial_color
                }
            }
            None => initial_color,
        };
        // Update the current entity's color based on selection and highlight state
        *current_color = match interactable.hover_event(&group).unwrap() {
            HoverEvents::None => {
                // This is needed when the user clicks elsewhere and the selection state changes.
                // Otherwise, the color would only change after a JustEntered or JustExited.
                // In a more complex example, this might be handled only if
                if hovered {
                    continue;
                } else {
                    unhighlight_color
                }
            }
            HoverEvents::JustEntered => highlight_params.hover_color,
            HoverEvents::JustExited => unhighlight_color,
        };
    }
}