Skip to main content

rustapi/tools/
selection.rs

1use crate::hud::{Hud, HudMode};
2use crate::prelude::*;
3use MapEvent::*;
4use ToolEvent::*;
5use rusterix::Assets;
6
7pub struct SelectionTool {
8    id: TheId,
9    click_pos: Vec2<f32>,
10    rectangle_undo_map: Map,
11    hud: Hud,
12}
13
14impl Tool for SelectionTool {
15    fn new() -> Self
16    where
17        Self: Sized,
18    {
19        Self {
20            id: TheId::named("Select Tool"),
21            click_pos: Vec2::zero(),
22            rectangle_undo_map: Map::default(),
23            hud: Hud::new(HudMode::Selection),
24        }
25    }
26
27    fn id(&self) -> TheId {
28        self.id.clone()
29    }
30    fn info(&self) -> String {
31        if cfg!(target_os = "macos") {
32            fl!("tool_selection_mac")
33        } else {
34            fl!("tool_selection")
35        }
36    }
37    fn icon_name(&self) -> String {
38        str!("cursor")
39    }
40    fn accel(&self) -> Option<char> {
41        Some('S')
42    }
43
44    fn help_url(&self) -> Option<String> {
45        Some("docs/creator/tools/selection".to_string())
46    }
47
48    fn tool_event(
49        &mut self,
50        tool_event: ToolEvent,
51        ui: &mut TheUI,
52        _ctx: &mut TheContext,
53        _project: &mut Project,
54        server_ctx: &mut ServerContext,
55    ) -> bool {
56        match tool_event {
57            Activate => {
58                server_ctx.curr_map_tool_type = MapToolType::Selection;
59
60                return true;
61            }
62            DeActivate => {
63                if let Some(layout) = ui.get_hlayout("Game Tool Params") {
64                    layout.clear();
65                    layout.set_reverse_index(None);
66                }
67                server_ctx.curr_map_tool_type = MapToolType::General;
68                return true;
69            }
70            _ => {}
71        };
72        false
73    }
74
75    fn map_event(
76        &mut self,
77        map_event: MapEvent,
78        ui: &mut TheUI,
79        ctx: &mut TheContext,
80        map: &mut Map,
81        server_ctx: &mut ServerContext,
82    ) -> Option<ProjectUndoAtom> {
83        let mut undo_atom: Option<ProjectUndoAtom> = None;
84
85        match map_event {
86            MapKey(c) => {
87                match c {
88                    '1'..='9' => map.subdivisions = (c as u8 - b'0') as f32,
89                    '0' => map.subdivisions = 10.0,
90                    _ => {}
91                }
92                crate::editor::RUSTERIX.write().unwrap().set_dirty();
93            }
94            MapClicked(coord) => {
95                if self.hud.clicked(coord.x, coord.y, map, ui, ctx, server_ctx) {
96                    crate::editor::RUSTERIX.write().unwrap().set_dirty();
97                    return None;
98                }
99
100                if !server_ctx.hover_is_empty() {
101                    let prev = map.clone();
102                    let arrays = server_ctx.hover_to_arrays();
103                    if ui.shift {
104                        // Add
105                        map.add_to_selection(arrays.0, arrays.1, arrays.2);
106                    } else if ui.alt {
107                        // Remove
108                        map.remove_from_selection(arrays.0, arrays.1, arrays.2);
109                    } else {
110                        // Replace
111                        map.selected_vertices = arrays.0;
112                        map.selected_linedefs = arrays.1;
113                        map.selected_sectors = arrays.2;
114                    }
115
116                    undo_atom = Some(ProjectUndoAtom::MapEdit(
117                        server_ctx.pc,
118                        Box::new(prev),
119                        Box::new(map.clone()),
120                    ));
121
122                    ctx.ui.send(TheEvent::Custom(
123                        TheId::named("Map Selection Changed"),
124                        TheValue::Empty,
125                    ));
126                }
127
128                self.click_pos = Vec2::new(coord.x as f32, coord.y as f32);
129                self.rectangle_undo_map = map.clone();
130            }
131            MapDragged(coord) => {
132                if server_ctx.editor_view_mode != EditorViewMode::D2 {
133                    if let Some(render_view) = ui.get_render_view("PolyView") {
134                        let dim = *render_view.dim();
135                        let click_pos = server_ctx.local_to_map_grid(
136                            Vec2::new(dim.width as f32, dim.height as f32),
137                            self.click_pos,
138                            map,
139                            map.subdivisions,
140                        );
141                        let drag_pos = server_ctx.local_to_map_grid(
142                            Vec2::new(dim.width as f32, dim.height as f32),
143                            Vec2::new(coord.x as f32, coord.y as f32),
144                            map,
145                            map.subdivisions,
146                        );
147
148                        let top_left =
149                            Vec2::new(click_pos.x.min(drag_pos.x), click_pos.y.min(drag_pos.y));
150                        let bottom_right =
151                            Vec2::new(click_pos.x.max(drag_pos.x), click_pos.y.max(drag_pos.y));
152
153                        let selection =
154                            server_ctx.geometry_in_rectangle(top_left, bottom_right, map);
155
156                        *map = self.rectangle_undo_map.clone();
157                        map.curr_rectangle = Some((click_pos, drag_pos));
158
159                        if ui.shift {
160                            // Add
161                            map.add_to_selection(selection.0, selection.1, selection.2);
162                        } else if ui.alt {
163                            // Remove
164                            map.remove_from_selection(selection.0, selection.1, selection.2);
165                        } else {
166                            // Replace
167                            map.selected_vertices = selection.0;
168                            map.selected_linedefs = selection.1;
169                            map.selected_sectors = selection.2;
170                        }
171                    }
172                    crate::editor::RUSTERIX.write().unwrap().set_dirty();
173                }
174            }
175            MapUp(_) => {
176                if server_ctx.editor_view_mode != EditorViewMode::D2 {
177                    if map.curr_rectangle.is_some() {
178                        map.curr_rectangle = None;
179
180                        undo_atom = Some(ProjectUndoAtom::MapEdit(
181                            server_ctx.pc,
182                            Box::new(self.rectangle_undo_map.clone()),
183                            Box::new(map.clone()),
184                        ));
185
186                        ctx.ui.send(TheEvent::Custom(
187                            TheId::named("Map Selection Changed"),
188                            TheValue::Empty,
189                        ));
190                    }
191                }
192            }
193            MapHover(coord) => {
194                if self.hud.hovered(coord.x, coord.y, map, ui, ctx, server_ctx) {
195                    crate::editor::RUSTERIX.write().unwrap().set_dirty();
196                    return None;
197                }
198
199                if let Some(render_view) = ui.get_render_view("PolyView") {
200                    let dim = *render_view.dim();
201                    server_ctx.hover = server_ctx.geometry_at(
202                        Vec2::new(dim.width as f32, dim.height as f32),
203                        Vec2::new(coord.x as f32, coord.y as f32),
204                        map,
205                    );
206
207                    let cp = server_ctx.local_to_map_grid(
208                        Vec2::new(dim.width as f32, dim.height as f32),
209                        Vec2::new(coord.x as f32, coord.y as f32),
210                        map,
211                        map.subdivisions,
212                    );
213                    ctx.ui.send(TheEvent::Custom(
214                        TheId::named("Cursor Pos Changed"),
215                        TheValue::Float2(cp),
216                    ));
217                    server_ctx.hover_cursor = Some(cp);
218                }
219            }
220            MapDelete => {
221                if !map.selected_vertices.is_empty()
222                    || !map.selected_linedefs.is_empty()
223                    || !map.selected_sectors.is_empty()
224                {
225                    let prev = map.clone();
226
227                    let vertices = map.selected_vertices.clone();
228                    let linedefs = map.selected_linedefs.clone();
229                    let sectors = map.selected_sectors.clone();
230
231                    map.delete_elements(&vertices, &linedefs, &sectors);
232                    map.selected_vertices.clear();
233                    map.selected_linedefs.clear();
234                    map.selected_sectors.clear();
235
236                    undo_atom = Some(ProjectUndoAtom::MapEdit(
237                        server_ctx.pc,
238                        Box::new(prev),
239                        Box::new(map.clone()),
240                    ));
241
242                    ctx.ui.send(TheEvent::Custom(
243                        TheId::named("Map Selection Changed"),
244                        TheValue::Empty,
245                    ));
246                }
247            }
248            MapEscape => {
249                // Hover is empty, check if we need to clear selection
250                if !map.selected_vertices.is_empty()
251                    || !map.selected_linedefs.is_empty()
252                    || !map.selected_sectors.is_empty()
253                {
254                    let prev = map.clone();
255
256                    map.selected_vertices.clear();
257                    map.selected_linedefs.clear();
258                    map.selected_sectors.clear();
259
260                    undo_atom = Some(ProjectUndoAtom::MapEdit(
261                        server_ctx.pc,
262                        Box::new(prev),
263                        Box::new(map.clone()),
264                    ));
265
266                    ctx.ui.send(TheEvent::Custom(
267                        TheId::named("Map Selection Changed"),
268                        TheValue::Empty,
269                    ));
270                }
271                server_ctx.profile_view = None;
272            }
273        }
274
275        undo_atom
276    }
277
278    fn draw_hud(
279        &mut self,
280        buffer: &mut TheRGBABuffer,
281        map: &mut Map,
282        ctx: &mut TheContext,
283        server_ctx: &mut ServerContext,
284        assets: &Assets,
285    ) {
286        self.hud.draw(buffer, map, ctx, server_ctx, None, assets);
287    }
288    /*
289    fn handle_event(
290        &mut self,
291        event: &TheEvent,
292        _ui: &mut TheUI,
293        ctx: &mut TheContext,
294        _project: &mut Project,
295        server_ctx: &mut ServerContext,
296    ) -> bool {
297        let mut redraw = false;
298        #[allow(clippy::single_match)]
299        match event {
300            _ => {}
301        }
302        redraw
303    }*/
304}