numerous bug fixes
This commit is contained in:
parent
e8bec58827
commit
5df9198be8
60
src/main.rs
60
src/main.rs
@ -1,10 +1,12 @@
|
|||||||
//! src/main.rs
|
//! src/main.rs
|
||||||
//! This file launches the web server which hosts the fileserver and the api
|
//! This file launches the web server which hosts the fileserver and the api
|
||||||
|
//! Author: Arlo Filley
|
||||||
//!
|
//!
|
||||||
|
//! TODO:
|
||||||
|
//! - move structures into a different file
|
||||||
|
//! - find a way to make logging in more secure (password hashes?)
|
||||||
|
|
||||||
pub mod sql;
|
// Imports for rocket
|
||||||
|
|
||||||
// relevant macros and imports for rocket.rs
|
|
||||||
#[macro_use] extern crate rocket;
|
#[macro_use] extern crate rocket;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
Rocket,
|
Rocket,
|
||||||
@ -18,16 +20,31 @@ use rocket::{
|
|||||||
json::Json
|
json::Json
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
use sql::LeaderBoardTest;
|
|
||||||
|
// Imports for sql, see sql.rs for more information
|
||||||
|
pub mod sql;
|
||||||
use crate::sql::*;
|
use crate::sql::*;
|
||||||
|
|
||||||
|
/// Test api route that returns hello world.
|
||||||
|
/// Acessible from http://url/test
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn test() -> String {
|
fn test() -> String {
|
||||||
sql::create_database()
|
|
||||||
.expect("couldn't create database");
|
|
||||||
String::from("Hello World!")
|
String::from("Hello World!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Api route that creates a database if one
|
||||||
|
/// does not already exist.
|
||||||
|
/// Acessible from http://url/api/create_database
|
||||||
|
#[get("/create_database")]
|
||||||
|
fn create_database() -> String {
|
||||||
|
sql::create_database()
|
||||||
|
.expect("couldn't create database");
|
||||||
|
String::from("Successfully created a database")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// the datascructure that the webserver will recieve
|
||||||
|
/// when a post is made to the http://url/api/post_test route
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
struct PostTest<'r> {
|
struct PostTest<'r> {
|
||||||
@ -41,6 +58,8 @@ struct PostTest<'r> {
|
|||||||
user_id: u32
|
user_id: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Api Route that accepts test data and posts it to the database
|
||||||
|
/// Acessible from http://url/api/post_test
|
||||||
#[post("/post_test", data = "<test>")]
|
#[post("/post_test", data = "<test>")]
|
||||||
fn post_test(
|
fn post_test(
|
||||||
test: Json<PostTest<'_>>
|
test: Json<PostTest<'_>>
|
||||||
@ -57,6 +76,7 @@ fn post_test(
|
|||||||
).expect("error in posting test to tests table");
|
).expect("error in posting test to tests table");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Struct representing the user
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
struct User<'r> {
|
struct User<'r> {
|
||||||
@ -64,6 +84,9 @@ struct User<'r> {
|
|||||||
password: &'r str
|
password: &'r str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Route takes data about the user as a struct
|
||||||
|
/// and then creates the user in the database
|
||||||
|
/// Acessible from http://url/api/create_user
|
||||||
#[post("/create_user", data = "<user>")]
|
#[post("/create_user", data = "<user>")]
|
||||||
fn create_user(
|
fn create_user(
|
||||||
user: Json<User<'_>>
|
user: Json<User<'_>>
|
||||||
@ -74,28 +97,47 @@ fn create_user(
|
|||||||
).expect("Error: Couldn't create new user");
|
).expect("Error: Couldn't create new user");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// takes the users login information and returns the users user id
|
||||||
|
/// which can be used to identify their tests etc.
|
||||||
|
/// Accessible from http://url/api/login
|
||||||
#[get("/login/<username>/<password>")]
|
#[get("/login/<username>/<password>")]
|
||||||
fn login(username: &str, password: &str) -> String {
|
fn login(username: &str, password: &str) -> String {
|
||||||
let user_id = sql::find_user(username, password).expect("error finding user_id");
|
let user_id = sql::find_user(username, password).expect("error finding user_id");
|
||||||
user_id.to_string()
|
user_id.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the users tests from the database and returns it as a
|
||||||
|
/// json array.
|
||||||
|
/// Accessible from http://url/api/get_user_tests
|
||||||
#[get("/get_user_tests/<user_id>")]
|
#[get("/get_user_tests/<user_id>")]
|
||||||
fn get_user_tests(user_id: u32) -> Json<Vec<Test>> {
|
fn get_user_tests(user_id: u32) -> Json<Vec<Test>> {
|
||||||
let tests = sql::get_user_tests(user_id).expect("error finding user_id");
|
let tests = sql::get_user_tests(user_id).expect("error finding user_id");
|
||||||
Json(tests)
|
Json(tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the highest test data from each user as
|
||||||
|
/// a json array
|
||||||
|
/// Acessible from http://url/api/leaderboard
|
||||||
#[get("/leaderboard")]
|
#[get("/leaderboard")]
|
||||||
fn leaderboard() -> Json<Vec<LeaderBoardTest>> {
|
fn leaderboard() -> Json<Vec<LeaderBoardTest>> {
|
||||||
let leaderboard = sql::get_leaderboard(0).expect("error finding user_id");
|
let leaderboard = sql::get_leaderboard(0).expect("error finding user_id");
|
||||||
Json(leaderboard)
|
Json(leaderboard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The main function which builds and launches the
|
||||||
|
/// webserver with all appropriate routes and fileservers
|
||||||
#[launch]
|
#[launch]
|
||||||
fn rocket() -> Rocket<Build> {
|
fn rocket() -> Rocket<Build> {
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.mount("/test", routes![test]) // testing only, should return "Hello world"
|
// testing only, should return "Hello world"
|
||||||
.mount("/api", routes![post_test, create_user, login, get_user_tests, leaderboard])
|
.mount("/test", routes![test])
|
||||||
.mount("/typing", FileServer::from(relative!("website"))) // hosts the fileserver
|
// hosts the api routes necessary for the website
|
||||||
|
// to interact with the database
|
||||||
|
.mount("/api", routes![
|
||||||
|
create_database, create_user,
|
||||||
|
post_test, login, get_user_tests,
|
||||||
|
leaderboard
|
||||||
|
])
|
||||||
|
// hosts the fileserver
|
||||||
|
.mount("/typing", FileServer::from(relative!("website")))
|
||||||
}
|
}
|
33
src/sql.rs
33
src/sql.rs
@ -1,11 +1,28 @@
|
|||||||
|
//! src/sql.rs
|
||||||
|
//! This file contains all the necessary code to
|
||||||
|
//! interact with the sqlite database using functions
|
||||||
|
//! it abstracts away the rusqlite necessary to perform
|
||||||
|
//! these functions
|
||||||
|
//! Author: Arlo Filley
|
||||||
|
//!
|
||||||
|
//! TODO:
|
||||||
|
//! - put necessary structs into a different file
|
||||||
|
//! - create structure for the input of post test
|
||||||
|
|
||||||
|
// Imports for json handling and rusqlite
|
||||||
use rusqlite::{Connection, Result};
|
use rusqlite::{Connection, Result};
|
||||||
use rocket::serde::Serialize;
|
use rocket::serde::Serialize;
|
||||||
|
|
||||||
|
/// gets a connection to the database and returns it as
|
||||||
|
/// a rusqlite::connection
|
||||||
fn get_connection() -> rusqlite::Connection {
|
fn get_connection() -> rusqlite::Connection {
|
||||||
Connection::open("database/database.sqlite")
|
Connection::open("database/database.sqlite")
|
||||||
.expect("Error creating database connection")
|
.expect("Error creating database connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates the necessary tables inside the database with
|
||||||
|
/// correct normalised links between data for later
|
||||||
|
/// querying
|
||||||
pub fn create_database() -> Result<()> {
|
pub fn create_database() -> Result<()> {
|
||||||
let connection = get_connection();
|
let connection = get_connection();
|
||||||
|
|
||||||
@ -37,6 +54,8 @@ pub fn create_database() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// takes necessary data about a test and creates
|
||||||
|
/// a database record with the data
|
||||||
pub fn post_test(
|
pub fn post_test(
|
||||||
test_type: &str,
|
test_type: &str,
|
||||||
test_length: u32,
|
test_length: u32,
|
||||||
@ -86,6 +105,8 @@ pub fn post_test(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// takes a username and password and creates a database
|
||||||
|
/// entry for a new user
|
||||||
pub fn create_user(
|
pub fn create_user(
|
||||||
username: &str,
|
username: &str,
|
||||||
password: &str
|
password: &str
|
||||||
@ -113,11 +134,15 @@ pub fn create_user(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// struct which can be deserialised
|
||||||
|
/// from json to get the user_id
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
user_id: u32,
|
user_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// takes a username and password as inputs and returns the
|
||||||
|
/// user_id of the user if one exists
|
||||||
pub fn find_user(
|
pub fn find_user(
|
||||||
username: &str,
|
username: &str,
|
||||||
password: &str
|
password: &str
|
||||||
@ -146,6 +171,8 @@ pub fn find_user(
|
|||||||
Ok(user_id)
|
Ok(user_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// struct representing data that needs to be sent
|
||||||
|
/// to the user
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct Test {
|
pub struct Test {
|
||||||
@ -158,6 +185,8 @@ pub struct Test {
|
|||||||
accuracy: u8,
|
accuracy: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns all the tests that a given user_id has
|
||||||
|
/// completed from the database
|
||||||
pub fn get_user_tests(
|
pub fn get_user_tests(
|
||||||
user_id: u32
|
user_id: u32
|
||||||
) -> Result<Vec<Test>> {
|
) -> Result<Vec<Test>> {
|
||||||
@ -189,6 +218,8 @@ pub fn get_user_tests(
|
|||||||
Ok(tests)
|
Ok(tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// struct that represents all the data that gets sent to the user
|
||||||
|
/// when they make a leaderboard request
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct LeaderBoardTest {
|
pub struct LeaderBoardTest {
|
||||||
@ -196,6 +227,8 @@ pub struct LeaderBoardTest {
|
|||||||
wpm: u8,
|
wpm: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns a vector of leaderboard tests, where each one is the fastest words
|
||||||
|
/// per minute that a given user has achieved
|
||||||
pub fn get_leaderboard(
|
pub fn get_leaderboard(
|
||||||
_user_id: u32
|
_user_id: u32
|
||||||
) -> Result<Vec<LeaderBoardTest>>{
|
) -> Result<Vec<LeaderBoardTest>>{
|
||||||
|
@ -1,3 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides abstracted functions to interact with the api
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - use localstorage for storing test data
|
||||||
|
* - allow for test storage without an account
|
||||||
|
* - validate all inputs that are sent to the server
|
||||||
|
* - give useful errors to the user if there is an api error
|
||||||
|
* - split into multiple files to more easily read code
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides all the useful methods to interact with the api.
|
||||||
|
*/
|
||||||
class API {
|
class API {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -48,6 +63,8 @@ class API {
|
|||||||
xhr.send(
|
xhr.send(
|
||||||
JSON.stringify(data)
|
JSON.stringify(data)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
user.lastTest = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,21 +74,25 @@ class API {
|
|||||||
const test = screenManager.screen.textbox.getLetters();
|
const test = screenManager.screen.textbox.getLetters();
|
||||||
const testType = "words";
|
const testType = "words";
|
||||||
let testLength = test.length;
|
let testLength = test.length;
|
||||||
let testTime = screenManager.timer.getTime();
|
let testTime = screenManager.screen.timer.getTime();
|
||||||
const testSeed = 0;
|
const testSeed = 0;
|
||||||
const quoteId = 0;
|
const quoteId = 0;
|
||||||
let wpm;
|
let wpm;
|
||||||
const accuracy = 0;
|
|
||||||
const userId = Number(user.userId);
|
const userId = Number(user.userId);
|
||||||
let test_content = screenManager.screen.textbox.testContent;
|
let test_content = screenManager.screen.textbox.testContent;
|
||||||
|
|
||||||
let string = "";
|
let string = "";
|
||||||
|
let inaccurateLetters = 0;
|
||||||
for (let letter = 0; letter < test.length; letter++) {
|
for (let letter = 0; letter < test.length; letter++) {
|
||||||
if (test[letter] === test_content[letter]) {
|
if (test[letter] === test_content[letter]) {
|
||||||
string += test[letter];
|
string += test[letter];
|
||||||
|
} else {
|
||||||
|
inaccurateLetters += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const accuracy = (test.length - inaccurateLetters) / test.length * 100;
|
||||||
|
|
||||||
// this is the wpm calculation factoring in the time of test
|
// this is the wpm calculation factoring in the time of test
|
||||||
// it assumes that all words are 5 characters long because on average
|
// it assumes that all words are 5 characters long because on average
|
||||||
// they are
|
// they are
|
||||||
|
@ -1,11 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides an abstraction of all the data about the user
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - save user preferences about colours
|
||||||
|
* - make greater useage of localstorage to store tests before signup/login
|
||||||
|
* and post them to the database if a login is made.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this class displays a number of textboxes that allows the user to input a
|
||||||
|
* username and password. Then find out the user_id of that account through the
|
||||||
|
* necessary api routes.
|
||||||
|
*/
|
||||||
class User {
|
class User {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.username = "not logged in";
|
this.username = "not logged in";
|
||||||
this.password = "there";
|
this.password = "there";
|
||||||
this.userId = 0;
|
this.userId = 0;
|
||||||
this.tests;
|
|
||||||
this.leaderboard;
|
this.leaderboard;
|
||||||
this.nextTest = `satisfy powerful pleasant bells disastrous mean kited is gusted romantic past taste immolate productive leak close show crabby awake handsails finicky betray long-term incompetent wander show manage toys convey hop constitute number send like off ice aboard well-made vast vacuous tramp seed force divergent flower porter fire untidy soggy fetch`;
|
|
||||||
this.time = 15;
|
this.time = 15;
|
||||||
|
|
||||||
|
this.tests;
|
||||||
|
this.lastTest;
|
||||||
|
this.nextTest = `satisfy powerful pleasant bells disastrous mean kited is gusted romantic past taste immolate productive leak close show crabby awake handsails finicky betray long-term incompetent wander show manage toys convey hop constitute number send like off ice aboard well-made vast vacuous tramp seed force divergent flower porter fire untidy soggy fetch`;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +0,0 @@
|
|||||||
This favicon was generated using the following font:
|
|
||||||
|
|
||||||
- Font Title: Roboto
|
|
||||||
- Font Author: Copyright 2011 Google Inc. All Rights Reserved.
|
|
||||||
- Font Source: http://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5vAx05IsDqlA.ttf
|
|
||||||
- Font License: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html))
|
|
Binary file not shown.
@ -32,10 +32,12 @@
|
|||||||
<script src="./screens/startscreen.js"></script>
|
<script src="./screens/startscreen.js"></script>
|
||||||
<script src="./screens/testscreen.js"></script>
|
<script src="./screens/testscreen.js"></script>
|
||||||
<script src="./screens/endscreen.js"></script>
|
<script src="./screens/endscreen.js"></script>
|
||||||
|
<script src="./screens/accountScreen.js"></script>
|
||||||
<script src="./screens/signUpScreen.js"></script>
|
<script src="./screens/signUpScreen.js"></script>
|
||||||
<script src="./screens/loginscreen.js"></script>
|
<script src="./screens/loginscreen.js"></script>
|
||||||
<script src="./screens/profilescreen.js"></script>
|
<script src="./screens/profilescreen.js"></script>
|
||||||
<script src="./screens/leaderboardscreen.js"></script>
|
<script src="./screens/leaderboardscreen.js"></script>
|
||||||
|
<script src="./screens/settingsScreen.js"></script>
|
||||||
|
|
||||||
<!-- API Script Files -->
|
<!-- API Script Files -->
|
||||||
<script src="./api/api.js"></script>
|
<script src="./api/api.js"></script>
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
* @author Arlo Filley
|
* @author Arlo Filley
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// these are all of the globally accessible variables that are
|
||||||
|
// needed for the site to run correctly
|
||||||
let canvas, api, screenManager, user;
|
let canvas, api, screenManager, user;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
133
website/screens/accountScreen.js
Normal file
133
website/screens/accountScreen.js
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides a screen for the user to login through
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - move into an seperated account page with signup and logout
|
||||||
|
* - make passwords not display plain text
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this class displays a number of textboxes that allows the user to input a
|
||||||
|
* username and password. Then find out the user_id of that account through the
|
||||||
|
* necessary api routes.
|
||||||
|
*/
|
||||||
|
class AccountScreen {
|
||||||
|
constructor() {
|
||||||
|
this.textboxes = [
|
||||||
|
new Textbox(
|
||||||
|
120, 350, 500, 100, 0, true, "#000", false,
|
||||||
|
"#000", "#000", true
|
||||||
|
),
|
||||||
|
|
||||||
|
new Textbox(
|
||||||
|
120, 500, 500, 100, 0, true, "#000", false,
|
||||||
|
"000", "#000", false
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
this.buttons = [
|
||||||
|
new Button(
|
||||||
|
100, 300, 500, 100, 0, true, "#000", false,
|
||||||
|
"#000", "#fff", "", true, "#000", "#000", "#fff"
|
||||||
|
),
|
||||||
|
|
||||||
|
new Button(
|
||||||
|
100, 450, 500, 100, 0, true, "#000", false,
|
||||||
|
"#000", "#fff", "", true, "#000", "#000", "#fff"
|
||||||
|
),
|
||||||
|
|
||||||
|
new Button(
|
||||||
|
900, 300, 100, 50, 0, true, "#000", false,
|
||||||
|
"#000", "#00ff00", "Login"
|
||||||
|
),
|
||||||
|
|
||||||
|
new Button(
|
||||||
|
900, 400, 100, 50, 0, true, "#000", false,
|
||||||
|
"#000", "#00ff00", "Sign up"
|
||||||
|
),
|
||||||
|
|
||||||
|
new Button(
|
||||||
|
900, 500, 100, 50, 0, true, "#000", false,
|
||||||
|
"#000", "#00ff00", "Logout"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
this.menu = new Menu();
|
||||||
|
|
||||||
|
// keeps track of which textbox the user last clicked on
|
||||||
|
this.activeTextBox = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the SignUpScreen class with all
|
||||||
|
* appropriate elements
|
||||||
|
*/
|
||||||
|
draw() {
|
||||||
|
background("#eeeee4");
|
||||||
|
textSize(100);
|
||||||
|
fill("#000");
|
||||||
|
text("Account", 300, 100);
|
||||||
|
for (let i = 0; i < this.buttons.length; i++) {
|
||||||
|
this.buttons[i].draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this.textboxes.length; i++) {
|
||||||
|
this.textboxes[i].draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
textSize(30);
|
||||||
|
text("Username", 110, 275);
|
||||||
|
text("Password", 110, 425);
|
||||||
|
|
||||||
|
if (this.buttons[0].isPressed()) {
|
||||||
|
this.textboxes[this.activeTextBox].line = false;
|
||||||
|
this.activeTextBox=0;
|
||||||
|
this.textboxes[this.activeTextBox].line = true;
|
||||||
|
} else if (this.buttons[1].isPressed()) {
|
||||||
|
this.textboxes[this.activeTextBox].line = false;
|
||||||
|
this.activeTextBox=1;
|
||||||
|
this.textboxes[this.activeTextBox].line = true;
|
||||||
|
} else if (this.buttons[2].isPressed()) {
|
||||||
|
api.login(
|
||||||
|
this.textboxes[0].getWords(),
|
||||||
|
this.textboxes[1].getWords()
|
||||||
|
)
|
||||||
|
screenManager.setScreen(new StartScreen());
|
||||||
|
} else if (this.buttons[3].isPressed()) {
|
||||||
|
api.createUser(
|
||||||
|
this.textboxes[0].getWords(),
|
||||||
|
this.textboxes[1].getWords()
|
||||||
|
)
|
||||||
|
screenManager.setScreen(new StartScreen());
|
||||||
|
} else if (this.buttons[4].isPressed()) {
|
||||||
|
api.logout();
|
||||||
|
screenManager.setScreen(new StartScreen());
|
||||||
|
}
|
||||||
|
this.menu.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {key} key
|
||||||
|
*/
|
||||||
|
letterTyped(key) {
|
||||||
|
if (key === "Tab" && this.activeTextBox === 0) {
|
||||||
|
this.textboxes[this.activeTextBox].line = false;
|
||||||
|
this.activeTextBox=1;
|
||||||
|
this.textboxes[this.activeTextBox].line = true;
|
||||||
|
} else if (key === "Tab" && this.activeTextBox === 1) {
|
||||||
|
this.textboxes[this.activeTextBox].line = false;
|
||||||
|
this.activeTextBox=0;
|
||||||
|
this.textboxes[this.activeTextBox].line = true;
|
||||||
|
} else if (key === "Enter") {
|
||||||
|
api.login(
|
||||||
|
this.textboxes[0].getWords(),
|
||||||
|
this.textboxes[1].getWords()
|
||||||
|
)
|
||||||
|
screenManager.setScreen(new StartScreen());
|
||||||
|
} else {
|
||||||
|
this.textboxes[this.activeTextBox].letterTyped(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides a screen class that can be displayed at the end of a test
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - provide the user with the data of the test that they have just
|
||||||
|
* completed, such as their wpm, accuracy, etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is for a screen that is displayed at the end of a test,
|
||||||
|
* currently it just tells the user to press start to enter another test
|
||||||
|
*/
|
||||||
class EndScreen {
|
class EndScreen {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.menu = new Menu();
|
this.menu = new Menu();
|
||||||
@ -8,7 +21,13 @@ class EndScreen {
|
|||||||
textSize(100);
|
textSize(100);
|
||||||
textAlign(CENTER, CENTER);
|
textAlign(CENTER, CENTER);
|
||||||
fill(0);
|
fill(0);
|
||||||
text("Test Complete\nPress enter to start another test", 0, 0, windowWidth - 100, windowHeight - 100);
|
text("Test Complete", 0, 0, windowWidth - 100, windowHeight / 6);
|
||||||
|
|
||||||
|
textSize(30);
|
||||||
|
text(`${user.lastTest.wpm} words per minute`, windowWidth / 2, 200);
|
||||||
|
text(`${user.lastTest.accuracy}% accuracy`, windowWidth / 2, 240);
|
||||||
|
text(`${user.lastTest.test_length} characters typed`, windowWidth / 2, 280);
|
||||||
|
text(`${user.lastTest.test_time}s`, windowWidth / 2, 320);
|
||||||
this.menu.draw();
|
this.menu.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides a leaderboard for the user to compare times.
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - implement a way for the user to scroll down the tests.
|
||||||
|
* - display more tests on the screen at once, up to 15
|
||||||
|
* - store the leaderboard in localstorage as a cache for the most recent results
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this class is a screen which shows the current leaderboard from the
|
||||||
|
* results gotten via the api.
|
||||||
|
*/
|
||||||
class LeaderboardScreen {
|
class LeaderboardScreen {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.menu = new Menu();
|
this.menu = new Menu();
|
||||||
|
@ -1,65 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides a screen for the user to login through
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - move into an seperated account page with signup and logout
|
||||||
|
* - make passwords not display plain text
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this class displays a number of textboxes that allows the user to input a
|
||||||
|
* username and password. Then find out the user_id of that account through the
|
||||||
|
* necessary api routes.
|
||||||
|
*/
|
||||||
class LoginScreen {
|
class LoginScreen {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.textboxes = [
|
this.textboxes = [
|
||||||
new Textbox(
|
new Textbox(
|
||||||
120,250,
|
120, 250, 500, 100, 0, true, "#000", false,
|
||||||
500,100,
|
"#000", "#000", true
|
||||||
0,
|
|
||||||
true,
|
|
||||||
"#000",
|
|
||||||
false, "#000",
|
|
||||||
"#000",
|
|
||||||
true
|
|
||||||
),
|
),
|
||||||
|
|
||||||
new Textbox(
|
new Textbox(
|
||||||
120,400,
|
120, 400, 500, 100, 0, true, "#000", false,
|
||||||
500,100,
|
"000", "#000", false
|
||||||
0,
|
|
||||||
true,
|
|
||||||
"#000",
|
|
||||||
false,"000",
|
|
||||||
"#000",
|
|
||||||
false
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
this.buttons = [
|
this.buttons = [
|
||||||
new Button(
|
new Button(
|
||||||
100,200,
|
100, 200, 500, 100, 0, true, "#000", false,
|
||||||
500,100,
|
"#000", "#fff", ""
|
||||||
0,
|
|
||||||
true,
|
|
||||||
"#000",
|
|
||||||
false,"#000",
|
|
||||||
"#fff",""
|
|
||||||
),
|
),
|
||||||
|
|
||||||
new Button(
|
new Button(
|
||||||
100,350,
|
100, 350, 500, 100, 0, true, "#000", false,
|
||||||
500,100,
|
"#000", "#fff", ""
|
||||||
0,
|
|
||||||
true,
|
|
||||||
"#000",
|
|
||||||
false,"#000",
|
|
||||||
"#fff",""
|
|
||||||
),
|
),
|
||||||
|
|
||||||
new Button(
|
new Button(
|
||||||
700,300,
|
700, 300, 100, 50, 0, true, "#000", false,
|
||||||
100,50,
|
"#000", "#00ff00", "Login"
|
||||||
0,
|
|
||||||
true,
|
|
||||||
"#000",
|
|
||||||
false,"#000",
|
|
||||||
"#00ff00","Login"
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
this.menu = new Menu();
|
this.menu = new Menu();
|
||||||
|
|
||||||
this.activeTextBox = 0
|
|
||||||
// keeps track of which textbox the user last clicked on
|
// keeps track of which textbox the user last clicked on
|
||||||
|
this.activeTextBox = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides the user with their profilescreen, where they can see their own tests
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - change button name
|
||||||
|
* - provide filters for tests
|
||||||
|
* - implement a way to scroll through tests
|
||||||
|
* - create a way to have personal bests and track them
|
||||||
|
* - store tests in localstorage.
|
||||||
|
* - show user tests even if they are not logged in
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class displays all of the test data for a given user
|
||||||
|
*/
|
||||||
class ProfileScreen {
|
class ProfileScreen {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.menu = new Menu();
|
this.menu = new Menu();
|
||||||
|
@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides the screen manager class, with the necassary code to switch between screen classes
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - implement transitions between screens in a more fluid way
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides the ScreenManager class stores the current screen
|
||||||
|
* and provides the getters and setters necessary to switch between screen classes
|
||||||
|
* easily
|
||||||
|
*/
|
||||||
class ScreenManager {
|
class ScreenManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.textbox;
|
this.textbox;
|
||||||
|
26
website/screens/settingsScreen.js
Normal file
26
website/screens/settingsScreen.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides a screen where the user can edit the settings of their tests
|
||||||
|
* @author Arlo Filley
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides all of the necessary settings for the user to be able to edit test settings
|
||||||
|
*/
|
||||||
|
class settingsScreen {
|
||||||
|
constructor() {
|
||||||
|
this.menu = new Menu();
|
||||||
|
this.timeMenu = new TimeMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
textAlign(CENTER, CENTER);
|
||||||
|
background("#eeeee4");
|
||||||
|
|
||||||
|
textSize(100);
|
||||||
|
fill("#000");
|
||||||
|
text("Test Settings", 450, 100);
|
||||||
|
|
||||||
|
this.menu.draw();
|
||||||
|
this.timeMenu.draw();
|
||||||
|
}
|
||||||
|
}
|
@ -1,58 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides a way for the user to sign up for an account
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - move into an seperated account page with signup and logout
|
||||||
|
* - make passwords not display plain text
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides the textboxes and methods necessary for a user
|
||||||
|
* to sign up for a new account, which it should then log them into
|
||||||
|
*/
|
||||||
class SignUpScreen {
|
class SignUpScreen {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.textboxes = [
|
this.textboxes = [
|
||||||
new Textbox(
|
new Textbox(
|
||||||
120,250,
|
120, 250, 500, 100,0, true, "#000", false,
|
||||||
500,100,
|
"#000", "#000", true
|
||||||
0,
|
|
||||||
true,
|
|
||||||
"#000",
|
|
||||||
false, "#000",
|
|
||||||
"#000",
|
|
||||||
true
|
|
||||||
),
|
),
|
||||||
|
|
||||||
new Textbox(
|
new Textbox(
|
||||||
120,400,
|
120, 400, 500, 100, 0, true, "#000", false,
|
||||||
500,100,
|
"000", "#000", false
|
||||||
0,
|
|
||||||
true,
|
|
||||||
"#000",
|
|
||||||
false,"000",
|
|
||||||
"#000",
|
|
||||||
false
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
this.buttons = [
|
this.buttons = [
|
||||||
new Button(
|
new Button(
|
||||||
100,200,
|
100, 200, 500, 100, 0, true, "#000", false,
|
||||||
500,100,
|
"#000", "#fff", ""
|
||||||
0,
|
|
||||||
true,
|
|
||||||
"#000",
|
|
||||||
false,"#000",
|
|
||||||
"#fff",""
|
|
||||||
),
|
),
|
||||||
|
|
||||||
new Button(
|
new Button(
|
||||||
100,350,
|
100, 350, 500, 100, 0, true, "#000", false,
|
||||||
500,100,
|
"#000", "#fff", ""
|
||||||
0,
|
|
||||||
true,
|
|
||||||
"#000",
|
|
||||||
false,"#000",
|
|
||||||
"#fff",""
|
|
||||||
),
|
),
|
||||||
|
|
||||||
new Button(
|
new Button(
|
||||||
700,300,
|
700, 300, 100, 50, 0, true, "#000", false,
|
||||||
100,50,
|
"#000", "#00ff00", "Sign Up"
|
||||||
0,
|
|
||||||
true,
|
|
||||||
"#000",
|
|
||||||
false,"#000",
|
|
||||||
"#00ff00","Sign Up"
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file This file is the base screen when the user visits the site
|
||||||
|
* @author Arlo Filley
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This screen class is the base screen. It provides the user with basic instructions
|
||||||
|
* and a set of menus to navigate the site
|
||||||
|
*/
|
||||||
class StartScreen {
|
class StartScreen {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.menu = new Menu();
|
this.menu = new Menu();
|
||||||
|
@ -1,15 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides the functionality for the test
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - provide a button that allows the user to exit the test
|
||||||
|
* - provide a count down to the start of a test
|
||||||
|
* - implement menus to allow the user to control the parameters of a test
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this class displays the text of the test to the the screen and then takes input from the user
|
||||||
|
* displaying red if it is inaccurate, and green if it is
|
||||||
|
*/
|
||||||
class TestScreen {
|
class TestScreen {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.textbox = new Textbox(100,100,windowWidth - 200,windowHeight,0,true,"#000", false, "#000", "#000", true, true);
|
this.textbox = new Textbox(100,100,windowWidth - 200,windowHeight,0,true,"#000", false, "#000", "#000", true, true);
|
||||||
screenManager.timer = new Timer(0,0,windowWidth,50,0,true,"#fff", true, "#000", "#666", user.time, true);
|
this.timer = new Timer(0,0,windowWidth,50,0,true,"#fff", true, "#000", "#666", user.time, true);
|
||||||
screenManager.timer.start();
|
this.timer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
background("#eeeee4");
|
background("#eeeee4");
|
||||||
this.textbox.draw();
|
this.textbox.draw();
|
||||||
screenManager.timer.draw();
|
this.timer.draw();
|
||||||
screenManager.timer.tick();
|
this.timer.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
letterTyped(key) {
|
letterTyped(key) {
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides the button class, which can
|
||||||
|
* be checked for clicks
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - implement visual changes (borders, etc)
|
||||||
|
* - replace with methods with getters and setters
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button class, a rectangle that can be checked for mouse clicks
|
||||||
|
*/
|
||||||
class Button {
|
class Button {
|
||||||
// this is the doc comment for the Timer class
|
// this is the doc comment for the Timer class
|
||||||
/**
|
/**
|
||||||
@ -15,7 +29,16 @@ class Button {
|
|||||||
* @param {bool} pBar
|
* @param {bool} pBar
|
||||||
* @param {string} Label
|
* @param {string} Label
|
||||||
*/
|
*/
|
||||||
constructor(pX, pY, pWidth, pHeight, pLayer, pVisible, pTextColor, pBorder, pBorderColor, pBackgroundColor, pLabel) {
|
constructor(pX = 100, pY = 100,
|
||||||
|
pWidth = 200, pHeight = 30,
|
||||||
|
pLayer = 0, pVisible = true,
|
||||||
|
pTextColor = "#fff",
|
||||||
|
pBorder = false, pBorderColor = "#000",
|
||||||
|
pBackgroundColor = "#000",
|
||||||
|
pLabel = "Default Button",
|
||||||
|
pHoverBorder = true, pHoverBorderColor = "#000",
|
||||||
|
pHoverTextColor = "#000", pHoverBackgroundColor = "#00ff00"
|
||||||
|
) {
|
||||||
this.x = pX;
|
this.x = pX;
|
||||||
this.y = pY;
|
this.y = pY;
|
||||||
this.width = pWidth;
|
this.width = pWidth;
|
||||||
@ -27,9 +50,16 @@ class Button {
|
|||||||
this.borderColor = pBorderColor;
|
this.borderColor = pBorderColor;
|
||||||
this.backgroundColor = pBackgroundColor;
|
this.backgroundColor = pBackgroundColor;
|
||||||
this.label = pLabel;
|
this.label = pLabel;
|
||||||
|
|
||||||
|
// Attributes to control the look of the button
|
||||||
|
// when the user is hovering over it
|
||||||
|
this.hoverBorder = pHoverBorder;
|
||||||
|
this.hoverBorderColor = pHoverBorderColor;
|
||||||
|
this.hoverTextColor = pHoverTextColor;
|
||||||
|
this.hoverBackgroundColor = pHoverBackgroundColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
getX() {
|
getx() {
|
||||||
return this.x;
|
return this.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,11 +149,11 @@ class Button {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This functions returns more
|
* This functions returns more
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
isPressed() {
|
isPressed() {
|
||||||
if (!mouseIsPressed) {
|
if (!this.visible) {
|
||||||
// a unique p5.js value that checks if the mouse is clicked
|
return;
|
||||||
|
} else if (!mouseIsPressed) { // a unique p5.js value that checks if the mouse is clicked
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,12 +168,31 @@ class Button {
|
|||||||
* This function draws the button with the label
|
* This function draws the button with the label
|
||||||
*/
|
*/
|
||||||
draw() {
|
draw() {
|
||||||
|
if (!this.visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
textSize(20);
|
textSize(20);
|
||||||
fill(this.backgroundColor);
|
|
||||||
rect(this.x, this.y, this.width, this.height);
|
|
||||||
|
|
||||||
fill(this.textColor);
|
if (mouseX > this.x && mouseX < this.x + this.width && mouseY > this.y && mouseY < this.y + this.height) {
|
||||||
text(this.label, this.x, this.y, this.width, this.height);
|
|
||||||
// passing 4 arguments to this function means the text will wrap within this box
|
if (this.hoverBorder) {
|
||||||
|
strokeWeight(2);
|
||||||
|
stroke(this.hoverBorderColor)
|
||||||
|
} else {
|
||||||
|
noStroke();
|
||||||
|
}
|
||||||
|
fill(this.hoverBackgroundColor);
|
||||||
|
rect(this.x, this.y, this.width, this.height);
|
||||||
|
|
||||||
|
noStroke();
|
||||||
|
fill(this.hoverTextColor);
|
||||||
|
text(this.label, this.x, this.y, this.width, this.height);
|
||||||
|
} else {
|
||||||
|
fill(this.backgroundColor);
|
||||||
|
rect(this.x, this.y, this.width, this.height);
|
||||||
|
fill(this.textColor);
|
||||||
|
text(this.label, this.x, this.y, this.width, this.height);
|
||||||
|
}
|
||||||
|
noStroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,3 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides a canvas class wrapper for the p5.js canvas
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this class provides a wrapper around the
|
||||||
|
* p5.js canvas, with easier methods to work with.
|
||||||
|
*/
|
||||||
class Canvas {
|
class Canvas {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.x = 0;
|
this.x = 0;
|
||||||
|
@ -1,35 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides a menu class to allow the user to easily navigate the site
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - more sensible button names for easier navigation
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this class provides a menu with all the relevent buttons the user will need,
|
||||||
|
* it also handles when the user presses a button, by creating the correct screen
|
||||||
|
*/
|
||||||
class Menu {
|
class Menu {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.buttons = [
|
this.buttons = [
|
||||||
new Button(0,0,100,30,0,true,"#fff",false,"#000","#000","Sign Up"),
|
new Button(0, 0, 100, 30, 0, true, "#fff", false, "#000", "#000", "Account"),
|
||||||
new Button(110,0,100,30,0,true,"#fff",false,"#000","#000","Login"),
|
new Button(101, 0, 130, 30, 0, true, "#fff", false, "#000", "#000", "Test Data"),
|
||||||
new Button(220,0,100,30,0,true,"#fff",false,"#000","#000","Logout"),
|
new Button(232, 0, 140, 30, 0, true, "#fff", false, "#000", "#000", "Start Test"),
|
||||||
new Button(330,0,100,30,0,true,"#fff",false,"#000","#000","Profile"),
|
new Button(373, 0, 140, 30, 0, true, "#fff", false, "#000", "#000", "Leaderboard"),
|
||||||
new Button(440,0,100,30,0,true,"#fff",false,"#000","#000","Test"),
|
new Button(514, 0, 180, 30, 0, true, "#fff", false, "#000", "#000", "Test Settings")
|
||||||
new Button(550,0,140,30,0,true,"#fff",false,"#000","#000","Leaderboard"),
|
|
||||||
]
|
]
|
||||||
this.timeMenu = new TimeMenu();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
|
textAlign(CENTER, CENTER);
|
||||||
for (let i = 0; i < this.buttons.length; i++) {
|
for (let i = 0; i < this.buttons.length; i++) {
|
||||||
this.buttons[i].draw()
|
this.buttons[i].draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.buttons[0].isPressed()) {
|
if (this.buttons[0].isPressed()) {
|
||||||
screenManager.setScreen(new SignUpScreen());
|
screenManager.setScreen(new AccountScreen());
|
||||||
} else if (this.buttons[1].isPressed()) {
|
} else if (this.buttons[1].isPressed()) {
|
||||||
screenManager.setScreen(new LoginScreen());
|
|
||||||
} else if (this.buttons[2].isPressed()) {
|
|
||||||
api.logout();
|
|
||||||
} else if (this.buttons[3].isPressed()) {
|
|
||||||
screenManager.setScreen(new ProfileScreen());
|
screenManager.setScreen(new ProfileScreen());
|
||||||
} else if (this.buttons[4].isPressed()) {
|
} else if (this.buttons[2].isPressed()) {
|
||||||
screenManager.setScreen(new TestScreen())
|
screenManager.setScreen(new TestScreen())
|
||||||
} else if (this.buttons[5].isPressed()) {
|
} else if (this.buttons[3].isPressed()) {
|
||||||
screenManager.setScreen(new LeaderboardScreen())
|
screenManager.setScreen(new LeaderboardScreen())
|
||||||
|
} else if (this.buttons[4].isPressed()) {
|
||||||
|
screenManager.setScreen(new settingsScreen())
|
||||||
}
|
}
|
||||||
|
|
||||||
this.timeMenu.draw();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides the textbox class for taking user input
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - add all characters a user could press
|
||||||
|
* - refactor the code displaying the characters. It can become slow after lots of typing
|
||||||
|
* - password mode, where the charcters are hidden from the user
|
||||||
|
* - getters and setters
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class takes input from the user and displays it some form
|
||||||
|
* it handles the test input from the user, and the login and sign
|
||||||
|
* up pages.
|
||||||
|
*/
|
||||||
class Textbox {
|
class Textbox {
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of the Textbox class
|
* Creates a new instance of the Textbox class
|
||||||
@ -142,10 +158,6 @@ class Textbox {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
letterTyped(pKey) {
|
letterTyped(pKey) {
|
||||||
if (screenManager.screen.constructor.name === "TestScreen" && screenManager.timer.time === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pKey === "Backspace" && this.letters.length > 0) {
|
if (pKey === "Backspace" && this.letters.length > 0) {
|
||||||
this.letters.pop();
|
this.letters.pop();
|
||||||
this.words = this.words.substring(0, this.words.length-1)
|
this.words = this.words.substring(0, this.words.length-1)
|
||||||
|
@ -1,41 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides a time menu class for editing the length of a test
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - implement visual changes (borders, etc)
|
||||||
|
* - replace with methods with getters and setters
|
||||||
|
* - highlight which option the user has chosen in some way
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this class displays a dropdown menu for the user where
|
||||||
|
* they can edit the duration of a test
|
||||||
|
*/
|
||||||
class TimeMenu {
|
class TimeMenu {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.buttons = [
|
this.buttons = [
|
||||||
new Button(700,0,100,30,0,true,"#fff",false,"#000","#000","15s"),
|
new Button(100, 230, 100, 30, 0, true, "#fff", false, "#000", "#000", "15s"),
|
||||||
new Button(700,30,100,30,0,true,"#fff",false,"#000","#000","30s"),
|
new Button(100, 260, 100, 30, 0, true, "#fff", false, "#000", "#000", "30s"),
|
||||||
new Button(700,60,100,30,0,true,"#fff",false,"#000","#000","45s"),
|
new Button(100, 290, 100, 30, 0, true, "#fff", false, "#000", "#000", "45s"),
|
||||||
new Button(700,90,100,30,0,true,"#fff",false,"#000","#000","60s"),
|
new Button(100, 320, 100, 30, 0, true, "#fff", false, "#000", "#000", "60s"),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
this.topButton = this.buttons[0];
|
||||||
this.dropdown = false;
|
this.dropdown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
this.buttons[0].draw();
|
|
||||||
|
|
||||||
if (this.dropdown) {
|
if (this.dropdown) {
|
||||||
for (let i = 0; i < this.buttons.length; i++) {
|
for (let i = 0; i < this.buttons.length; i++) {
|
||||||
this.buttons[i].draw()
|
this.buttons[i].draw()
|
||||||
if (this.buttons[0].isPressed() && user.time != 15) {
|
|
||||||
user.time = 15;
|
|
||||||
this.dropdown = false;
|
|
||||||
} else if (this.buttons[1].isPressed()) {
|
|
||||||
user.time = 30;
|
|
||||||
this.dropdown = false;
|
|
||||||
} else if (this.buttons[2].isPressed()) {
|
|
||||||
user.time = 45;
|
|
||||||
this.dropdown = false;
|
|
||||||
} else if (this.buttons[3].isPressed()) {
|
|
||||||
user.time = 60;
|
|
||||||
this.dropdown = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (this.buttons[0].isPressed()) {
|
if (this.buttons[0].isPressed() && user.time != 15) {
|
||||||
|
user.time = 15;
|
||||||
|
this.topButton = new Button(100, 230, 100, 30, 0, true, "#fff", false, "#000", "#000", "15s");
|
||||||
|
this.dropdown = false;
|
||||||
|
} else if (this.buttons[1].isPressed()) {
|
||||||
|
user.time = 30;
|
||||||
|
this.topButton = new Button(100, 230, 100, 30, 0, true, "#fff", false, "#000", "#000", "30s");
|
||||||
|
this.dropdown = false;
|
||||||
|
} else if (this.buttons[2].isPressed()) {
|
||||||
|
user.time = 45;
|
||||||
|
this.topButton = new Button(100, 230, 100, 30, 0, true, "#fff", false, "#000", "#000", "45s");
|
||||||
|
this.dropdown = false;
|
||||||
|
} else if (this.buttons[3].isPressed()) {
|
||||||
|
user.time = 60;
|
||||||
|
this.topButton = new Button(100, 230, 100, 30, 0, true, "#fff", false, "#000", "#000", "60s");
|
||||||
|
this.dropdown = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.topButton.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.topButton.isPressed()) {
|
||||||
this.dropdown = true;
|
this.dropdown = true;
|
||||||
} else if (mouseIsPressed) {
|
} else if (mouseIsPressed) {
|
||||||
this.dropdown = false;
|
this.dropdown = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @file This file provides a time menu class for editing the length of a test
|
||||||
|
* @author Arlo Filley
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - implement visual changes (borders, etc)
|
||||||
|
* - fix the timer number becoming invisible after a
|
||||||
|
* it drops below a certain amount of time
|
||||||
|
* - use getters and setters
|
||||||
|
* - use the millis() p5.js function for if the framerate becomes
|
||||||
|
* slowed down by the amount being drawn to the screen
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides the timer, which handles when a test starts and ends as well
|
||||||
|
* as providing a visual element for the user to see
|
||||||
|
*/
|
||||||
class Timer {
|
class Timer {
|
||||||
// this is the doc comment for the Timer class
|
|
||||||
/**
|
/**
|
||||||
* @param {int} pX
|
* @param {int} pX
|
||||||
* @param {int} pY
|
* @param {int} pY
|
||||||
|
Loading…
Reference in New Issue
Block a user