cursor/
cursor.rs

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