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
120
121
122
123
124
125
126
127
use bevy::prelude::*;

use super::{Group, Interactable, InteractionState};

pub struct Dragged {
  pub group:        Group,
  pub translation:  Vec2,
  pub origin:       Vec2,
  pub just_dropped: bool,
  pub just_dragged: bool,
}

impl Dragged {
  pub fn just_dropped(&self) -> bool {
    self.just_dropped
  }
  pub fn just_dragged(&self) -> bool {
    self.just_dragged
  }
}

pub struct DragPlugin;
impl Plugin for DragPlugin {
  fn build(&self, app: &mut AppBuilder) {
    app
      .init_resource::<InteractionState>()
      .add_system(mouse_press_start_drag_system.system())
      .add_system(mouse_release_stop_drag_system.system())
      .add_system(drag_system.system());
  }
}

pub fn drag_system(
  interaction_state: Res<InteractionState>,
  mut draggables: Query<(&mut Transform, &mut Dragged, &GlobalTransform)>,
) {
  for (mut transform, mut dragged, global_transform) in draggables.iter_mut() {
    if dragged.just_dragged {
      dragged.just_dragged = false;
    }
    if let Some(cursor_position) = interaction_state.cursor_positions.get(&dragged.group) {
      let parent_matrix = global_transform
        .compute_matrix()
        .mul_mat4(&transform.compute_matrix().inverse());
      let global_hook_translation =
        (*cursor_position + dragged.translation).extend(transform.translation.z);

      transform.translation = parent_matrix
        .inverse()
        .transform_point3(global_hook_translation);
    }
  }
}

pub enum DropStrategy {
  Reset,
  Leave,
}

pub struct Draggable {
  // Where the entity is hooked onto the cursor while dragging.
  // If no hook is given, the entity will be pinned to the cursor
  // as it was when the drag was started.
  pub hook:          Option<Vec2>,
  pub groups:        Vec<Group>,
  pub drop_strategy: DropStrategy,
}

impl Default for Draggable {
  fn default() -> Self {
    Self {
      hook:          None,
      groups:        vec![Group::default()],
      drop_strategy: DropStrategy::Leave,
    }
  }
}

pub fn mouse_press_start_drag_system(
  interaction_state: Res<InteractionState>,
  mouse_button_input: Res<Input<MouseButton>>,
  draggables: Query<(Entity, &Draggable, &GlobalTransform), With<Interactable>>,
  mut commands: Commands,
) {
  if !mouse_button_input.just_pressed(MouseButton::Left) {
    return;
  }
  for (entity, draggable, global_transform) in draggables.iter() {
    for group in draggable.groups.iter() {
      if let Some(list) = interaction_state.ordered_interact_list_map.get(group) {
        if let Some((_, position)) = list.iter().find(|(e, _)| e == &entity) {
          let translation = draggable
            .hook
            .unwrap_or(global_transform.translation.truncate() - *position);
          commands.entity(entity).insert(Dragged {
            group: group.clone(),
            translation,
            origin: global_transform.translation.truncate(),
            just_dropped: false,
            just_dragged: true,
          });
          break;
        }
      }
    }
  }
}

pub fn mouse_release_stop_drag_system(
  mouse_button_input: Res<Input<MouseButton>>,
  mut draggables: Query<(Entity, &Draggable, &mut Dragged, &mut Transform), With<Interactable>>,
  mut commands: Commands,
) {
  if !mouse_button_input.just_released(MouseButton::Left) {
    return;
  }
  for (entity, draggable, mut dragged, mut transform) in draggables.iter_mut() {
    if dragged.just_dropped {
      if let DropStrategy::Reset = draggable.drop_strategy {
        transform.translation = dragged.origin.extend(transform.translation.z);
      }
      commands.entity(entity).remove::<Dragged>();
    } else {
      dragged.just_dropped = true;
    }
  }
}