Implemented the new graphics API:
- Removed the internal classes sf::Renderer and sf::Matrix3 - Split sf::Drawable into sf::Drawable and sf::Transformable - Added sf::Transform - Added sf::Vertex - Added sf::VertexArray - Types of shapes are now handled with their own derived class - Modified the Pong example
This commit is contained in:
parent
541509d2a7
commit
5bae08a2d8
65 changed files with 4756 additions and 3326 deletions
|
@ -4,7 +4,6 @@
|
|||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <SFML/OpenGL.hpp>
|
||||
#include <iostream>
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Entry point of application
|
||||
|
@ -77,9 +76,9 @@ int main()
|
|||
}
|
||||
|
||||
// Draw the background
|
||||
window.SaveGLStates();
|
||||
window.PushGLStates();
|
||||
window.Draw(background);
|
||||
window.RestoreGLStates();
|
||||
window.PopGLStates();
|
||||
|
||||
// Activate the window before using OpenGL commands.
|
||||
// This is useless here because we have only one window which is
|
||||
|
@ -138,12 +137,12 @@ int main()
|
|||
glEnd();
|
||||
|
||||
// Draw some text on top of our OpenGL object
|
||||
window.SaveGLStates();
|
||||
window.PushGLStates();
|
||||
sf::Text text("SFML / OpenGL demo");
|
||||
text.SetPosition(250.f, 450.f);
|
||||
text.SetColor(sf::Color(255, 255, 255, 170));
|
||||
text.SetPosition(250.f, 450.f);
|
||||
window.Draw(text);
|
||||
window.RestoreGLStates();
|
||||
window.PopGLStates();
|
||||
|
||||
// Finally, display the rendered frame on screen
|
||||
window.Display();
|
||||
|
|
|
@ -19,172 +19,216 @@ int main()
|
|||
{
|
||||
std::srand(static_cast<unsigned int>(std::time(NULL)));
|
||||
|
||||
// Defines PI
|
||||
const float PI = 3.14159f;
|
||||
// Define some constants
|
||||
const float pi = 3.14159f;
|
||||
const int gameWidth = 800;
|
||||
const int gameHeight = 600;
|
||||
sf::Vector2f paddleSize(25, 100);
|
||||
float ballRadius = 10.f;
|
||||
|
||||
// Create the window of the application
|
||||
sf::RenderWindow window(sf::VideoMode(800, 600, 32), "SFML Pong");
|
||||
sf::RenderWindow window(sf::VideoMode(gameWidth, gameHeight, 32), "SFML Pong");
|
||||
|
||||
// Load the sounds used in the game
|
||||
sf::SoundBuffer ballSoundBuffer;
|
||||
if (!ballSoundBuffer.LoadFromFile("resources/ball.wav"))
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
sf::Sound ballSound(ballSoundBuffer);
|
||||
|
||||
// Load the textures used in the game
|
||||
sf::Texture backgroundTexture, leftPaddleTexture, rightPaddleTexture, ballTexture;
|
||||
if (!backgroundTexture.LoadFromFile("resources/background.jpg") ||
|
||||
!leftPaddleTexture.LoadFromFile("resources/paddle_left.png") ||
|
||||
!rightPaddleTexture.LoadFromFile("resources/paddle_right.png") ||
|
||||
!ballTexture.LoadFromFile("resources/ball.png"))
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// Create the left paddle
|
||||
sf::RectangleShape leftPaddle;
|
||||
leftPaddle.SetSize(paddleSize - sf::Vector2f(3, 3));
|
||||
leftPaddle.SetOutlineThickness(3);
|
||||
leftPaddle.SetOutlineColor(sf::Color::Black);
|
||||
leftPaddle.SetFillColor(sf::Color(100, 100, 200));
|
||||
leftPaddle.SetOrigin(paddleSize / 2.f);
|
||||
|
||||
// Create the right paddle
|
||||
sf::RectangleShape rightPaddle;
|
||||
rightPaddle.SetSize(paddleSize - sf::Vector2f(3, 3));
|
||||
rightPaddle.SetOutlineThickness(3);
|
||||
rightPaddle.SetOutlineColor(sf::Color::Black);
|
||||
rightPaddle.SetFillColor(sf::Color(200, 100, 100));
|
||||
rightPaddle.SetOrigin(paddleSize / 2.f);
|
||||
|
||||
// Create the ball
|
||||
sf::CircleShape ball;
|
||||
ball.SetRadius(ballRadius - 3);
|
||||
ball.SetOutlineThickness(3);
|
||||
ball.SetOutlineColor(sf::Color::Black);
|
||||
ball.SetFillColor(sf::Color::White);
|
||||
ball.SetOrigin(ballRadius / 2, ballRadius / 2);
|
||||
|
||||
// Load the text font
|
||||
sf::Font font;
|
||||
if (!font.LoadFromFile("resources/sansation.ttf"))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Initialize the end text
|
||||
sf::Text end;
|
||||
end.SetFont(font);
|
||||
end.SetCharacterSize(60);
|
||||
end.Move(150.f, 200.f);
|
||||
end.SetColor(sf::Color(50, 50, 250));
|
||||
|
||||
// Create the sprites of the background, the paddles and the ball
|
||||
sf::Sprite background(backgroundTexture);
|
||||
sf::Sprite leftPaddle(leftPaddleTexture);
|
||||
sf::Sprite rightPaddle(rightPaddleTexture);
|
||||
sf::Sprite ball(ballTexture);
|
||||
|
||||
leftPaddle.Move(10, (window.GetView().GetSize().y - leftPaddle.GetSize().y) / 2);
|
||||
rightPaddle.Move(window.GetView().GetSize().x - rightPaddle.GetSize().x - 10, (window.GetView().GetSize().y - rightPaddle.GetSize().y) / 2);
|
||||
ball.Move((window.GetView().GetSize().x - ball.GetSize().x) / 2, (window.GetView().GetSize().y - ball.GetSize().y) / 2);
|
||||
// Initialize the pause message
|
||||
sf::Text pauseMessage;
|
||||
pauseMessage.SetFont(font);
|
||||
pauseMessage.SetCharacterSize(40);
|
||||
pauseMessage.SetPosition(170.f, 150.f);
|
||||
pauseMessage.SetColor(sf::Color::White);
|
||||
pauseMessage.SetString("Welcome to SFML pong!\nPress space to start the game");
|
||||
|
||||
// Define the paddles properties
|
||||
sf::Clock AITimer;
|
||||
const float AITime = 0.1f;
|
||||
float leftPaddleSpeed = 400.f;
|
||||
float rightPaddleSpeed = 400.f;
|
||||
const sf::Uint32 AITime = 300;
|
||||
const float paddleSpeed = 400.f;
|
||||
float rightPaddleSpeed = 0.f;
|
||||
const float ballSpeed = 400.f;
|
||||
float ballAngle = 0.f; // to be changed later
|
||||
|
||||
// Define the ball properties
|
||||
float ballSpeed = 400.f;
|
||||
float ballAngle;
|
||||
do
|
||||
{
|
||||
// Make sure the ball initial angle is not too much vertical
|
||||
ballAngle = std::rand() * 2 * PI / RAND_MAX;
|
||||
} while (std::abs(std::cos(ballAngle)) < 0.7f);
|
||||
|
||||
bool isPlaying = true;
|
||||
bool isPlaying = false;
|
||||
while (window.IsOpened())
|
||||
{
|
||||
// Handle events
|
||||
sf::Event event;
|
||||
while (window.PollEvent(event))
|
||||
{
|
||||
// Window closed or escape key pressed : exit
|
||||
// Window closed or escape key pressed: exit
|
||||
if ((event.Type == sf::Event::Closed) ||
|
||||
((event.Type == sf::Event::KeyPressed) && (event.Key.Code == sf::Keyboard::Escape)))
|
||||
{
|
||||
window.Close();
|
||||
break;
|
||||
}
|
||||
|
||||
// Space key pressed: play
|
||||
if ((event.Type == sf::Event::KeyPressed) && (event.Key.Code == sf::Keyboard::Space))
|
||||
{
|
||||
if (!isPlaying)
|
||||
{
|
||||
// (re)start the game
|
||||
isPlaying = true;
|
||||
|
||||
// Reset the position of the paddles and ball
|
||||
leftPaddle.SetPosition(10 + paddleSize.x / 2, gameHeight / 2);
|
||||
rightPaddle.SetPosition(gameWidth - 10 - paddleSize.x / 2, gameHeight / 2);
|
||||
ball.SetPosition(gameWidth / 2, gameHeight / 2);
|
||||
|
||||
// Reset the ball angle
|
||||
do
|
||||
{
|
||||
// Make sure the ball initial angle is not too much vertical
|
||||
ballAngle = (std::rand() % 360) * 2 * pi / 360;
|
||||
}
|
||||
while (std::abs(std::cos(ballAngle)) < 0.7f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isPlaying)
|
||||
{
|
||||
float deltaTime = window.GetFrameTime() / 1000.f;
|
||||
|
||||
// Move the player's paddle
|
||||
if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Up) && (leftPaddle.GetPosition().y > 5.f))
|
||||
leftPaddle.Move(0.f, -leftPaddleSpeed * window.GetFrameTime() / 1000.f);
|
||||
if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Down) && (leftPaddle.GetPosition().y < window.GetView().GetSize().y - leftPaddle.GetSize().y - 5.f))
|
||||
leftPaddle.Move(0.f, leftPaddleSpeed * window.GetFrameTime() / 1000.f);
|
||||
if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Up) &&
|
||||
(leftPaddle.GetPosition().y - paddleSize.y / 2 > 5.f))
|
||||
{
|
||||
leftPaddle.Move(0.f, -paddleSpeed * deltaTime);
|
||||
}
|
||||
if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Down) &&
|
||||
(leftPaddle.GetPosition().y + paddleSize.y / 2 < gameHeight - 5.f))
|
||||
{
|
||||
leftPaddle.Move(0.f, paddleSpeed * deltaTime);
|
||||
}
|
||||
|
||||
// Move the computer's paddle
|
||||
if (((rightPaddleSpeed < 0.f) && (rightPaddle.GetPosition().y > 5.f)) ||
|
||||
((rightPaddleSpeed > 0.f) && (rightPaddle.GetPosition().y < window.GetView().GetSize().y - rightPaddle.GetSize().y - 5.f)))
|
||||
if (((rightPaddleSpeed < 0.f) && (rightPaddle.GetPosition().y - paddleSize.y / 2 > 5.f)) ||
|
||||
((rightPaddleSpeed > 0.f) && (rightPaddle.GetPosition().y + paddleSize.y / 2 < gameHeight - 5.f)))
|
||||
{
|
||||
rightPaddle.Move(0.f, rightPaddleSpeed * window.GetFrameTime() / 1000.f);
|
||||
rightPaddle.Move(0.f, rightPaddleSpeed * deltaTime);
|
||||
}
|
||||
|
||||
// Update the computer's paddle direction according to the ball position
|
||||
if (AITimer.GetElapsedTime() > AITime)
|
||||
{
|
||||
AITimer.Reset();
|
||||
if ((rightPaddleSpeed < 0) && (ball.GetPosition().y + ball.GetSize().y > rightPaddle.GetPosition().y + rightPaddle.GetSize().y))
|
||||
rightPaddleSpeed = -rightPaddleSpeed;
|
||||
if ((rightPaddleSpeed > 0) && (ball.GetPosition().y < rightPaddle.GetPosition().y))
|
||||
rightPaddleSpeed = -rightPaddleSpeed;
|
||||
if (ball.GetPosition().y + ballRadius > rightPaddle.GetPosition().y + paddleSize.y / 2)
|
||||
rightPaddleSpeed = paddleSpeed;
|
||||
else if (ball.GetPosition().y - ballRadius < rightPaddle.GetPosition().y - paddleSize.y / 2)
|
||||
rightPaddleSpeed = -paddleSpeed;
|
||||
else
|
||||
rightPaddleSpeed = 0.f;
|
||||
}
|
||||
|
||||
// Move the ball
|
||||
float factor = ballSpeed * window.GetFrameTime() / 1000.f;
|
||||
ball.Move(std::cos(ballAngle) * factor, std::sin(ballAngle) * factor);
|
||||
float factor = ballSpeed * deltaTime;
|
||||
ball.Move(std::cos(ballAngle) * factor, std::sin(ballAngle) * factor);
|
||||
|
||||
// Check collisions between the ball and the screen
|
||||
if (ball.GetPosition().x < 0.f)
|
||||
if (ball.GetPosition().x - ballRadius < 0.f)
|
||||
{
|
||||
isPlaying = false;
|
||||
end.SetString("You lost !\n(press escape to exit)");
|
||||
pauseMessage.SetString("You lost !\nPress space to restart or\nescape to exit");
|
||||
}
|
||||
if (ball.GetPosition().x + ball.GetSize().x > window.GetView().GetSize().x)
|
||||
if (ball.GetPosition().x + ballRadius > 800)
|
||||
{
|
||||
isPlaying = false;
|
||||
end.SetString("You won !\n(press escape to exit)");
|
||||
pauseMessage.SetString("You won !\nPress space to restart or\nescape to exit");
|
||||
}
|
||||
if (ball.GetPosition().y < 0.f)
|
||||
if (ball.GetPosition().y - ballRadius < 0.f)
|
||||
{
|
||||
ballSound.Play();
|
||||
ballAngle = -ballAngle;
|
||||
ball.SetY(0.1f);
|
||||
ball.SetPosition(ball.GetPosition().x, ballRadius + 0.1f);
|
||||
}
|
||||
if (ball.GetPosition().y + ball.GetSize().y > window.GetView().GetSize().y)
|
||||
if (ball.GetPosition().y + ballRadius > gameHeight)
|
||||
{
|
||||
ballSound.Play();
|
||||
ballAngle = -ballAngle;
|
||||
ball.SetY(window.GetView().GetSize().y - ball.GetSize().y - 0.1f);
|
||||
ball.SetPosition(ball.GetPosition().x, gameHeight - ballRadius - 0.1f);
|
||||
}
|
||||
|
||||
// Check the collisions between the ball and the paddles
|
||||
// Left Paddle
|
||||
if (ball.GetPosition().x < leftPaddle.GetPosition().x + leftPaddle.GetSize().x &&
|
||||
ball.GetPosition().x > leftPaddle.GetPosition().x + (leftPaddle.GetSize().x / 2.0f) &&
|
||||
ball.GetPosition().y + ball.GetSize().y >= leftPaddle.GetPosition().y &&
|
||||
ball.GetPosition().y <= leftPaddle.GetPosition().y + leftPaddle.GetSize().y)
|
||||
if (ball.GetPosition().x - ballRadius < leftPaddle.GetPosition().x + paddleSize.x / 2 &&
|
||||
ball.GetPosition().x - ballRadius > leftPaddle.GetPosition().x &&
|
||||
ball.GetPosition().y + ballRadius >= leftPaddle.GetPosition().y - paddleSize.y / 2 &&
|
||||
ball.GetPosition().y - ballRadius <= leftPaddle.GetPosition().y + paddleSize.y / 2)
|
||||
{
|
||||
if (ball.GetPosition().y > leftPaddle.GetPosition().y)
|
||||
ballAngle = pi - ballAngle + (std::rand() % 20) * pi / 180;
|
||||
else
|
||||
ballAngle = pi - ballAngle - (std::rand() % 20) * pi / 180;
|
||||
|
||||
ballSound.Play();
|
||||
ballAngle = PI - ballAngle;
|
||||
ball.SetX(leftPaddle.GetPosition().x + leftPaddle.GetSize().x + 0.1f);
|
||||
ball.SetPosition(leftPaddle.GetPosition().x + ballRadius + paddleSize.x / 2 + 0.1f, ball.GetPosition().y);
|
||||
}
|
||||
|
||||
// Right Paddle
|
||||
if (ball.GetPosition().x + ball.GetSize().x > rightPaddle.GetPosition().x &&
|
||||
ball.GetPosition().x + ball.GetSize().x < rightPaddle.GetPosition().x + (rightPaddle.GetSize().x / 2.0f) &&
|
||||
ball.GetPosition().y + ball.GetSize().y >= rightPaddle.GetPosition().y &&
|
||||
ball.GetPosition().y <= rightPaddle.GetPosition().y + rightPaddle.GetSize().y)
|
||||
if (ball.GetPosition().x + ballRadius > rightPaddle.GetPosition().x - paddleSize.x / 2 &&
|
||||
ball.GetPosition().x + ballRadius < rightPaddle.GetPosition().x &&
|
||||
ball.GetPosition().y + ballRadius >= rightPaddle.GetPosition().y - paddleSize.y / 2 &&
|
||||
ball.GetPosition().y - ballRadius <= rightPaddle.GetPosition().y + paddleSize.y / 2)
|
||||
{
|
||||
if (ball.GetPosition().y > rightPaddle.GetPosition().y)
|
||||
ballAngle = pi - ballAngle + (std::rand() % 20) * pi / 180;
|
||||
else
|
||||
ballAngle = pi - ballAngle - (std::rand() % 20) * pi / 180;
|
||||
|
||||
ballSound.Play();
|
||||
ballAngle = PI - ballAngle;
|
||||
ball.SetX(rightPaddle.GetPosition().x - ball.GetSize().x - 0.1f);
|
||||
ball.SetPosition(rightPaddle.GetPosition().x - ballRadius - paddleSize.x / 2 - 0.1f, ball.GetPosition().y);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the window
|
||||
window.Clear();
|
||||
window.Clear(sf::Color(50, 200, 50));
|
||||
|
||||
// Draw the background, paddles and ball sprites
|
||||
window.Draw(background);
|
||||
window.Draw(leftPaddle);
|
||||
window.Draw(rightPaddle);
|
||||
window.Draw(ball);
|
||||
|
||||
// If the game is over, display the end message
|
||||
if (!isPlaying)
|
||||
window.Draw(end);
|
||||
if (isPlaying)
|
||||
{
|
||||
// Draw the paddles and the ball
|
||||
window.Draw(leftPaddle);
|
||||
window.Draw(rightPaddle);
|
||||
window.Draw(ball);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw the pause message
|
||||
window.Draw(pauseMessage);
|
||||
}
|
||||
|
||||
// Display things on screen
|
||||
window.Display();
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 88 KiB |
Binary file not shown.
Before Width: | Height: | Size: 249 B |
Binary file not shown.
Before Width: | Height: | Size: 762 B |
Binary file not shown.
Before Width: | Height: | Size: 683 B |
|
@ -58,9 +58,9 @@ public :
|
|||
}
|
||||
|
||||
// Get the current shader
|
||||
const sf::Shader& GetShader() const
|
||||
const sf::Shader* GetShader() const
|
||||
{
|
||||
return myIterator->second;
|
||||
return &myIterator->second;
|
||||
}
|
||||
|
||||
private :
|
||||
|
@ -144,8 +144,8 @@ int main()
|
|||
sf::Text shaderStr;
|
||||
shaderStr.SetFont(font);
|
||||
shaderStr.SetCharacterSize(20);
|
||||
shaderStr.SetPosition(5.f, 0.f);
|
||||
shaderStr.SetColor(sf::Color(250, 100, 30));
|
||||
shaderStr.SetPosition(5.f, 0.f);
|
||||
shaderStr.SetString("Background shader: \"" + backgroundShader.GetName() + "\"\n"
|
||||
"Flower shader: \"" + entityShader.GetName() + "\"\n"
|
||||
"Global shader: \"" + globalShader.GetName() + "\"\n");
|
||||
|
@ -154,8 +154,8 @@ int main()
|
|||
sf::Text infoStr;
|
||||
infoStr.SetFont(font);
|
||||
infoStr.SetCharacterSize(20);
|
||||
infoStr.SetPosition(5.f, 500.f);
|
||||
infoStr.SetColor(sf::Color(250, 100, 30));
|
||||
infoStr.SetPosition(5.f, 500.f);
|
||||
infoStr.SetString("Move your mouse to change the shaders' parameters\n"
|
||||
"Press numpad 1/4 to change the background shader\n"
|
||||
"Press numpad 2/5 to change the flower shader\n"
|
||||
|
@ -249,8 +249,8 @@ void DisplayError()
|
|||
|
||||
// Define a string for displaying the error message
|
||||
sf::Text error("Sorry, your system doesn't support shaders");
|
||||
error.SetPosition(100.f, 250.f);
|
||||
error.SetColor(sf::Color(200, 100, 150));
|
||||
error.SetPosition(100.f, 250.f);
|
||||
|
||||
// Start the game loop
|
||||
while (window.IsOpened())
|
||||
|
|
|
@ -35,7 +35,8 @@ void PlaySound()
|
|||
sf::Sleep(100);
|
||||
|
||||
// Display the playing position
|
||||
std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << sound.GetPlayingOffset() << " sec ";
|
||||
std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << sound.GetPlayingOffset() / 1000.f << " sec ";
|
||||
std::cout << std::flush;
|
||||
}
|
||||
std::cout << std::endl << std::endl;
|
||||
}
|
||||
|
@ -68,7 +69,8 @@ void PlayMusic()
|
|||
sf::Sleep(100);
|
||||
|
||||
// Display the playing position
|
||||
std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << music.GetPlayingOffset() << " sec ";
|
||||
std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << music.GetPlayingOffset() / 1000.f << " sec ";
|
||||
std::cout << std::flush;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
|
|
@ -76,7 +76,8 @@ int main()
|
|||
while (sound.GetStatus() == sf::Sound::Playing)
|
||||
{
|
||||
// Display the playing position
|
||||
std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << sound.GetPlayingOffset() << " sec";
|
||||
std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << sound.GetPlayingOffset() / 1000.f << " sec";
|
||||
std::cout << std::flush;
|
||||
|
||||
// Leave some CPU time for other threads
|
||||
sf::Sleep(100);
|
||||
|
|
|
@ -81,8 +81,8 @@ INT WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, INT)
|
|||
return EXIT_FAILURE;
|
||||
sf::Sprite sprite1(texture1);
|
||||
sf::Sprite sprite2(texture2);
|
||||
sprite1.SetOrigin(sprite1.GetSize() / 2.f);
|
||||
sprite1.SetPosition(sprite1.GetSize() / 2.f);
|
||||
sprite1.SetOrigin(texture1.GetWidth() / 2.f, texture1.GetHeight() / 2.f);
|
||||
sprite1.SetPosition(sprite1.GetOrigin());
|
||||
|
||||
// Create a clock for measuring elapsed time
|
||||
sf::Clock clock;
|
||||
|
@ -100,16 +100,18 @@ INT WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, INT)
|
|||
}
|
||||
else
|
||||
{
|
||||
sf::Uint32 time = clock.GetElapsedTime();
|
||||
|
||||
// Clear views
|
||||
SFMLView1.Clear();
|
||||
SFMLView2.Clear();
|
||||
|
||||
// Draw sprite 1 on view 1
|
||||
sprite1.SetRotation(clock.GetElapsedTime() * 0.1f);
|
||||
sprite1.SetRotation(time * 0.1f);
|
||||
SFMLView1.Draw(sprite1);
|
||||
|
||||
// Draw sprite 2 on view 2
|
||||
sprite2.SetX(cos(clock.GetElapsedTime() * 0.001f) * 100.f);
|
||||
sprite2.SetPosition(std::cos(time * 0.001f) * 100.f, 0.f);
|
||||
SFMLView2.Draw(sprite2);
|
||||
|
||||
// Display each view on screen
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue