cursor/
cursor.rs

1use sfml::{
2    graphics::{
3        Color, Font, Rect, RectangleShape, RenderTarget, RenderWindow, Shape, Text, Transformable,
4    },
5    system::Vector2,
6    window::{mouse, ContextSettings, Cursor, CursorType, Event, Style},
7    SfResult,
8};
9
10include!("../example_common.rs");
11
12const DRAW_AREA_TOPLEFT: (u16, u16) = (300, 64);
13const DRAW_GRID_WH: u8 = 16;
14const DRAW_CELL_WH: u8 = 26;
15
16fn gridindex(
17    grid: &mut [bool; DRAW_GRID_WH as usize * DRAW_GRID_WH as usize],
18    x: usize,
19    y: usize,
20) -> Option<&mut bool> {
21    grid.get_mut(y * DRAW_GRID_WH as usize + x)
22}
23
24fn mouse_over(rect: &Rect<i32>, mouse_x: i32, mouse_y: i32) -> bool {
25    rect.contains(Vector2::new(mouse_x, mouse_y))
26}
27
28enum ButtonStyle {
29    Normal,
30    Highlighted,
31    Selected,
32    Error,
33}
34
35fn draw_button(
36    rect: &Rect<i32>,
37    shape: &mut RectangleShape,
38    text: &mut Text,
39    string: &str,
40    render_window: &mut RenderWindow,
41    style: ButtonStyle,
42) {
43    shape.set_position((rect.left as f32, rect.top as f32));
44    shape.set_size((rect.width as f32, rect.height as f32));
45    let (rect_fill, rect_outline, text_fill) = match style {
46        ButtonStyle::Normal => (Color::TRANSPARENT, Color::WHITE, Color::WHITE),
47        ButtonStyle::Highlighted => (Color::WHITE, Color::WHITE, Color::BLACK),
48        ButtonStyle::Selected => (Color::GREEN, Color::GREEN, Color::BLACK),
49        ButtonStyle::Error => (Color::RED, Color::BLACK, Color::BLACK),
50    };
51    shape.set_outline_color(rect_outline);
52    shape.set_fill_color(rect_fill);
53    text.set_position((rect.left as f32 + 12.0, rect.top as f32 + 8.0));
54    text.set_fill_color(text_fill);
55    text.set_string(string);
56    render_window.draw(shape);
57    render_window.draw(text);
58}
59
60fn bstyle(highlighted: bool, selected: bool, error: bool) -> ButtonStyle {
61    if error {
62        ButtonStyle::Error
63    } else if highlighted {
64        ButtonStyle::Highlighted
65    } else if selected {
66        ButtonStyle::Selected
67    } else {
68        ButtonStyle::Normal
69    }
70}
71
72fn main() -> SfResult<()> {
73    example_ensure_right_working_dir();
74
75    let mut cursor = Cursor::from_system(CursorType::Arrow)?;
76    let mut rw = RenderWindow::new(
77        (800, 800),
78        "SFML cursor example",
79        Style::CLOSE,
80        &ContextSettings::default(),
81    )?;
82    rw.set_vertical_sync_enabled(true);
83    let font = Font::from_file("sansation.ttf")?;
84    let mut failed_index = usize::MAX;
85    let mut selected_index = usize::MAX;
86    let set_button = Rect::new(348, 500, 100, 32);
87    let hotspot_button = Rect::new(458, 500, 100, 32);
88    let clear_button = Rect::new(568, 500, 100, 32);
89    let mut pixel_grid = [false; DRAW_GRID_WH as usize * DRAW_GRID_WH as usize];
90    let mut hotspot_selection = false;
91    let mut hotspot_selected = false;
92    let mut hotspot = Vector2::new(8, 8);
93    let mut modif = false;
94
95    let mut buttons = Vec::new();
96    let cursor_types = [
97        CursorType::Arrow,
98        CursorType::ArrowWait,
99        CursorType::Wait,
100        CursorType::Text,
101        CursorType::Hand,
102        CursorType::SizeHorizontal,
103        CursorType::SizeVertical,
104        CursorType::SizeTopLeftBottomRight,
105        CursorType::SizeBottomLeftTopRight,
106        CursorType::SizeLeft,
107        CursorType::SizeRight,
108        CursorType::SizeTop,
109        CursorType::SizeBottom,
110        CursorType::SizeTopLeft,
111        CursorType::SizeBottomRight,
112        CursorType::SizeBottomLeft,
113        CursorType::SizeTopRight,
114        CursorType::SizeAll,
115        CursorType::Cross,
116        CursorType::Help,
117        CursorType::NotAllowed,
118    ];
119    for i in 0..cursor_types.len() {
120        buttons.push(Rect::new(16, 16 + i as i32 * 36, 250, 32));
121    }
122
123    while rw.is_open() {
124        while let Some(ev) = rw.poll_event() {
125            match ev {
126                Event::Closed => rw.close(),
127                Event::MouseButtonPressed {
128                    button: mouse::Button::Left,
129                    x,
130                    y,
131                } => {
132                    for (i, b) in buttons.iter().enumerate() {
133                        if mouse_over(b, x, y) {
134                            match cursor.load_from_system(cursor_types[i]) {
135                                Ok(()) => {
136                                    unsafe {
137                                        rw.set_mouse_cursor(&cursor);
138                                    }
139                                    selected_index = i;
140                                }
141                                Err(e) => {
142                                    eprintln!("{e}");
143                                    failed_index = i;
144                                }
145                            }
146                        }
147                    }
148                    if mouse_over(&set_button, x, y) {
149                        let mut pixels = [0; DRAW_GRID_WH as usize * DRAW_GRID_WH as usize * 4];
150                        for (i, px) in pixel_grid.iter().enumerate() {
151                            let offset = i * 4;
152                            if *px {
153                                pixels[offset] = 255;
154                                pixels[offset + 1] = 255;
155                                pixels[offset + 2] = 255;
156                                pixels[offset + 3] = 255;
157                            }
158                        }
159                        unsafe {
160                            match cursor.load_from_pixels(
161                                &pixels,
162                                Vector2::new(DRAW_GRID_WH as u32, DRAW_GRID_WH as u32),
163                                hotspot,
164                            ) {
165                                Ok(()) => {
166                                    rw.set_mouse_cursor(&cursor);
167                                }
168                                Err(e) => {
169                                    eprintln!("{e}");
170                                }
171                            }
172                        }
173                        modif = false;
174                    }
175                    if mouse_over(&clear_button, x, y) {
176                        for px in pixel_grid.iter_mut() {
177                            *px = false;
178                        }
179                        modif = true;
180                    }
181                    if mouse_over(&hotspot_button, x, y) {
182                        hotspot_selection = true;
183                    }
184                }
185                Event::MouseButtonReleased {
186                    button: mouse::Button::Left,
187                    ..
188                } => {
189                    if hotspot_selected {
190                        hotspot_selection = false;
191                        hotspot_selected = false;
192                    }
193                }
194                _ => {}
195            }
196        }
197        let mut set_button_highlighted = false;
198        let mut hotspot_button_highlighted = false;
199        let mut clear_button_highlighted = false;
200        // System cursor set button interactions
201        let mp = rw.mouse_position();
202        let mut highlight_index = usize::MAX;
203        for (i, b) in buttons.iter().enumerate() {
204            if mouse_over(b, mp.x, mp.y) {
205                highlight_index = i;
206            }
207        }
208        if mouse_over(&set_button, mp.x, mp.y) {
209            set_button_highlighted = true;
210        }
211        if mouse_over(&hotspot_button, mp.x, mp.y) {
212            hotspot_button_highlighted = true;
213        }
214        if mouse_over(&clear_button, mp.x, mp.y) {
215            clear_button_highlighted = true;
216        }
217        // Grid interactions
218        let rela_x = mp.x - DRAW_AREA_TOPLEFT.0 as i32;
219        let rela_y = mp.y - DRAW_AREA_TOPLEFT.1 as i32;
220        let (gx, gy) = (rela_x / DRAW_CELL_WH as i32, rela_y / DRAW_CELL_WH as i32);
221        if gx >= 0 && gy >= 0 {
222            if let Some(cell) = gridindex(&mut pixel_grid, gx as usize, gy as usize) {
223                if hotspot_selection {
224                    hotspot_selected = true;
225                    hotspot = Vector2::new(gx as u32, gy as u32);
226                    modif = true;
227                } else if mouse::Button::Left.is_pressed() {
228                    *cell = true;
229                    modif = true;
230                } else if mouse::Button::Right.is_pressed() {
231                    *cell = false;
232                    modif = true;
233                }
234            }
235        }
236        rw.clear(Color::BLACK);
237        // Draw system cursor set buttons
238        let mut shape = RectangleShape::default();
239        let mut text = Text::new("", &font, 14);
240        shape.set_outline_thickness(-1.0);
241        shape.set_outline_color(Color::WHITE);
242        for (i, b) in buttons.iter().enumerate() {
243            let types = [
244                "ARROW",
245                "ARROW_WAIT",
246                "WAIT",
247                "TEXT",
248                "HAND",
249                "SIZE_HORIZONTAL",
250                "SIZE_VERTICAL",
251                "SIZE_TOP_LEFT_BOTTOM_RIGHT",
252                "SIZE_BOTTOM_LEFT_TOP_RIGHT",
253                "SIZE_LEFT",
254                "SIZE_RIGHT",
255                "SIZE_TOP",
256                "SIZE_BOTTOM",
257                "SIZE_TOP_LEFT",
258                "SIZE_BOTTOM_RIGHT",
259                "SIZE_BOTTOM_LEFT",
260                "SIZE_TOP_RIGHT",
261                "SIZE_ALL",
262                "CROSS",
263                "HELP",
264                "NOT_ALLOWED",
265            ];
266            draw_button(
267                b,
268                &mut shape,
269                &mut text,
270                types[i],
271                &mut rw,
272                bstyle(highlight_index == i, selected_index == i, failed_index == i),
273            );
274        }
275        // Draw pixel drawing grid
276        shape.set_fill_color(Color::TRANSPARENT);
277        for y in 0..DRAW_GRID_WH {
278            for x in 0..DRAW_GRID_WH {
279                if hotspot.x == x as u32 && hotspot.y == y as u32 {
280                    shape.set_outline_color(Color::RED);
281                } else {
282                    shape.set_outline_color(Color::rgb(180, 180, 180));
283                }
284                if gridindex(&mut pixel_grid, x as usize, y as usize).is_some_and(|bool| *bool) {
285                    shape.set_fill_color(Color::WHITE);
286                } else {
287                    shape.set_fill_color(Color::TRANSPARENT);
288                }
289                shape.set_size((DRAW_CELL_WH as f32, DRAW_CELL_WH as f32));
290                shape.set_position((
291                    DRAW_AREA_TOPLEFT.0 as f32 + (x as f32 * DRAW_CELL_WH as f32),
292                    DRAW_AREA_TOPLEFT.1 as f32 + (y as f32 * DRAW_CELL_WH as f32),
293                ));
294                rw.draw(&shape);
295            }
296        }
297        draw_button(
298            &set_button,
299            &mut shape,
300            &mut text,
301            if modif { "Set*" } else { "Set" },
302            &mut rw,
303            bstyle(set_button_highlighted, false, false),
304        );
305        draw_button(
306            &hotspot_button,
307            &mut shape,
308            &mut text,
309            "Hotspot",
310            &mut rw,
311            bstyle(hotspot_button_highlighted, hotspot_selection, false),
312        );
313        draw_button(
314            &clear_button,
315            &mut shape,
316            &mut text,
317            "Clear",
318            &mut rw,
319            bstyle(clear_button_highlighted, false, false),
320        );
321        rw.display();
322    }
323    Ok(())
324}