diff --git a/CMakeLists.txt b/CMakeLists.txt index 66a900c7..20c14a01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.14) -project(libscratchcpp VERSION 0.10.0 LANGUAGES C CXX) +project(libscratchcpp VERSION 0.11.0 LANGUAGES C CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_STANDARD 17) @@ -31,11 +31,12 @@ target_sources(scratchcpp include/scratchcpp/scratchconfiguration.h include/scratchcpp/iengine.h include/scratchcpp/iextension.h - include/scratchcpp/iblocksection.h + include/scratchcpp/thread.h include/scratchcpp/asset.h include/scratchcpp/costume.h include/scratchcpp/sound.h include/scratchcpp/value.h + include/scratchcpp/valuedata.h include/scratchcpp/entity.h include/scratchcpp/variable.h include/scratchcpp/list.h @@ -50,9 +51,11 @@ target_sources(scratchcpp include/scratchcpp/block.h include/scratchcpp/istagehandler.h include/scratchcpp/ispritehandler.h + include/scratchcpp/drawable.h include/scratchcpp/target.h include/scratchcpp/stage.h include/scratchcpp/sprite.h + include/scratchcpp/textbubble.h include/scratchcpp/itimer.h include/scratchcpp/keyevent.h include/scratchcpp/rect.h diff --git a/Doxyfile b/Doxyfile index 9b266624..c2fcdb0a 100644 --- a/Doxyfile +++ b/Doxyfile @@ -958,7 +958,9 @@ RECURSIVE = YES # run. EXCLUDE = docs/theme \ - include/scratchcpp/spimpl.h + include/scratchcpp/spimpl.h \ + include/scratchcpp/signal.h \ + include/scratchcpp/veque.h # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/docs/Block sections.md b/docs/Block sections.md deleted file mode 100644 index a3d75212..00000000 --- a/docs/Block sections.md +++ /dev/null @@ -1,158 +0,0 @@ -\page blockSections Block sections - -A block section is a group of Scratch blocks, for example motion blocks. - -Block sections are created using the \link libscratchcpp::IBlockSection IBlockSection \endlink interface. - -## Creating a block section -To create a custom block section, subclass \link libscratchcpp::IBlockSection IBlockSection \endlink and override -\link libscratchcpp::IBlockSection::name() name() \endlink to return the block section name. - -It's recommended to use the libscratchcpp namespace like this in your block section class: -```cpp -using namespace libscratchcpp; -``` - -### Adding blocks -See the [Scratch Wiki](https://en.scratch-wiki.info/wiki/Scratch_File_Format#Blocks) for more information about blocks. - -Scratch projects are compiled by the \link libscratchcpp::Compiler Compiler \endlink class. -To add a block, you have to let the compiler know how to compile it. -Start by defining a **compile function**. -For example, the compile function for a `hello world` block would look like this: -```cpp -class MySection : public IBlockSection { - public: - ... - static void compileHelloWorld(Compiler *compiler); - static unsigned int helloWorld(VirtualMachine *vm); -}; - -void MySection::compileHelloWorld(Compiler *compiler) { - compiler->addFunctionCall(&helloWorld); -} - -unsigned int MySection::helloWorld(VirtualMachine *vm) { - std::cout << "Hello, world!" << std::endl; - return 0; -} -``` -\note Make sure the functions are **static**. - -Register the compile function using the \link libscratchcpp::IBlockSection::addCompileFunction() addCompileFunction() \endlink method in the constructor: -```cpp -MySection::MySection() { - addCompileFunction("mysection_helloworld", &MySection::compileHelloWorld); -} -``` -Where `mysection_helloworld` is the opcode of the `hello world` block. - -### Adding inputs -To add inputs, create an `Inputs` enumeration in your block section: -```hpp -class MySection : public IBlockSection { - enum Inputs { - TEXT - }; - ... -}; -``` -Then add inputs in the constructor: -```cpp -MySection::MySection() { - ... - addInput("TEXT", TEXT); -} -``` -The compiler will assign the input name with the `TEXT` ID. In this case, the ID is 0 because it's the first member of the enumeration. - -To add the input to the compiled code, call the \link libscratchcpp::Compiler::addInput() addInput() \endlink function: -```cpp -void MySection::compileHelloWorld(Compiler *compiler) { - compiler->addInput(TEXT); - compiler->addFunctionCall(&helloWorld); -} -``` - -The value of the input can be read during runtime using the \link libscratchcpp::VirtualMachine::getInput() getInput() \endlink function: -```cpp -unsigned int MySection::helloWorld(VirtualMachine *vm) { - std::cout << "Hello, " << vm->getInput(0, 1)->toString() << "!" << std::endl; - return 1; -} -``` -\note The order of the inputs is the same as in the compile function. Do not use the `Inputs` enumeration in runtime functions. - -```cpp -vm->getInput(0, 1) -``` -The first argument is the index of the input and the second argument is the amount of inputs. -\note Make sure to return the amount of inputs in the `helloWorld` function. - -### Adding fields -**Fields** are drop-down menus into which one cannot drop a reporter. -Fields have a predefined set of values. -```cpp -class MySection : public IBlockSection { - ... - enum Fields { - ANIMAL - }; - - enum FieldValues { - Cat, - Dog - }; - ... -}; - -MySection::MySection() { - ... - addField("ANIMAL", ANIMAL); -} -``` - -Because fields are handled at compile time, you can read them from the compile function: -```cpp -void MySection::compileHelloWorld(Compiler *compiler) { - int id = compiler->field(ANIMAL)->specialValueId(); - - switch(id) { - case Cat: - compiler->addFunctionCall(&helloCat); - break; - - case Dog: - compiler->addFunctionCall(&helloDog); - break; - - default: - break; - } -} - -unsigned int MySection::helloCat(VirtualMachine *vm) { - std::cout << "Hello, cat!" << std::endl; - return 0; -} - -unsigned int MySection::helloDog(VirtualMachine *vm) { - std::cout << "Hello, dog!" << std::endl; - return 0; -} -``` -\note Don't confuse \link libscratchcpp::Field::specialValueId() specialValueId() \endlink with \link libscratchcpp::Field::valueId() valueId() \endlink -because \link libscratchcpp::Field::valueId() valueId() \endlink stores the ID of the block, variable, list or broadcast selected in the dropdown list. - -To get a pointer to the block, variable, list or broadcast selected in the dropdown list, use \link libscratchcpp::Field::valuePtr() valuePtr() \endlink. - -### Registering the block section -Block sections are registered in the \link libscratchcpp::IExtension::registerSections() registerSections() \endlink -function of an extension: - -```cpp -void MyExtension::registerSections(Engine *engine) { - engine->registerSection(std::make_shared()); -} -``` -See the [Extensions](extensions.html) page for instructions on how to create an extension. diff --git a/docs/Extensions.md b/docs/Extensions.md index 2e076d08..5e0d3836 100644 --- a/docs/Extensions.md +++ b/docs/Extensions.md @@ -1,30 +1,152 @@ \page extensions Extensions -An extension is a group of block sections (see [Block sections](blockSections.html)). +An extension is a group of Scratch blocks, for example motion blocks. ## Creating an extension To create a custom extension, subclass \link libscratchcpp::IExtension IExtension \endlink and override \link libscratchcpp::IExtension::name() name() \endlink and \link libscratchcpp::IExtension::description() description() \endlink -functions to return the name and the description of the extension. - -There's also an \link libscratchcpp::IExtension::includeByDefault() includeByDefault() \endlink function which returns `true` -if the extension is intended to be hidden from the block palette and included in a Scratch project by default. - -The default implementation returns `false`. +functions to return the name and the description of the extension. Then override \link libscratchcpp::IExtension::registerBlocks() registerBlocks() \endlink +where you'll register the compile functions for blocks. It's recommended to use the libscratchcpp namespace like this in your extension class: ```cpp using namespace libscratchcpp; ``` -### Registering block sections -Block sections can be registered by overriding the \link libscratchcpp::IExtension::registerSections() registerSections() \endlink function: +### Adding blocks +See the [Scratch Wiki](https://en.scratch-wiki.info/wiki/Scratch_File_Format#Blocks) for more information about blocks. + +Scratch projects are compiled by the \link libscratchcpp::Compiler Compiler \endlink class. +To add a block, you have to let the compiler know how to compile it. +Start by defining a **compile function**. +For example, the compile function for a `hello world` block would look like this: +```cpp +class MyExtension : public IExtension { + public: + ... + static void compileHelloWorld(Compiler *compiler); + static unsigned int helloWorld(VirtualMachine *vm); +}; + +void MyExtension::compileHelloWorld(Compiler *compiler) { + compiler->addFunctionCall(&helloWorld); +} + +unsigned int MyExtension::helloWorld(VirtualMachine *vm) { + std::cout << "Hello, world!" << std::endl; + return 0; +} +``` +\note Make sure the functions are **static**. + +Register the compile function using the \link libscratchcpp::IEngine::addCompileFunction() addCompileFunction() \endlink method in link libscratchcpp::IExtension::registerBlocks() registerBlocks() \endlink: +```cpp +MyExtension::registerBlocks(IEngine *engine) { + engine->addCompileFunction(this, "myextension_helloworld", &MyExtension::compileHelloWorld); +} +``` +Where `myextension_helloworld` is the opcode of the `hello world` block. + +### Adding inputs +To add inputs, create an `Inputs` enumeration in your extension: +```hpp +class MyExtension : public IExtension { + enum Inputs { + TEXT + }; + ... +}; +``` +Then add inputs in link libscratchcpp::IExtension::registerBlocks() registerBlocks() \endlink: +```cpp +MyExtension::registerBlocks(IEngine *engine) { + ... + engine->addInput(this, "TEXT", TEXT); +} +``` +The compiler will assign the input name with the `TEXT` ID. In this case, the ID is 0 because it's the first member of the enumeration. + +To add the input to the compiled code, call the \link libscratchcpp::Compiler::addInput() addInput() \endlink function: +```cpp +void MyExtension::compileHelloWorld(Compiler *compiler) { + compiler->addInput(TEXT); + compiler->addFunctionCall(&helloWorld); +} +``` + +The value of the input can be read during runtime using the \link libscratchcpp::VirtualMachine::getInput() getInput() \endlink function: +```cpp +unsigned int MyExtension::helloWorld(VirtualMachine *vm) { + std::cout << "Hello, " << vm->getInput(0, 1)->toString() << "!" << std::endl; + return 1; +} +``` +\note The order of the inputs is the same as in the compile function. Do not use the `Inputs` enumeration in runtime functions. + +```cpp +vm->getInput(0, 1) +``` +The first argument is the index of the input and the second argument is the amount of inputs. +\note Make sure to return the amount of inputs in the `helloWorld` function. + +### Adding fields +**Fields** are drop-down menus into which one cannot drop a reporter. +Fields have a predefined set of values. +```cpp +class MyExtension : public IExtension { + ... + enum Fields { + ANIMAL + }; + + enum FieldValues { + Cat, + Dog + }; + ... +}; + +MyExtension::registerBlocks(IEngine *engine) { + ... + engine->addField(this, "ANIMAL", ANIMAL); + engine->addFieldValue(this, "Cat", Cat); + engine->addFieldValue(this, "Dog", Dog); +} +``` + +Because fields are handled at compile time, you can read them from the compile function: ```cpp -void MyExtension::registerSections(IEngine *engine) { - engine->registerSection(std::make_shared()); +void MyExtension::compileHelloWorld(Compiler *compiler) { + int id = compiler->field(ANIMAL)->specialValueId(); + + switch(id) { + case Cat: + compiler->addFunctionCall(&helloCat); + break; + + case Dog: + compiler->addFunctionCall(&helloDog); + break; + + default: + break; + } +} + +unsigned int MyExtension::helloCat(VirtualMachine *vm) { + std::cout << "Hello, cat!" << std::endl; + return 0; +} + +unsigned int MyExtension::helloDog(VirtualMachine *vm) { + std::cout << "Hello, dog!" << std::endl; + return 0; } ``` -See the [Block sections](blockSections.html) page for instructions on how to create a block section. +\note Don't confuse \link libscratchcpp::Field::specialValueId() specialValueId() \endlink with \link libscratchcpp::Field::valueId() valueId() \endlink +because \link libscratchcpp::Field::valueId() valueId() \endlink stores the ID of the block, variable, list or broadcast selected in the dropdown list. + +To get a pointer to the block, variable, list or broadcast selected in the dropdown list, use \link libscratchcpp::Field::valuePtr() valuePtr() \endlink. ### Registering the extension Register the extension **before** loading a project, using the \link libscratchcpp::ScratchConfiguration ScratchConfiguration \endlink class: diff --git a/docs/Getting started.md b/docs/Getting started.md index 859fd4f0..b9b5dfaf 100644 --- a/docs/Getting started.md +++ b/docs/Getting started.md @@ -24,5 +24,5 @@ int main(int argc, char **argv) { The \link libscratchcpp::Project::run() run() \endlink method runs an event loop which stops after all scripts finish. For CLI project players, using \link libscratchcpp::Project::run() run() \endlink is enough. If you are developing -a GUI project player and need to receive input events such as key presses, you'll need to use \link libscratchcpp::Project::runEventLoop() runEventLoop() \endlink -and run it in another thread (to keep the UI responsive). +a GUI project player and need to receive input events such as key presses, you'll need to use \link libscratchcpp::IEngine::step() step() \endlink +for ticks and \link libscratchcpp::Project::start() start() \endlink and \link libscratchcpp::Project::stop() stop() \endlink diff --git a/docs/Image formats.md b/docs/Image formats.md deleted file mode 100644 index f1173bd1..00000000 --- a/docs/Image formats.md +++ /dev/null @@ -1,33 +0,0 @@ -\page imageFormats Image formats - -To work with costumes, libscratchcpp has to read image files from Scratch projects. -Only JPEG and PNG are currently supported, but SVG is going to be supported in the future. - -# Implementing a custom image format -To implement a custom image format that libscratchcpp doesn't support, -subclass \link libscratchcpp::IImageFormat IImageFormat \endlink -and override all of the methods. - -Then subclass \link libscratchcpp::IImageFormatFactory IImageFormatFactory \endlink -and override \link libscratchcpp::IImageFormatFactory::createInstance() createInstance() \endlink. - -It's recommended to use the libscratchcpp namespace like this in your classes: -```cpp -using namespace libscratchcpp; -``` - -Here's an example of the `createInstance()` method: -```cpp -std::shared_ptr TiffImageFormatFactory::createInstance() { - return std::make_shared(); -} -``` - -# Registering the image format -To register the image format, you need to register the factory class. -It will be used by classes such as \link libscratchcpp::Costume Costume \endlink -internally to instantiate your image format. - -```cpp -libscratchcpp::ScratchConfiguration::registerImageFormat("tiff", std::make_shared()); -``` diff --git a/include/scratchcpp/drawable.h b/include/scratchcpp/drawable.h new file mode 100644 index 00000000..8424bf59 --- /dev/null +++ b/include/scratchcpp/drawable.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "global.h" +#include "spimpl.h" + +namespace libscratchcpp +{ + +class IEngine; + +class DrawablePrivate; + +/*! \brief The Drawable class is the base class of rendered elements (stage, sprites, text bubbles). */ +class LIBSCRATCHCPP_EXPORT Drawable +{ + public: + Drawable(); + Drawable(const Drawable &) = delete; + + /*! Returns true if this Drawable is a Target. */ + virtual bool isTarget() const { return false; } + + /*! Returns true if this Drawable is a TextBubble. */ + virtual bool isTextBubble() const { return false; } + + int layerOrder() const; + virtual void setLayerOrder(int newLayerOrder); + + IEngine *engine() const; + virtual void setEngine(IEngine *engine); + + private: + spimpl::unique_impl_ptr impl; +}; + +} // namespace libscratchcpp diff --git a/include/scratchcpp/iblocksection.h b/include/scratchcpp/iblocksection.h deleted file mode 100644 index 92b71cfb..00000000 --- a/include/scratchcpp/iblocksection.h +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include "spimpl.h" - -#include "global.h" - -namespace libscratchcpp -{ - -class BlockSectionPrivate; -class IEngine; - -/*! - * \brief The IBlockSection class is an interface for block sections/categories. - * - * \see Block sections - */ -class LIBSCRATCHCPP_EXPORT IBlockSection -{ - public: - virtual ~IBlockSection() { } - - /*! Override this method to return the name of the block section. */ - virtual std::string name() const = 0; - - /*! - * Override this method to configure whether the category in the block palette should be visible.\n - * For example, there isn't a list category in the Scratch user interface. - * \note The default implementation returns true. - */ - virtual bool categoryVisible() const { return true; } - - /*! Override this method to register blocks. */ - virtual void registerBlocks(IEngine *engine) = 0; - - /*! This method is called when a project is loaded. */ - virtual void onInit(IEngine *engine) { } -}; - -} // namespace libscratchcpp diff --git a/include/scratchcpp/iengine.h b/include/scratchcpp/iengine.h index aa5aac94..a8cffbc2 100644 --- a/include/scratchcpp/iengine.h +++ b/include/scratchcpp/iengine.h @@ -14,15 +14,18 @@ namespace libscratchcpp { -class IBlockSection; +class IExtension; class Broadcast; class Block; +class Field; +class Drawable; class Target; class Sprite; class Stage; class Variable; class List; class Script; +class Thread; class ITimer; class KeyEvent; class Monitor; @@ -58,26 +61,26 @@ class LIBSCRATCHCPP_EXPORT IEngine virtual void stop() = 0; /*! Starts a script with the given top level block as the given Target (a sprite or the stage). */ - virtual VirtualMachine *startScript(std::shared_ptr topLevelBlock, Target *) = 0; + virtual Thread *startScript(std::shared_ptr topLevelBlock, Target *) = 0; /*! Starts the scripts of the broadcast with the given index. */ - virtual void broadcast(int index) = 0; + virtual void broadcast(int index, Thread *sender, bool wait) = 0; /*! Starts the scripts of the given broadcast. */ - virtual void broadcastByPtr(Broadcast *broadcast) = 0; + virtual void broadcastByPtr(Broadcast *broadcast, Thread *sender, bool wait) = 0; /*! Starts the "when backdrop switches to" scripts for the given backdrop broadcast. */ - virtual void startBackdropScripts(Broadcast *broadcast) = 0; + virtual void startBackdropScripts(Broadcast *broadcast, Thread *sender, bool wait) = 0; /*! Stops the given script. */ - virtual void stopScript(VirtualMachine *vm) = 0; + virtual void stopScript(Thread *vm) = 0; /*! * Stops all scripts in the given target. * \param[in] target The Target to stop. * \param[in] exceptScript Sets this parameter to stop all scripts except the given script. */ - virtual void stopTarget(Target *target, VirtualMachine *exceptScript) = 0; + virtual void stopTarget(Target *target, Thread *exceptScript) = 0; /*! Calls the "when I start as a clone" blocks of the given sprite. */ virtual void initClone(std::shared_ptr clone) = 0; @@ -121,7 +124,7 @@ class LIBSCRATCHCPP_EXPORT IEngine virtual sigslot::signal<> &aboutToRender() = 0; /*! Emits when a script is about to stop. */ - virtual sigslot::signal &threadAboutToStop() = 0; + virtual sigslot::signal &threadAboutToStop() = 0; /*! Emits when the project is stopped by calling stop(). */ virtual sigslot::signal<> &stopped() = 0; @@ -207,12 +210,6 @@ class LIBSCRATCHCPP_EXPORT IEngine /*! Toggles sprite fencing. */ virtual void setSpriteFencingEnabled(bool enable) = 0; - /*! Returns true if there are any running script of the broadcast with the given index. */ - virtual bool broadcastRunning(unsigned int index) = 0; - - /*! Returns true if there are any running script of the given broadcast. */ - virtual bool broadcastByPtrRunning(Broadcast *broadcast) = 0; - /*! * Call this from a block implementation to force a redraw (screen refresh). * \note This has no effect in "run without screen refresh" custom blocks. @@ -222,12 +219,6 @@ class LIBSCRATCHCPP_EXPORT IEngine /*! Returns the timer of the project. */ virtual ITimer *timer() const = 0; - /*! - * Registers the given block section. - * \see Block sections - */ - virtual void registerSection(std::shared_ptr section) = 0; - /*! Returns the index of the given block function. */ virtual unsigned int functionIndex(BlockFunc f) = 0; @@ -235,53 +226,53 @@ class LIBSCRATCHCPP_EXPORT IEngine virtual const std::vector &blockFunctions() const = 0; /*! - * Call this from IBlockSection#registerBlocks() to add a compile function to a block section. - * \see Block sections + * Call this from IExtension#registerBlocks() to add a compile function to a block section. + * \see Extensions */ - virtual void addCompileFunction(IBlockSection *section, const std::string &opcode, BlockComp f) = 0; + virtual void addCompileFunction(IExtension *extension, const std::string &opcode, BlockComp f) = 0; /*! - * Call this from IBlockSection#registerBlocks() to add a hat block predicate compile function to a block section. + * Call this from IExtension#registerBlocks() to add a hat block predicate compile function to a block section. * \note This only works with edge-activated hats. - * \see Block sections + * \see Extensions */ - virtual void addHatPredicateCompileFunction(IBlockSection *section, const std::string &opcode, HatPredicateCompileFunc f) = 0; + virtual void addHatPredicateCompileFunction(IExtension *extension, const std::string &opcode, HatPredicateCompileFunc f) = 0; /*! - * Call this from IBlockSection#registerBlocks() to add a monitor name function to a block section. - * \see Block sections + * Call this from IExtension#registerBlocks() to add a monitor name function to a block section. + * \see Extensions */ - virtual void addMonitorNameFunction(IBlockSection *section, const std::string &opcode, MonitorNameFunc f) = 0; + virtual void addMonitorNameFunction(IExtension *extension, const std::string &opcode, MonitorNameFunc f) = 0; /*! - * Call this from IBlockSection#registerBlocks() to add a monitor value change function to a block section. - * \see Block sections + * Call this from IExtension#registerBlocks() to add a monitor value change function to a block section. + * \see Extensions */ - virtual void addMonitorChangeFunction(IBlockSection *section, const std::string &opcode, MonitorChangeFunc f) = 0; + virtual void addMonitorChangeFunction(IExtension *extension, const std::string &opcode, MonitorChangeFunc f) = 0; /*! - * Call this from IBlockSection#registerBlocks() to add a hat block to a block section. - * \see Block sections + * Call this from IExtension#registerBlocks() to add a hat block to a block section. + * \see Extensions */ - virtual void addHatBlock(IBlockSection *section, const std::string &opcode) = 0; + virtual void addHatBlock(IExtension *extension, const std::string &opcode) = 0; /*! - * Call this from IBlockSection#registerBlocks() to add an input to a block section. - * \see Block sections + * Call this from IExtension#registerBlocks() to add an input to a block section. + * \see Extensions */ - virtual void addInput(IBlockSection *section, const std::string &name, int id) = 0; + virtual void addInput(IExtension *extension, const std::string &name, int id) = 0; /*! - * Call this from IBlockSection#registerBlocks() to add a field to a block section. - * \see Block sections + * Call this from IExtension#registerBlocks() to add a field to a block section. + * \see Extensions */ - virtual void addField(IBlockSection *section, const std::string &name, int id) = 0; + virtual void addField(IExtension *extension, const std::string &name, int id) = 0; /*! - * Call this from IBlockSection#registerBlocks() to add a field value to a block section. - * \see Block sections + * Call this from IExtension#registerBlocks() to add a field value to a block section. + * \see Extensions */ - virtual void addFieldValue(IBlockSection *section, const std::string &value, int id) = 0; + virtual void addFieldValue(IExtension *extension, const std::string &value, int id) = 0; /*! Returns the list of broadcasts. */ virtual const std::vector> &broadcasts() const = 0; @@ -305,16 +296,16 @@ class LIBSCRATCHCPP_EXPORT IEngine virtual void addGreenFlagScript(std::shared_ptr hatBlock) = 0; /*! Registers the broadcast script. */ - virtual void addBroadcastScript(std::shared_ptr whenReceivedBlock, int fieldId, Broadcast *broadcast) = 0; + virtual void addBroadcastScript(std::shared_ptr whenReceivedBlock, Field *field, Broadcast *broadcast) = 0; /*! Registers the backdrop change script. */ - virtual void addBackdropChangeScript(std::shared_ptr hatBlock, int fieldId) = 0; + virtual void addBackdropChangeScript(std::shared_ptr hatBlock, Field *field) = 0; /* Registers the given "when I start as clone" script. */ virtual void addCloneInitScript(std::shared_ptr hatBlock) = 0; /* Registers the given "when key pressed" script. */ - virtual void addKeyPressScript(std::shared_ptr hatBlock, int fieldId) = 0; + virtual void addKeyPressScript(std::shared_ptr hatBlock, Field *field) = 0; /* Registers the given "when this sprite/stage clicked" script. */ virtual void addTargetClickScript(std::shared_ptr hatBlock) = 0; @@ -331,6 +322,9 @@ class LIBSCRATCHCPP_EXPORT IEngine /*! Returns the target at index. */ virtual Target *targetAt(int index) const = 0; + /*! Gets visible targets sorted by layer order and stores them in the given vector. */ + virtual void getVisibleTargets(std::vector &dst) const = 0; + /*! * Returns the index of the target with the given name. * \note Using "Stage" will return the index of the sprite with this name, or nullptr if it doesn't exist. @@ -339,19 +333,19 @@ class LIBSCRATCHCPP_EXPORT IEngine virtual int findTarget(const std::string &targetName) const = 0; /*! Moves the given sprite to the front layer. */ - virtual void moveSpriteToFront(Sprite *sprite) = 0; + virtual void moveDrawableToFront(Drawable *drawable) = 0; /*! Moves the given sprite to the back layer. */ - virtual void moveSpriteToBack(Sprite *sprite) = 0; + virtual void moveDrawableToBack(Drawable *drawable) = 0; /*! Moves the given sprite forward a number of layers. */ - virtual void moveSpriteForwardLayers(Sprite *sprite, int layers) = 0; + virtual void moveDrawableForwardLayers(Drawable *drawable, int layers) = 0; /*! Moves the given sprite backward a number of layers. */ - virtual void moveSpriteBackwardLayers(Sprite *sprite, int layers) = 0; + virtual void moveDrawableBackwardLayers(Drawable *drawable, int layers) = 0; /*! Moves the given sprite behind some other sprite. */ - virtual void moveSpriteBehindOther(Sprite *sprite, Sprite *other) = 0; + virtual void moveDrawableBehindOther(Drawable *drawable, Drawable *other) = 0; /*! Returns the Stage. */ virtual Stage *stage() const = 0; @@ -362,6 +356,12 @@ class LIBSCRATCHCPP_EXPORT IEngine /*! Sets the list of monitors. */ virtual void setMonitors(const std::vector> &newMonitors) = 0; + /*! Creates a monitor for the given variable if it's missing and returns it. */ + virtual Monitor *createVariableMonitor(std::shared_ptr var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction) = 0; + + /*! Creates a monitor for the given list if it's missing and returns it. */ + virtual Monitor *createListMonitor(std::shared_ptr list, const std::string &opcode, const std::string &listFieldName, int listFieldId, BlockComp compileFunction) = 0; + /*! Emits when a monitor is added. */ virtual sigslot::signal &monitorAdded() = 0; diff --git a/include/scratchcpp/iextension.h b/include/scratchcpp/iextension.h index ccd65ec6..a53b8356 100644 --- a/include/scratchcpp/iextension.h +++ b/include/scratchcpp/iextension.h @@ -27,14 +27,11 @@ class LIBSCRATCHCPP_EXPORT IExtension /*! Returns the description of the extension. */ virtual std::string description() const = 0; - /*! - * Returns true if the extension is hidden from the block palette - * and should be available in a Scratch project by default - */ - virtual bool includeByDefault() const { return false; } - - /*! Registers block sections. \see Block sections */ - virtual void registerSections(IEngine *engine) = 0; + /*! Override this method to register blocks. */ + virtual void registerBlocks(IEngine *engine) = 0; + + /*! This method is called when a project is loaded. */ + virtual void onInit(IEngine *engine) { } }; } // namespace libscratchcpp diff --git a/include/scratchcpp/igraphicseffect.h b/include/scratchcpp/igraphicseffect.h index 7520cfc4..cc3a58eb 100644 --- a/include/scratchcpp/igraphicseffect.h +++ b/include/scratchcpp/igraphicseffect.h @@ -17,6 +17,9 @@ class LIBSCRATCHCPP_EXPORT IGraphicsEffect /*! Returns the name of the graphics effect. */ virtual std::string name() const = 0; + + /*! Returns the clamped value of the graphic effect. */ + virtual double clamp(double value) const = 0; }; } // namespace libscratchcpp diff --git a/include/scratchcpp/imonitorhandler.h b/include/scratchcpp/imonitorhandler.h index ac5180ce..d31582ba 100644 --- a/include/scratchcpp/imonitorhandler.h +++ b/include/scratchcpp/imonitorhandler.h @@ -18,6 +18,8 @@ class LIBSCRATCHCPP_EXPORT IMonitorHandler virtual void init(Monitor *monitor) = 0; virtual void onValueChanged(const VirtualMachine *vm) = 0; + virtual void onXChanged(int x) = 0; + virtual void onYChanged(int y) = 0; virtual void onVisibleChanged(bool visible) = 0; }; diff --git a/include/scratchcpp/ispritehandler.h b/include/scratchcpp/ispritehandler.h index 9bfb4307..d8a61a68 100644 --- a/include/scratchcpp/ispritehandler.h +++ b/include/scratchcpp/ispritehandler.h @@ -59,12 +59,6 @@ class LIBSCRATCHCPP_EXPORT ISpriteHandler /*! Called when all graphics effects are cleared. */ virtual void onGraphicsEffectsCleared() = 0; - /*! Called when the bubble type changes. */ - virtual void onBubbleTypeChanged(Target::BubbleType type) = 0; - - /*! Called when the bubble text changes. */ - virtual void onBubbleTextChanged(const std::string &text) = 0; - /*! Used to get the current costume width. */ virtual int costumeWidth() const = 0; @@ -92,6 +86,9 @@ class LIBSCRATCHCPP_EXPORT ISpriteHandler /*! Used to check whether the sprite touches the given color. */ virtual bool touchingColor(const Value &color) const = 0; + + /*! Used to check whether the mask part of the sprite touches the given color. */ + virtual bool touchingColor(const Value &color, const Value &mask) const = 0; }; } // namespace libscratchcpp diff --git a/include/scratchcpp/istagehandler.h b/include/scratchcpp/istagehandler.h index 0b397e17..b6cf7f50 100644 --- a/include/scratchcpp/istagehandler.h +++ b/include/scratchcpp/istagehandler.h @@ -40,12 +40,6 @@ class LIBSCRATCHCPP_EXPORT IStageHandler /*! Called when all graphics effects are cleared. */ virtual void onGraphicsEffectsCleared() = 0; - /*! Called when the bubble type changes. */ - virtual void onBubbleTypeChanged(Target::BubbleType type) = 0; - - /*! Called when the bubble text changes. */ - virtual void onBubbleTextChanged(const std::string &text) = 0; - /*! Used to get the current costume width. */ virtual int costumeWidth() const = 0; @@ -69,6 +63,9 @@ class LIBSCRATCHCPP_EXPORT IStageHandler /*! Used to check whether the stage touches the given color. */ virtual bool touchingColor(const Value &color) const = 0; + + /*! Used to check whether the mask part of the stage touches the given color. */ + virtual bool touchingColor(const Value &color, const Value &mask) const = 0; }; } // namespace libscratchcpp diff --git a/include/scratchcpp/list.h b/include/scratchcpp/list.h index 6d63e8b2..8ce30c85 100644 --- a/include/scratchcpp/list.h +++ b/include/scratchcpp/list.h @@ -3,7 +3,7 @@ #pragma once #include -#include +#include #include "value.h" #include "entity.h" @@ -15,15 +15,19 @@ class Target; class Monitor; class ListPrivate; -/*! \brief The List class represents a Scratch list. */ -class LIBSCRATCHCPP_EXPORT List - : public std::deque - , public Entity +/*! + * \brief The List class represents a Scratch list. + * + * Due to the high optimization of the methods, indices out of range will result in undefined behavior! + */ +class LIBSCRATCHCPP_EXPORT List : public Entity { public: List(const std::string &id, const std::string &name); List(const List &) = delete; + ~List(); + const std::string &name(); void setName(const std::string &name); @@ -33,24 +37,197 @@ class LIBSCRATCHCPP_EXPORT List Monitor *monitor() const; void setMonitor(Monitor *monitor); - long indexOf(const Value &value) const; - bool contains(const Value &value) const; + /*! Returns the list size. */ + inline size_t size() const { return m_size; } + + /*! Returns true if the list is empty. */ + inline bool empty() const { return m_size == 0; } + + /*! Returns the index of the given item. */ + inline size_t indexOf(const ValueData &value) const + { + for (size_t i = 0; i < m_size; i++) { + if (value_equals(&m_dataPtr->operator[](i), &value)) + return i; + } + + return -1; + } + + /*! Returns the index of the given item. */ + inline size_t indexOf(const Value &value) const { return indexOf(value.data()); } + + /*! Returns true if the list contains the given item. */ + inline bool contains(const ValueData &value) const { return (indexOf(value) != -1); } + + /*! Returns true if the list contains the given item. */ + inline bool contains(const Value &value) const { return contains(value.data()); } + + /*! Clears the list. */ + inline void clear() + { + // Keep at least 200,000 items allocated if the list has more + constexpr size_t limit = 200000; + m_size = 0; + + if (m_dataPtr->size() > limit) + reserve(limit); + } + + /*! Appends an empty item and returns the reference to it. Can be used for custom initialization. */ + inline ValueData &appendEmpty() + { + m_size++; + reserve(getAllocSize(m_size)); + return m_dataPtr->operator[](m_size - 1); + } + + /*! Appends an item. */ + inline void append(const ValueData &value) { value_assign_copy(&appendEmpty(), &value); } + + /*! Appends an item. */ + inline void append(const Value &value) { append(value.data()); } /*! Removes the item at index. */ - void removeAt(int index) { erase(begin() + index); } + inline void removeAt(size_t index) + { + assert(index >= 0 && index < size()); + std::rotate(m_dataPtr->begin() + index, m_dataPtr->begin() + index + 1, m_dataPtr->begin() + m_size); + m_size--; + } + + /*! Inserts an item at index. */ + inline void insert(size_t index, const ValueData &value) + { + assert(index >= 0 && index <= size()); + m_size++; + reserve(getAllocSize(m_size)); + std::rotate(m_dataPtr->rbegin() + m_dataPtr->size() - m_size, m_dataPtr->rbegin() + m_dataPtr->size() - m_size + 1, m_dataPtr->rend() - index); + value_assign_copy(&m_dataPtr->operator[](index), &value); + } /*! Inserts an item at index. */ - void insert(int index, const Value &value) { std::deque::insert(begin() + index, value); } + inline void insert(size_t index, const Value &value) { insert(index, value.data()); } /*! Replaces the item at index. */ - void replace(int index, const Value &value) { at(index) = value; } + inline void replace(size_t index, const ValueData &value) + { + assert(index >= 0 && index < size()); + value_assign_copy(&m_dataPtr->operator[](index), &value); + } - std::string toString() const; + /*! Replaces the item at index. */ + inline void replace(size_t index, const Value &value) { replace(index, value.data()); } + + inline ValueData &operator[](size_t index) + { + assert(index >= 0 && index < size()); + return m_dataPtr->operator[](index); + } + + /*! Joins the list items with spaces or without any separator if there are only digits and stores the result in dst. */ + inline void toString(std::string &dst) const + { + dst.clear(); + veque::veque strings; + strings.reserve(m_size); + bool digits = true; + size_t i; + + for (i = 0; i < m_size; i++) { + const ValueData *item = &m_dataPtr->operator[](i); + strings.push_back(std::string()); + value_toString(item, &strings.back()); + + if (value_isValidNumber(item) && !strings.back().empty()) { + double doubleNum = value_toDouble(item); + long num = value_toLong(item); + + if (doubleNum != num) { + digits = false; + break; + } + + if (num < 0 || num >= 10) { + digits = false; + break; + } + } else { + digits = false; + break; + } + } + + std::string s; + + if (digits) { + for (i = 0; i < strings.size(); i++) + dst.append(strings[i]); + + for (; i < m_size; i++) { + value_toString(&m_dataPtr->operator[](i), &s); + dst.append(s); + } + } else { + for (i = 0; i < strings.size(); i++) { + dst.append(strings[i]); + + if (i + 1 < m_size) + dst.push_back(' '); + } + + for (; i < m_size; i++) { + value_toString(&m_dataPtr->operator[](i), &s); + dst.append(s); + + if (i + 1 < m_size) + dst.push_back(' '); + } + } + } + + /*! Same as the other method, but returns the result directly. */ + inline std::string toString() const + { + std::string ret; + toString(ret); + return ret; + } std::shared_ptr clone(); private: + inline void reserve(size_t size) + { + assert(size >= m_size); + + while (size > m_dataPtr->size()) { + m_dataPtr->push_back(ValueData()); + value_init(&m_dataPtr->back()); + } + + while (size < m_dataPtr->size()) { + value_free(&m_dataPtr->back()); + m_dataPtr->erase(m_dataPtr->end()); + } + } + + inline size_t getAllocSize(size_t x) + { + if (x == 0) + return 0; + + size_t ret = 1; + + while (ret < x) + ret *= 2; + + return ret; + } + spimpl::unique_impl_ptr impl; + veque::veque *m_dataPtr = nullptr; // NOTE: accessing through pointer is faster! (from benchmarks) + size_t m_size = 0; }; } // namespace libscratchcpp diff --git a/include/scratchcpp/monitor.h b/include/scratchcpp/monitor.h index 6fd84a41..85f5c8e4 100644 --- a/include/scratchcpp/monitor.h +++ b/include/scratchcpp/monitor.h @@ -12,7 +12,7 @@ namespace libscratchcpp class IMonitorHandler; class Block; class Script; -class IBlockSection; +class IExtension; class Sprite; class Value; class Rect; @@ -48,8 +48,8 @@ class LIBSCRATCHCPP_EXPORT Monitor : public Entity std::shared_ptr