docs: convert Phase 2 classes to documentation macros (Animation, Window, SceneObject)

Converted 3 files to use MCRF_* documentation macros:
- PyAnimation.cpp: 5 methods + 5 properties
- PyWindow.cpp: 3 methods + 8 properties
- PySceneObject.cpp: 3 methods + 2 properties

All conversions build successfully. Enhanced descriptions with implementation details.

Note: PyScene.cpp has no exposed methods/properties, so no conversion needed.

Progress: Phase 1 (4 files) + Phase 2 (3 files) = 7 new classes complete

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
John McCardle 2025-10-30 17:03:28 -04:00
parent 67aba5ef1f
commit 29aa6e62be
3 changed files with 100 additions and 36 deletions

View File

@ -1,5 +1,6 @@
#include "PyAnimation.h"
#include "McRFPy_API.h"
#include "McRFPy_Doc.h"
#include "UIDrawable.h"
#include "UIFrame.h"
#include "UICaption.h"
@ -261,33 +262,58 @@ PyObject* PyAnimation::has_valid_target(PyAnimationObject* self, PyObject* args)
}
PyGetSetDef PyAnimation::getsetters[] = {
{"property", (getter)get_property, NULL, "Target property name", NULL},
{"duration", (getter)get_duration, NULL, "Animation duration in seconds", NULL},
{"elapsed", (getter)get_elapsed, NULL, "Elapsed time in seconds", NULL},
{"is_complete", (getter)get_is_complete, NULL, "Whether animation is complete", NULL},
{"is_delta", (getter)get_is_delta, NULL, "Whether animation uses delta mode", NULL},
{"property", (getter)get_property, NULL,
MCRF_PROPERTY(property, "Target property name (str, read-only). The property being animated (e.g., 'pos', 'opacity', 'sprite_index')."), NULL},
{"duration", (getter)get_duration, NULL,
MCRF_PROPERTY(duration, "Animation duration in seconds (float, read-only). Total time for the animation to complete."), NULL},
{"elapsed", (getter)get_elapsed, NULL,
MCRF_PROPERTY(elapsed, "Elapsed time in seconds (float, read-only). Time since the animation started."), NULL},
{"is_complete", (getter)get_is_complete, NULL,
MCRF_PROPERTY(is_complete, "Whether animation is complete (bool, read-only). True when elapsed >= duration or complete() was called."), NULL},
{"is_delta", (getter)get_is_delta, NULL,
MCRF_PROPERTY(is_delta, "Whether animation uses delta mode (bool, read-only). In delta mode, the target value is added to the starting value."), NULL},
{NULL}
};
PyMethodDef PyAnimation::methods[] = {
{"start", (PyCFunction)start, METH_VARARGS,
"start(target) -> None\n\n"
"Start the animation on a target UI element.\n\n"
"Args:\n"
" target: The UI element to animate (Frame, Caption, Sprite, Grid, or Entity)\n\n"
"Note:\n"
" The animation will automatically stop if the target is destroyed."},
MCRF_METHOD(Animation, start,
MCRF_SIG("(target: UIDrawable)", "None"),
MCRF_DESC("Start the animation on a target UI element."),
MCRF_ARGS_START
MCRF_ARG("target", "The UI element to animate (Frame, Caption, Sprite, Grid, or Entity)")
MCRF_RETURNS("None")
MCRF_NOTE("The animation will automatically stop if the target is destroyed. Call AnimationManager.update(delta_time) each frame to progress animations.")
)},
{"update", (PyCFunction)update, METH_VARARGS,
"Update the animation by deltaTime (returns True if still running)"},
MCRF_METHOD(Animation, update,
MCRF_SIG("(delta_time: float)", "bool"),
MCRF_DESC("Update the animation by the given time delta."),
MCRF_ARGS_START
MCRF_ARG("delta_time", "Time elapsed since last update in seconds")
MCRF_RETURNS("bool: True if animation is still running, False if complete")
MCRF_NOTE("Typically called by AnimationManager automatically. Manual calls only needed for custom animation control.")
)},
{"get_current_value", (PyCFunction)get_current_value, METH_NOARGS,
"Get the current interpolated value"},
MCRF_METHOD(Animation, get_current_value,
MCRF_SIG("()", "Any"),
MCRF_DESC("Get the current interpolated value of the animation."),
MCRF_RETURNS("Any: Current value (type depends on property: float, int, Color tuple, Vector tuple, or str)")
MCRF_NOTE("Return type matches the target property type. For sprite_index returns int, for pos returns (x, y), for fill_color returns (r, g, b, a).")
)},
{"complete", (PyCFunction)complete, METH_NOARGS,
"complete() -> None\n\n"
"Complete the animation immediately by jumping to the final value."},
MCRF_METHOD(Animation, complete,
MCRF_SIG("()", "None"),
MCRF_DESC("Complete the animation immediately by jumping to the final value."),
MCRF_RETURNS("None")
MCRF_NOTE("Sets elapsed = duration and applies target value immediately. Completion callback will be called if set.")
)},
{"hasValidTarget", (PyCFunction)has_valid_target, METH_NOARGS,
"hasValidTarget() -> bool\n\n"
"Check if the animation still has a valid target.\n\n"
"Returns:\n"
" True if the target still exists, False if it was destroyed."},
MCRF_METHOD(Animation, hasValidTarget,
MCRF_SIG("()", "bool"),
MCRF_DESC("Check if the animation still has a valid target."),
MCRF_RETURNS("bool: True if the target still exists, False if it was destroyed")
MCRF_NOTE("Animations automatically clean up when targets are destroyed. Use this to check if manual cleanup is needed.")
)},
{NULL}
};

View File

@ -2,6 +2,7 @@
#include "PyScene.h"
#include "GameEngine.h"
#include "McRFPy_API.h"
#include "McRFPy_Doc.h"
#include <iostream>
// Static map to store Python scene objects by name
@ -213,19 +214,38 @@ void PySceneClass::call_on_resize(PySceneObject* self, int width, int height)
// Properties
PyGetSetDef PySceneClass::getsetters[] = {
{"name", (getter)get_name, NULL, "Scene name", NULL},
{"active", (getter)get_active, NULL, "Whether this scene is currently active", NULL},
{"name", (getter)get_name, NULL,
MCRF_PROPERTY(name, "Scene name (str, read-only). Unique identifier for this scene."), NULL},
{"active", (getter)get_active, NULL,
MCRF_PROPERTY(active, "Whether this scene is currently active (bool, read-only). Only one scene can be active at a time."), NULL},
{NULL}
};
// Methods
PyMethodDef PySceneClass::methods[] = {
{"activate", (PyCFunction)activate, METH_NOARGS,
"Make this the active scene"},
MCRF_METHOD(SceneClass, activate,
MCRF_SIG("()", "None"),
MCRF_DESC("Make this the active scene."),
MCRF_RETURNS("None")
MCRF_NOTE("Deactivates the current scene and activates this one. Scene transitions and lifecycle callbacks are triggered.")
)},
{"get_ui", (PyCFunction)get_ui, METH_NOARGS,
"Get the UI element collection for this scene"},
MCRF_METHOD(SceneClass, get_ui,
MCRF_SIG("()", "UICollection"),
MCRF_DESC("Get the UI element collection for this scene."),
MCRF_RETURNS("UICollection: Collection of UI elements (Frames, Captions, Sprites, Grids) in this scene")
MCRF_NOTE("Use to add, remove, or iterate over UI elements. Changes are reflected immediately.")
)},
{"register_keyboard", (PyCFunction)register_keyboard, METH_VARARGS,
"Register a keyboard handler function (alternative to overriding on_keypress)"},
MCRF_METHOD(SceneClass, register_keyboard,
MCRF_SIG("(callback: callable)", "None"),
MCRF_DESC("Register a keyboard event handler function."),
MCRF_ARGS_START
MCRF_ARG("callback", "Function that receives (key: str, pressed: bool) when keyboard events occur")
MCRF_RETURNS("None")
MCRF_NOTE("Alternative to overriding on_keypress() method. Handler is called for both key press and release events.")
)},
{NULL}
};

View File

@ -1,6 +1,7 @@
#include "PyWindow.h"
#include "GameEngine.h"
#include "McRFPy_API.h"
#include "McRFPy_Doc.h"
#include <SFML/Graphics.hpp>
#include <cstring>
@ -483,32 +484,49 @@ int PyWindow::set_scaling_mode(PyWindowObject* self, PyObject* value, void* clos
// Property definitions
PyGetSetDef PyWindow::getsetters[] = {
{"resolution", (getter)get_resolution, (setter)set_resolution,
"Window resolution as (width, height) tuple", NULL},
{"resolution", (getter)get_resolution, (setter)set_resolution,
MCRF_PROPERTY(resolution, "Window resolution as (width, height) tuple. Setting this recreates the window."), NULL},
{"fullscreen", (getter)get_fullscreen, (setter)set_fullscreen,
"Window fullscreen state", NULL},
MCRF_PROPERTY(fullscreen, "Window fullscreen state (bool). Setting this recreates the window."), NULL},
{"vsync", (getter)get_vsync, (setter)set_vsync,
"Vertical sync enabled state", NULL},
MCRF_PROPERTY(vsync, "Vertical sync enabled state (bool). Prevents screen tearing but may limit framerate."), NULL},
{"title", (getter)get_title, (setter)set_title,
"Window title string", NULL},
MCRF_PROPERTY(title, "Window title string (str). Displayed in the window title bar."), NULL},
{"visible", (getter)get_visible, (setter)set_visible,
"Window visibility state", NULL},
MCRF_PROPERTY(visible, "Window visibility state (bool). Hidden windows still process events."), NULL},
{"framerate_limit", (getter)get_framerate_limit, (setter)set_framerate_limit,
"Frame rate limit (0 for unlimited)", NULL},
MCRF_PROPERTY(framerate_limit, "Frame rate limit in FPS (int, 0 for unlimited). Caps maximum frame rate."), NULL},
{"game_resolution", (getter)get_game_resolution, (setter)set_game_resolution,
"Fixed game resolution as (width, height) tuple", NULL},
MCRF_PROPERTY(game_resolution, "Fixed game resolution as (width, height) tuple. Enables resolution-independent rendering with scaling."), NULL},
{"scaling_mode", (getter)get_scaling_mode, (setter)set_scaling_mode,
"Viewport scaling mode: 'center', 'stretch', or 'fit'", NULL},
MCRF_PROPERTY(scaling_mode, "Viewport scaling mode (str): 'center' (no scaling), 'stretch' (fill window), or 'fit' (maintain aspect ratio)."), NULL},
{NULL}
};
// Method definitions
PyMethodDef PyWindow::methods[] = {
{"get", (PyCFunction)PyWindow::get, METH_VARARGS | METH_CLASS,
"Get the Window singleton instance"},
MCRF_METHOD(Window, get,
MCRF_SIG("()", "Window"),
MCRF_DESC("Get the Window singleton instance."),
MCRF_RETURNS("Window: The global window object")
MCRF_NOTE("This is a class method. Call as Window.get(). There is only one window instance per application.")
)},
{"center", (PyCFunction)PyWindow::center, METH_NOARGS,
"Center the window on the screen"},
MCRF_METHOD(Window, center,
MCRF_SIG("()", "None"),
MCRF_DESC("Center the window on the screen."),
MCRF_RETURNS("None")
MCRF_NOTE("Only works in windowed mode. Has no effect when fullscreen or in headless mode.")
)},
{"screenshot", (PyCFunction)PyWindow::screenshot, METH_VARARGS | METH_KEYWORDS,
"Take a screenshot. Pass filename to save to file, or get raw bytes if no filename."},
MCRF_METHOD(Window, screenshot,
MCRF_SIG("(filename: str = None)", "bytes | None"),
MCRF_DESC("Take a screenshot of the current window contents."),
MCRF_ARGS_START
MCRF_ARG("filename", "Optional path to save screenshot. If omitted, returns raw RGBA bytes.")
MCRF_RETURNS("bytes | None: Raw RGBA pixel data if no filename given, otherwise None after saving")
MCRF_NOTE("Screenshot is taken at the actual window resolution. Use after render loop update for current frame.")
)},
{NULL}
};