2
0
Эх сурвалжийг харах

More work on user manuals

BearishSun 9 жил өмнө
parent
commit
171cdeb45c

+ 4 - 0
Documentation/Doxygen/doxystyle.css

@@ -308,6 +308,10 @@ dl.note {
 	border-color: rgba(89, 160, 195, 0.86);
 }
 
+div.fragment {
+	margin: 4px 8px 14px 2px;
+}
+
 #menuSegment {
     padding: 0;
     border-bottom: 5px solid #ffb028;

+ 127 - 0
Documentation/Manuals/Native/User/events.md

@@ -0,0 +1,127 @@
+Events						{#events}
+===============
+
+Events allow objects to expose events that may trigger during execution. External objects interested in those events can then register callbacks with those events and be notified when they happen. They are useful because they allow two objects to communicate without necessarily knowing about each other's types, which can reduce class coupling and improve design.
+
+They're represented with the @ref bs::TEvent<RetType, Args> "Event" class.
+
+# Creating events
+When creating an event, all you need to do is specify a format of the callback it sends out, for example:
+~~~~~~~~~~~~~{.cpp}
+class MyPlayerController
+{
+public:
+	Event<void()> onPlayerJumped; // Event that triggers a callback with no parameters
+	Event<void(UINT32)> onPlayerCollectedCoins; // Event that triggers a callback with an UINT32 parameter
+};
+~~~~~~~~~~~~~
+
+The format of the callback method signature is the same format as followed by C++ *std::function*: *returnType(param1Type, param2Type, ...)*. 
+
+# Triggering events
+
+When an object is ready to trigger an event simply call it like a function:
+~~~~~~~~~~~~~{.cpp}
+class MyPlayerController
+{
+public:
+	// Assume this is a function called every frame
+	void update()
+	{
+		bool spacePressed = /*... check input system for button press ...*/;
+		UINT32 grabbedCoins = /*... use physics system to see if player is colliding with any coin objects ...*/;
+		
+		if(spacePressed)
+			onPlayerJumped(); // Trigger event
+			
+		if(grabbedCoins > 0)
+			onPlayerCollectedCoins(grabbedCoins); // Trigger event
+	}
+
+	Event<void()> onPlayerJumped;
+	Event<void(UINT32)> onPlayerCollectedCoins;
+};
+~~~~~~~~~~~~~
+
+# Subscribing to events
+
+An external object can register itself with an event by calling @ref bs::TEvent<RetType, Args> "Event::connect". 
+~~~~~~~~~~~~~{.cpp}
+// Define a couple of methods that trigger when events are triggered
+auto playerJumpedCallback = [&]()
+{
+	gDebug().logDebug("Player jumped!");
+};
+
+auto playerCollectedCoinsCallback = [&](UINT32 numCoins)
+{
+	gDebug().logDebug("Player collected: " + toString(numCoins) + " coins!");
+};
+
+MyPlayerController playerController;
+
+// Subscribe to events
+playerController.onPlayerJumped.connect(&playerJumpedCallback);
+playerController.onPlayerCollectedCoins.connect(&playerCollectedCoinsCallback);
+
+// ... run player logic every frame ...
+~~~~~~~~~~~~~
+
+Subscribing to an event will return an @ref bs::HEvent "HEvent" handle. You can use this handle to manually disconnect from the event by calling @ref bs::HEvent::disconnect "HEvent::disconnect". For example:
+
+~~~~~~~~~~~~~{.cpp}
+HEvent eventHandle = playerController.onPlayerJumped.connect(&playerJumpedCallback);
+
+// ... do something and decide event is no longer required ...
+
+eventHandle.disconnect();
+~~~~~~~~~~~~~
+
+## Class methods as event callbacks
+In the example above we used lambda functions as event callbacks, and it would have also worked for global functions, but if you wish to use class methods as event callbacks, some additional logic is required. 
+
+The main difference between lambda/global functions and class methods is that class methods have an implicit parameter, the *this* pointer. This is not something you see when you define a function, but it is always there "under the hood".
+
+For example:
+~~~~~~~~~~~~~{.cpp}
+class MyEventSubscriber
+{
+	void eventCallback(); // What you see
+	// void eventCallback(MyEventSubscriber* thisPtr); // What the compiler sees for the above method
+};
+~~~~~~~~~~~~~
+
+When setting up event callbacks we must therefore provide the *this* pointer manually. However since the event owner has no idea which class will subscribe to its event, it cannot provide the *this* pointer to the object of that class. By using **std::bind** the event subscriber can instead permanently bind its *this* pointer when subscribing to an event.
+
+If you are unfamiliar with *std::bind*, it basically transforms a function signature by permanently *binding* one of its parameters. Here's a simple example:
+~~~~~~~~~~~~~{.cpp}
+auto myFunc = [&](UINT32 param)
+{ };
+
+// Permanently binds 5 as the function parameter
+auto myFuncNoParams = std::bind(myFunc, 5);
+
+// Call the function with parameter 5
+myFuncNoParams();
+~~~~~~~~~~~~~
+
+Using this same logic, we can permanently bind the *this* pointer when subscribing to an event:
+~~~~~~~~~~~~~{.cpp}
+class MyEventSubscriber
+{
+	void playerJumpedCallback() // Has a hidden "this" pointer parameter
+	{
+		gDebug().logDebug("Player jumped!");
+	}
+	
+	// Assuming the same player controller class we defined earlier
+	void subscribeToEvents(MyPlayerController& playerController)
+	{
+		// Bind the "this" pointer
+		auto callback = std::bind(&MyEventSubscriber::playerJumpedCallback, this);
+	
+		// Register the callback without the event needing to know about "this" pointer parameter
+		playerController.onPlayerJumped.connect(callback);
+	}
+};
+~~~~~~~~~~~~~

+ 89 - 0
Documentation/Manuals/Native/User/fileSystem.md

@@ -0,0 +1,89 @@
+File system									{#fileSystem}
+===============
+
+# Paths
+Instead of using strings for representing paths, Banshee uses the @ref bs::Path "Path" class. Aside from containing the path it provides a variety of other useful information and allows for path manipulation. It is recommended to always store paths using **Path** instead of strings.
+
+~~~~~~~~~~~~~{.cpp}
+Path myPath = "C:/Path/To/File.txt";
+~~~~~~~~~~~~~
+
+Some of the things you can do with a **Path**:
+ - Retrieve the filename using @ref bs::Path::getFilename "Path::getFilename"
+ - Retrieve the filename extension using @ref bs::Path::getExtension "Path::getExtension"
+ - Get last element of path, either file or directory using @ref bs::Path::getTail "Path::getTail"
+ - Iterate over directories, get drive, combine paths, convert relative to absolute paths and vice versa, and more. See the API reference for a complete list.
+ 
+For example:
+~~~~~~~~~~~~~{.cpp}
+Path myPath = "C:/Path/To/File.txt";
+
+String filename = myPath.getFilename(); // Returns filename, if the path has any
+myPath.setExtension(".jpg"); // Path is now "C:/Path/To/File.jpg"
+myPath.makeRelative("C:/Path"); // Path is now "To/File.jpg"
+
+Path a("C:/Path/To/");
+Path b("File.txt");
+Path combined = a + b; // // Path is now "C:/Path/To/File.txt"
+~~~~~~~~~~~~~
+ 
+All **Path** methods that return strings come in two variants, one that returns a narrow (8-bit) character string like **Path::getFilename**, and one that contains wide character string like **Path::getWFilename**.
+
+When setting paths be careful with setting backslashes or slashes at the end of the path. Path with a no backslash/slash on the end will be interpreted as a file path, and path with a backslash/slash will be interpreted as a folder path. For example:
+ - "C:/MyFolder" - "MyFolder" interpreted as a file, **Path::getFilename** returns "MyFolder"
+ - "C:/MyFolder/" - "MyFolder" interpreted as a folder, **Path::getFilename** returns an empty string
+ 
+# File system
+File system operations like opening, creating, deleting, moving, copying files/folders are provided by the @ref bs::FileSystem "FileSystem" class. Check the API reference for a complete list of operations.
+
+An example creating a folder and a file:
+~~~~~~~~~~~~~{.cpp}
+FileSystem::createDir("C:/Path/To/");
+
+SPtr<DataStream> fileStream = FileSystem::createAndOpenFile("C:/Path/To/File.txt");
+// Write to data stream (see below)
+~~~~~~~~~~~~~
+
+# Data streams
+If you create or open a file you will receive a @ref bs::DataStream "DataStream" object. Data streams allow you to easily write to, or read from open files. 
+
+~~~~~~~~~~~~~{.cpp}
+SPtr<DataStream> fileStream = FileSystem::createAndOpenFile("C:/Path/To/File.txt");
+
+// Write some string data
+fileStream->writeString("Writing to a file");
+
+// Write some binary data
+UINT8* myBuffer = bs_alloc(1024);
+
+// ... fill up the buffer with some data ...
+
+fileStream->write(myBuffer, 1024);
+fileStream->close();
+
+bs_free(myBuffer);
+~~~~~~~~~~~~~
+
+Once you are done with a stream make sure to close it by calling @ref bs::DataStream::close "DataStream::close". Stream will also be automatically closed when it goes out of scope.
+
+Streams don't need to be read or written to sequentially, use @ref bs::DataStream::seek "DataStream::seek" to move within any position of the stream, and @ref bs::DataStream::tell "DataStream::tell" to find out the current position.
+
+~~~~~~~~~~~~~{.cpp}
+// Open the file we wrote in the previous example
+SPtr<DataStream> fileStream = FileSystem::openFile("C:/Path/To/File.txt");
+
+// Seek past the string we wrote
+String str = "Writing to a file";
+fileStream->seek(str.size());
+
+// Read the byte data
+UINT8* myBuffer = bs_alloc(1024);
+fileStream->read(myBuffer, 1024);
+
+fileStream->close();
+bs_free(myBuffer);
+~~~~~~~~~~~~~
+
+Each time you read or write from the stream, the current read/write indices will advance. So subsequent calls to read/write will continue from the last position that was read/written.
+
+Finally, use @ref bs::DataStream::size "DataStream::size" to find out the size of a stream in bytes.

+ 84 - 1
Documentation/Manuals/Native/User/inputEvents.md

@@ -1,4 +1,87 @@
 Input events						{#inputEvents}
 ===============
 
-Events represent another way of handling user input. They are an alternative to input polling, and it's up to the developer to choose which way of handling input he prefers. These approaches aren't identical though, and events can provide more information to the developer. Same as polling, events are also handled by the **Input** class.
+Events represent another way of handling user input. They are an alternative to input polling, and it's up to the developer to choose which way of handling input he prefers. These approaches aren't identical though, and events can provide more information than polling. Same as polling, events are also handled by the **Input** class.
+
+# Button presses
+You can subscribe to the following events that report when the user interacted with a button:
+ - @ref bs::Input::onButtonDown "Input::onButtonDown" - Triggered whenever a button has been pressed.
+ - @ref bs::Input::onButtonUp "Input::onButtonUp" - Triggered whenever a button has been released.
+ 
+Both of these events supply the @ref bs::ButtonEvent "ButtonEvent" structure, containing the code of the button that was pressed, along with some other information.
+
+~~~~~~~~~~~~~{.cpp}
+Vector3 position(BsZero);
+
+// Callback method that triggers when any button is pressed
+auto handleButtonDown = [&](const ButtonEvent& event)
+{
+	// If user presses space, "jump"
+	if (event.buttonCode == BC_SPACE)
+		position.y += 5.0f;
+};
+
+// Connect the callback to the event
+gInput().onButtonDown.connect(&handleButtonDown);
+~~~~~~~~~~~~~
+
+# Mouse/touch input
+Use @ref bs::Input::onPointerMoved "Input::onPointerMoved" to track whenever the user moves the mouse or his finger on a touch device. The event supplies the @ref bs::PointerEvent "PointerEvent" structure, containing information like screen position of the event, delta from the last frame, state of all the mouse buttons, scroll wheel movement and more.
+
+~~~~~~~~~~~~~{.cpp}
+Vector3 position(BsZero);
+
+// Callback method that triggers whenever the pointer moves
+auto handlePointerMove = [&](const PointerEvent& event)
+{
+	// Move the object in 2D plane together with the pointer, if left mouse button is pressed
+	if(event.buttonStates[(int)PointerEventButton::Left])
+	{
+		position.x = (float)event.screenPos.x;
+		position.y = (float)event.screenPos.y;
+	}
+	
+	// Change object depth depending on mouse scroll wheel
+	position.z += event.mouseWheelScrollAmount;
+};
+
+// Connect the callback to the event
+gInput().onPointerMoved.connect(&handlePointerMove);
+~~~~~~~~~~~~~
+
+Pointers may also receive specialized button down/up events, similar to *Input::onButtonDown* and *Input::onButtonUp*. They trigger at the same time, but provide *PointerEvent* structure instead of *ButtonEvent* - which may be more useful in certain situations. These methods are:
+ - @ref bs::Input::onPointerPressed "Input::onPointerPressed" - Triggered whenever a pointer button has been pressed or screen touch began.
+ - @ref bs::Input::onPointerReleased "Input::onPointerReleased" - Triggered whenever a pointer button has been released or screen touch ended.
+ - @ref bs::Input::onPointerDoubleClick "Input::onPointerDoubleClick" - Triggered when the user quickly clicks the pointer buttons or taps the screen in succession.
+ 
+~~~~~~~~~~~~~{.cpp}
+Vector3 position(BsZero);
+
+// Callback method that triggers when any button is pressed
+auto handleDoubleClick = [&](const PointerEvent& event)
+{
+	// Jump on double click
+	position.y += 5.0f;
+};
+
+// Connect the callback to the event
+gInput().onPointerDoubleClick.connect(&handleDoubleClick);
+~~~~~~~~~~~~~
+
+# Text input
+If user is typing text (using a physical or a touch keyboard) you may subscribe to @ref bs::Input::onCharInput "Input::onCharInput" to receive individual characters as the user inputs them. 
+
+~~~~~~~~~~~~~{.cpp}
+StringStream inputString;
+
+// Callback method that appends a character to the inputString stream
+auto handleDoubleClick = [&](const TextInputEvent& event)
+{
+	inputString<<(char)textChar;
+};
+
+// Connect the callback to the event
+gInput().onCharInput.connect(&handleCharInput);
+~~~~~~~~~~~~~
+
+Note that the system will register keyboard buttons as both text input and as normal button presses - it's up to the caller to decide which to process when. If keyboard is used for gameplay then button presses should be used, but if user is actually typing text, then character input is better suited. This is because button events report button codes as physical keyboard keys, yet character input will actually translate those physical key presses into character codes depending on the user's keyboard settings, which ensures non-english keyboard layouts work as intended.

+ 1 - 1
Documentation/Manuals/Native/User/inputPolling.md

@@ -35,7 +35,7 @@ position.z += gInput().getAxisValue(InputAxis::LeftStickY);
 
 Most axes report their input in range [-1, 1], with the exception of mouse axes, which are unbound. 
 
-# Mouse position
+# Mouse input
 Often it is useful to receive mouse position directly, rather than dealing with raw mouse axis data. Use @ref bs::Input::getPointerPosition "Input::getPointerPosition" to retrieve the current position of the mouse cursor, in coordinates relative to the screen.
 
 Use @ref bs::Input::getPointerDelta "Input::getPointerDelta" to get the difference in coordinates between the position of the mouse on the previous and current frame.

+ 0 - 2
Documentation/Manuals/Native/User/windows.md

@@ -70,8 +70,6 @@ auto& props = newWindow->getProperties();
 gDebug().logDebug(toString(props.getWidth()) + " x " + toString(props.getHeight()));
 ~~~~~~~~~~~~~
 
-> gDebug() is a shortcut to the logging system, as explained in the [logging manual](@ref logging), covered later.
-
 # Window events
 Sometimes you might want to be notified if the user resizes the window externally, in which case use the @ref bs::RenderWindow::onResized "RenderWindow::onResized" event.
 

+ 10 - 10
Documentation/Manuals/Native/manuals.md

@@ -9,6 +9,16 @@ Manuals									{#manuals}
 - **Resources**
  - [Basics and import](@ref resourceBasicsAndImport)
  - [Saving and loading](@ref resourceSavingAndLoading)
+- **Utilities**
+ - [Containers](@ref containers)
+ - [Strings](@ref strings)
+ - [Memory allocation](@ref memory)
+ - [Smart pointers](@ref smartPointers) 
+ - [Events](@ref events)
+ - [File system](@ref fileSystem) 
+ - [Math utilities](@ref mathUtilities)
+ - [Logging messages](@ref logging)
+ - [Measuring time](@ref time)
 - **Graphics**
  - [Windows](@ref windows)
  - [Cameras](@ref cameras)
@@ -20,16 +30,6 @@ Manuals									{#manuals}
 - **Input**
  - [Input polling](@ref inputPolling) 
  - [Input events](@ref inputEvents)  
-- **Utilities**
- - [Containers](@ref containers)
- - [Strings](@ref strings)
- - [Memory allocation](@ref memory)
- - [Smart pointers](@ref smartPointers) 
- - [Events](@ref events)
- - [File system](@ref fileSystem) 
- - [Math utilities](@ref mathUtilities)
- - [Logging messages](@ref logging)
- - [Measuring time](@ref time)
  
 # Developer guides