Implement Scene subclass on_key callback support
Scene subclasses can now define on_key(self, key, state) methods that
receive keyboard events, matching the existing on_enter, on_exit, and
update lifecycle callbacks.
Changes:
- Rename call_on_keypress to call_on_key (consistent naming with property)
- Add triggerKeyEvent helper in McRFPy_API
- Call triggerKeyEvent from GameEngine when key_callable is not set
- Fix condition to check key_callable.isNone() (not just pointer existence)
- Handle both bound methods and instance-assigned callables
Usage:
class GameScene(mcrfpy.Scene):
def on_key(self, key, state):
if key == "Escape" and state == "end":
quit_game()
Property assignment (scene.on_key = callable) still works and takes
precedence when key_callable is set via the property setter.
Includes comprehensive test: tests/unit/scene_subclass_on_key_test.py
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b6eb70748a
commit
1d11b020b0
5 changed files with 118 additions and 10 deletions
|
|
@ -413,23 +413,28 @@ void PySceneClass::call_on_exit(PySceneObject* self)
|
|||
}
|
||||
}
|
||||
|
||||
void PySceneClass::call_on_keypress(PySceneObject* self, std::string key, std::string action)
|
||||
void PySceneClass::call_on_key(PySceneObject* self, const std::string& key, const std::string& action)
|
||||
{
|
||||
PyGILState_STATE gstate = PyGILState_Ensure();
|
||||
|
||||
PyObject* method = PyObject_GetAttrString((PyObject*)self, "on_keypress");
|
||||
if (method && PyCallable_Check(method)) {
|
||||
PyObject* result = PyObject_CallFunction(method, "ss", key.c_str(), action.c_str());
|
||||
// Look for on_key attribute on the Python object
|
||||
// This handles both:
|
||||
// 1. Subclass methods: class MyScene(Scene): def on_key(self, k, s): ...
|
||||
// 2. Instance attributes: ts.on_key = lambda k, s: ... (when subclass shadows property)
|
||||
PyObject* attr = PyObject_GetAttrString((PyObject*)self, "on_key");
|
||||
if (attr && PyCallable_Check(attr) && attr != Py_None) {
|
||||
// Call it - works for both bound methods and regular callables
|
||||
PyObject* result = PyObject_CallFunction(attr, "ss", key.c_str(), action.c_str());
|
||||
if (result) {
|
||||
Py_DECREF(result);
|
||||
} else {
|
||||
PyErr_Print();
|
||||
}
|
||||
Py_DECREF(method);
|
||||
Py_DECREF(attr);
|
||||
} else {
|
||||
// Clear AttributeError if method doesn't exist
|
||||
// Not callable or is None - nothing to call
|
||||
PyErr_Clear();
|
||||
Py_XDECREF(method);
|
||||
Py_XDECREF(attr);
|
||||
}
|
||||
|
||||
PyGILState_Release(gstate);
|
||||
|
|
@ -571,6 +576,18 @@ void McRFPy_API::triggerResize(int width, int height)
|
|||
}
|
||||
}
|
||||
|
||||
// Helper function to trigger key events on Python scene subclasses
|
||||
void McRFPy_API::triggerKeyEvent(const std::string& key, const std::string& action)
|
||||
{
|
||||
GameEngine* game = McRFPy_API::game;
|
||||
if (!game) return;
|
||||
|
||||
// Only notify the active scene if it has an on_key method (subclass)
|
||||
if (python_scenes.count(game->scene) > 0) {
|
||||
PySceneClass::call_on_key(python_scenes[game->scene], key, action);
|
||||
}
|
||||
}
|
||||
|
||||
// #151: Get the current scene as a Python Scene object
|
||||
PyObject* McRFPy_API::api_get_current_scene()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue