diff --git a/CMakeLists.txt b/CMakeLists.txt index deeb6780..0f9fcc13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,6 @@ 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 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/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 48498790..f4938a3c 100644 --- a/include/scratchcpp/iengine.h +++ b/include/scratchcpp/iengine.h @@ -14,7 +14,7 @@ namespace libscratchcpp { -class IBlockSection; +class IExtension; class Broadcast; class Block; class Target; @@ -217,12 +217,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; @@ -230,53 +224,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; @@ -381,11 +375,13 @@ class LIBSCRATCHCPP_EXPORT IEngine /*! Emits when a question is answered. */ virtual sigslot::signal &questionAnswered() = 0; +#ifndef USE_LLVM /*! Returns the list of extension names. */ virtual const std::vector &extensions() const = 0; /*! Sets the list of extension names. */ virtual void setExtensions(const std::vector &newExtensions) = 0; +#endif /*! Returns the map of scripts (each top level block has a Script object). */ virtual const std::unordered_map, std::shared_ptr