-
-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cursor icon support through Game trait #112
Cursor icon support through Game trait #112
Conversation
For the main issue, I think we could do something similar to what the current UI loop does here (ignoring the Lines 349 to 358 in 860753b
Basically, we store the last However, I think this is mainly a On a related note, we should probably implement the cursor update logic in the
Yeah, let's just start simple for now. We will need to come up with good names for each of the icons, which won't be easy 😅
We could implement
👍 |
I actually saw this bit and thought, maybe it's not too bad to keep track of the state of the cursor.
Although this does address the constant mutation of the cursor, I don't think it addresses the main issue by itself, unless I am missing something. (which is very likely 🙈 ) I assume some games will want to completely control the cursor and don't let the system take over at all. If that assumption is true, we'll still want to set the cursor to whatever is returned from the Having said that, my assumption might be completely off or might be an edge case. So, I'd be totally fine with ignoring all this for now and just implement the general case. I was just worried that by setting the cursor icon through the
Completely agreed! I looked very hard to see if what I am expecting was handled through an
Thank you for the note! I must have spent like an hour looking at that code yesterday. I think I am missing something about Rust syntax there to completely understand and follow it. I'll give it another try tonight 🤞 |
@hecrj I've moved the cursor update for the I've also made
or even with more type safety and restrictions:
This is just a rough idea, I am not proposing these exact states, but hopefully it gets the point across. In any case, I'd love to hear your thoughts before I continue with this PR. If we don't have a concrete plan for now, I think it might be worth to scope this PR and open an issue for the remaining improvements. |
src/ui.rs
Outdated
let new_game_cursor = ui.cursor_icon(); | ||
if new_game_cursor != self.game_cursor { | ||
self.game_cursor = new_game_cursor; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may be able to move this inside the if
branch below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 13429f2.
src/ui.rs
Outdated
window.update_cursor(self.game_cursor.into()); | ||
} else { | ||
window.set_cursor_visible(true); | ||
window.update_cursor(self.ui_cursor.into()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid cursor flickering, it may be a good idea to save the last cursor and visibility in graphics::Window
and only actually change state if necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 13429f2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's looking quite nice! Thank you! 🎉
Once we unify set_cursor_visible
and update_cursor
, I think it will be even better.
src/graphics/window.rs
Outdated
@@ -20,6 +22,8 @@ pub struct Window { | |||
width: f32, | |||
height: f32, | |||
is_fullscreen: bool, | |||
cursor_icon: winit::window::CursorIcon, | |||
cursor_visible: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could probably use an Option
to encode both things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 66828da.
self.surface.window().set_cursor_icon(new_cursor); | ||
self.cursor_icon = new_cursor; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then, we can make this take an Option<winit::Window::CursorIcon>
and remove set_cursor_visible
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 66828da.
src/ui.rs
Outdated
if self.mouse_cursor == MouseCursor::OutOfBounds { | ||
let game_cursor = ui.cursor_icon(); | ||
window.set_cursor_visible(game_cursor != CursorIcon::Hidden); | ||
window.update_cursor(game_cursor.into()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally, if we make CursorIcon
implement TryFrom
instead of From
, then we should be able to call update_cursor
easily like this:
window.update_cursor(game_cursor.try_into().ok());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 66828da.
src/ui.rs
Outdated
window.update_cursor(game_cursor.into()); | ||
} else { | ||
window.set_cursor_visible(true); | ||
window.update_cursor(self.mouse_cursor.into()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and we will need to add a Some
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 66828da.
@hecrj Thanks for the reviews. I've made your suggested changes and added documentation. I am marking this PR as ready to review. Let me know what you think! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it looks great! Thank you 🎉
Would you like to update the CHANGELOG
too? I enjoy doing that when I finish a feature and I figured you might as well :)
@hecrj I got stuck on some implementation details & decisions to make for #111, so I thought I'd open a draft PR to make it easier to discuss. The main problem revolves around the following:
When we control the cursor icon through the game trait by updating it every frame, we prevent the system taking over the icon. As an example, if I set the icon to
Default
and then try toMove
the window in Windows 10, theMove
icon goes away immediately. That might be what some clients want, but I don't think we should force them into specifying an icon at all times unless they want to.There are several ways to get around the issue, most of which I think will require us keeping state of the cursor, because as far as I can tell, there is no way to get the current cursor icon from
winit
.Game
instead offn cursor_icon(&self) -> CursorIcon
we could havefn cursor_icon(&self, current_icon: CursorIcon) -> CursorIcon
which would let the client decide when and how to update the cursor. It'd provide the knowledge of the currentcursor_icon
which might come in handy in some cases, although I am not sure if it'd be anything common.Unchanged
variant or something similar in theCursorIcon
enum
and don't update the icon in this case. I am afraid that this might be a bit more confusing then the first option, but it's hard to say.Game
instead offn cursor_icon(&self) -> CursorIcon
we could havefn cursor_icon(&self) -> Option<CursorIcon>
.None
could mean that don't set thecursor_icon
manually. It'd result in the last cursor icon to remain as is, but would also let the system take over. Without checking the docs, it might be interpreted asHidden
which would be bad.I don't even know how this all connects to
UserInterface
and how the cursor is updated from there. I might be thinking too much over this and just a simple implementation might be fine too, I am not sure. Either way, I'd really appreciate your input on what you think is the best course of action.Notes about the other parts of this WIP PR:
winit
or not. I was thinking I'd add only a few of them just to get the implementation out and then separately the different variants could be decided. I am totally fine with implementing the variants you might already have in mind.CursorIcon::Hidden
being mapped to theDefault
icon makes me even sadder. If we don't end up keeping track of the state, I think it might be best to useTryFrom
trait or have a custom function that returnsOption<CursorIcon>
and don't update the cursor if an icon variant doesn't make sense. I also played with having an enum like below, but I am not sure if added complexity is worth it.@hecrj Sorry for the long PR description. I'd appreciate any feedback you might have. (for the code or the questions) Feel free to ignore the notes for now if you want to focus on the main issue.