From da6d01b258e0ab46a32d89cfb1f7dfb38aa597fa Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 23 May 2023 15:31:04 -0400 Subject: [PATCH 01/21] removed Vagrant stuff --- Vagrantfile | 10 ---------- vagrant/as_user.sh | 8 -------- vagrant/provision.sh | 10 ---------- 3 files changed, 28 deletions(-) delete mode 100644 Vagrantfile delete mode 100755 vagrant/as_user.sh delete mode 100755 vagrant/provision.sh diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index 1f739c9..0000000 --- a/Vagrantfile +++ /dev/null @@ -1,10 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -Vagrant.configure("2") do |config| - config.vm.box = "ubuntu/xenial64" - config.vm.provision "shell", path: "vagrant/provision.sh" - config.vm.provider :virtualbox do |v| - v.customize ["modifyvm", :id, "--memory", 4096] - end -end diff --git a/vagrant/as_user.sh b/vagrant/as_user.sh deleted file mode 100755 index ab1b3c8..0000000 --- a/vagrant/as_user.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -echo "export PATH=\"$HOME/.local/bin:$PATH\"" >>~vagrant/.bashrc -mkdir /vagrant/data -ln -s /vagrant/data ~vagrant/.mtlstats - -cd /vagrant -stack install diff --git a/vagrant/provision.sh b/vagrant/provision.sh deleted file mode 100755 index bb2fffd..0000000 --- a/vagrant/provision.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -apt-get update -apt-get upgrade -apt-get -y install libghc-ncurses-dev -wget -qO- https://get.haskellstack.org/ | sh - -export HOME=/home/vagrant - -sudo -u vagrant /vagrant/vagrant/as_user.sh From 855854cd4270d91d52af6701af7af0ded84690db Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 23 May 2023 17:22:14 -0400 Subject: [PATCH 02/21] updated copyright notice --- app/Main.hs | 2 +- package.yaml | 5 ++--- src/Mtlstats.hs | 2 +- src/Mtlstats/Actions.hs | 2 +- src/Mtlstats/Actions/EditStandings.hs | 2 +- src/Mtlstats/Actions/NewGame.hs | 2 +- src/Mtlstats/Actions/NewGame/GoalieInput.hs | 2 +- src/Mtlstats/Config.hs | 2 +- src/Mtlstats/Control.hs | 2 +- src/Mtlstats/Control/CreateGoalie.hs | 2 +- src/Mtlstats/Control/CreatePlayer.hs | 2 +- src/Mtlstats/Control/EditGoalie.hs | 2 +- src/Mtlstats/Control/EditPlayer.hs | 2 +- src/Mtlstats/Control/EditStandings.hs | 2 +- src/Mtlstats/Control/NewGame.hs | 2 +- src/Mtlstats/Control/NewGame/GoalieInput.hs | 2 +- src/Mtlstats/Control/TitleScreen.hs | 2 +- src/Mtlstats/Format.hs | 2 +- src/Mtlstats/Handlers.hs | 2 +- src/Mtlstats/Helpers/Goalie.hs | 2 +- src/Mtlstats/Helpers/Player.hs | 2 +- src/Mtlstats/Helpers/Position.hs | 2 +- src/Mtlstats/Menu.hs | 2 +- src/Mtlstats/Menu/EditGoalie.hs | 2 +- src/Mtlstats/Menu/EditPlayer.hs | 2 +- src/Mtlstats/Menu/EditStandings.hs | 2 +- src/Mtlstats/Prompt.hs | 2 +- src/Mtlstats/Prompt/EditGoalie.hs | 2 +- src/Mtlstats/Prompt/EditPlayer.hs | 2 +- src/Mtlstats/Prompt/EditStandings.hs | 2 +- src/Mtlstats/Prompt/NewGame.hs | 2 +- src/Mtlstats/Prompt/NewGame/GoalieInput.hs | 2 +- src/Mtlstats/Report.hs | 2 +- src/Mtlstats/Types.hs | 2 +- src/Mtlstats/Types/Menu.hs | 2 +- src/Mtlstats/Util.hs | 2 +- stack.yaml.lock | 19 ------------------- test/Actions/EditStandingsSpec.hs | 2 +- test/Actions/NewGame/GoalieInputSpec.hs | 2 +- test/Actions/NewGameSpec.hs | 2 +- test/ActionsSpec.hs | 2 +- test/FormatSpec.hs | 2 +- test/HandlersSpec.hs | 2 +- test/Helpers/GoalieSpec.hs | 2 +- test/Helpers/PlayerSpec.hs | 2 +- test/Helpers/PositionSpec.hs | 2 +- test/HelpersSpec.hs | 2 +- test/ReportSpec.hs | 2 +- test/Spec.hs | 2 +- test/Types/MenuSpec.hs | 2 +- test/TypesSpec.hs | 2 +- test/UtilSpec.hs | 2 +- 52 files changed, 52 insertions(+), 72 deletions(-) delete mode 100644 stack.yaml.lock diff --git a/app/Main.hs b/app/Main.hs index 8ef98b3..3716eb3 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/package.yaml b/package.yaml index afa1d36..e2b8c24 100644 --- a/package.yaml +++ b/package.yaml @@ -1,10 +1,9 @@ name: mtlstats version: 0.16.1 -github: "mtlstats/mtlstats" -license: GPL-3 +license: GPL-3.0-or-later author: "Jonathan Lamothe" maintainer: "jlamothe1980@gmail.com" -copyright: "Rhéal Lamothe" +copyright: "1984, 1985, 2019-2021, 2023 Rhéal Lamothe" extra-source-files: - README.md diff --git a/src/Mtlstats.hs b/src/Mtlstats.hs index eb796cb..13fead6 100644 --- a/src/Mtlstats.hs +++ b/src/Mtlstats.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Actions.hs b/src/Mtlstats/Actions.hs index ab6b0f6..019bb0d 100644 --- a/src/Mtlstats/Actions.hs +++ b/src/Mtlstats/Actions.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Actions/EditStandings.hs b/src/Mtlstats/Actions/EditStandings.hs index c936609..6e9d889 100644 --- a/src/Mtlstats/Actions/EditStandings.hs +++ b/src/Mtlstats/Actions/EditStandings.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Actions/NewGame.hs b/src/Mtlstats/Actions/NewGame.hs index 44d72b1..27eb790 100644 --- a/src/Mtlstats/Actions/NewGame.hs +++ b/src/Mtlstats/Actions/NewGame.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Actions/NewGame/GoalieInput.hs b/src/Mtlstats/Actions/NewGame/GoalieInput.hs index 6b4df22..3a84ba9 100644 --- a/src/Mtlstats/Actions/NewGame/GoalieInput.hs +++ b/src/Mtlstats/Actions/NewGame/GoalieInput.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Config.hs b/src/Mtlstats/Config.hs index 388d836..11f82e4 100644 --- a/src/Mtlstats/Config.hs +++ b/src/Mtlstats/Config.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Control.hs b/src/Mtlstats/Control.hs index badb7e1..abac5d2 100644 --- a/src/Mtlstats/Control.hs +++ b/src/Mtlstats/Control.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Control/CreateGoalie.hs b/src/Mtlstats/Control/CreateGoalie.hs index 27b0670..60866a6 100644 --- a/src/Mtlstats/Control/CreateGoalie.hs +++ b/src/Mtlstats/Control/CreateGoalie.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Control/CreatePlayer.hs b/src/Mtlstats/Control/CreatePlayer.hs index 0bc07d8..14772ca 100644 --- a/src/Mtlstats/Control/CreatePlayer.hs +++ b/src/Mtlstats/Control/CreatePlayer.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Control/EditGoalie.hs b/src/Mtlstats/Control/EditGoalie.hs index 87d75e7..ecd8084 100644 --- a/src/Mtlstats/Control/EditGoalie.hs +++ b/src/Mtlstats/Control/EditGoalie.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Control/EditPlayer.hs b/src/Mtlstats/Control/EditPlayer.hs index ed869b2..6d1bd47 100644 --- a/src/Mtlstats/Control/EditPlayer.hs +++ b/src/Mtlstats/Control/EditPlayer.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Control/EditStandings.hs b/src/Mtlstats/Control/EditStandings.hs index 22c41f1..85201e3 100644 --- a/src/Mtlstats/Control/EditStandings.hs +++ b/src/Mtlstats/Control/EditStandings.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Control/NewGame.hs b/src/Mtlstats/Control/NewGame.hs index c596b32..77080bc 100644 --- a/src/Mtlstats/Control/NewGame.hs +++ b/src/Mtlstats/Control/NewGame.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Control/NewGame/GoalieInput.hs b/src/Mtlstats/Control/NewGame/GoalieInput.hs index 938eb06..d436b5b 100644 --- a/src/Mtlstats/Control/NewGame/GoalieInput.hs +++ b/src/Mtlstats/Control/NewGame/GoalieInput.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Control/TitleScreen.hs b/src/Mtlstats/Control/TitleScreen.hs index 611b26e..db315d8 100644 --- a/src/Mtlstats/Control/TitleScreen.hs +++ b/src/Mtlstats/Control/TitleScreen.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Format.hs b/src/Mtlstats/Format.hs index 5fcec7e..e063a4f 100644 --- a/src/Mtlstats/Format.hs +++ b/src/Mtlstats/Format.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Handlers.hs b/src/Mtlstats/Handlers.hs index fa79b62..0d70cbc 100644 --- a/src/Mtlstats/Handlers.hs +++ b/src/Mtlstats/Handlers.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Helpers/Goalie.hs b/src/Mtlstats/Helpers/Goalie.hs index ab0b7c3..be01588 100644 --- a/src/Mtlstats/Helpers/Goalie.hs +++ b/src/Mtlstats/Helpers/Goalie.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Helpers/Player.hs b/src/Mtlstats/Helpers/Player.hs index 408fb65..6ac7caf 100644 --- a/src/Mtlstats/Helpers/Player.hs +++ b/src/Mtlstats/Helpers/Player.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Helpers/Position.hs b/src/Mtlstats/Helpers/Position.hs index 4822a54..08ca69b 100644 --- a/src/Mtlstats/Helpers/Position.hs +++ b/src/Mtlstats/Helpers/Position.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Menu.hs b/src/Mtlstats/Menu.hs index 2ae5cc4..a0ca403 100644 --- a/src/Mtlstats/Menu.hs +++ b/src/Mtlstats/Menu.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Menu/EditGoalie.hs b/src/Mtlstats/Menu/EditGoalie.hs index f84482f..838c799 100644 --- a/src/Mtlstats/Menu/EditGoalie.hs +++ b/src/Mtlstats/Menu/EditGoalie.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Menu/EditPlayer.hs b/src/Mtlstats/Menu/EditPlayer.hs index 9773624..0363d47 100644 --- a/src/Mtlstats/Menu/EditPlayer.hs +++ b/src/Mtlstats/Menu/EditPlayer.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Menu/EditStandings.hs b/src/Mtlstats/Menu/EditStandings.hs index e3e9fff..328549c 100644 --- a/src/Mtlstats/Menu/EditStandings.hs +++ b/src/Mtlstats/Menu/EditStandings.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 14661e0..0ca366e 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Prompt/EditGoalie.hs b/src/Mtlstats/Prompt/EditGoalie.hs index ae16ab4..fdf5835 100644 --- a/src/Mtlstats/Prompt/EditGoalie.hs +++ b/src/Mtlstats/Prompt/EditGoalie.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Prompt/EditPlayer.hs b/src/Mtlstats/Prompt/EditPlayer.hs index b064723..1191764 100644 --- a/src/Mtlstats/Prompt/EditPlayer.hs +++ b/src/Mtlstats/Prompt/EditPlayer.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Prompt/EditStandings.hs b/src/Mtlstats/Prompt/EditStandings.hs index 01b0cb9..6a67702 100644 --- a/src/Mtlstats/Prompt/EditStandings.hs +++ b/src/Mtlstats/Prompt/EditStandings.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Prompt/NewGame.hs b/src/Mtlstats/Prompt/NewGame.hs index a26240e..478756d 100644 --- a/src/Mtlstats/Prompt/NewGame.hs +++ b/src/Mtlstats/Prompt/NewGame.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Prompt/NewGame/GoalieInput.hs b/src/Mtlstats/Prompt/NewGame/GoalieInput.hs index d2c6582..19ea0f2 100644 --- a/src/Mtlstats/Prompt/NewGame/GoalieInput.hs +++ b/src/Mtlstats/Prompt/NewGame/GoalieInput.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Report.hs b/src/Mtlstats/Report.hs index 8ea8d41..a7558fa 100644 --- a/src/Mtlstats/Report.hs +++ b/src/Mtlstats/Report.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index 2afe782..a810f6e 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Types/Menu.hs b/src/Mtlstats/Types/Menu.hs index ea8a958..a791845 100644 --- a/src/Mtlstats/Types/Menu.hs +++ b/src/Mtlstats/Types/Menu.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/src/Mtlstats/Util.hs b/src/Mtlstats/Util.hs index 1edb1de..6616a0f 100644 --- a/src/Mtlstats/Util.hs +++ b/src/Mtlstats/Util.hs @@ -1,7 +1,7 @@ {- | mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/stack.yaml.lock b/stack.yaml.lock deleted file mode 100644 index a782f19..0000000 --- a/stack.yaml.lock +++ /dev/null @@ -1,19 +0,0 @@ -# This file was autogenerated by Stack. -# You should not edit this file by hand. -# For more information, please see the documentation at: -# https://docs.haskellstack.org/en/stable/lock_files - -packages: -- completed: - hackage: ncurses-0.2.16@sha256:8ad9fe6562a80d28166d76adbac1eb4d40c6511fe4e9272ed6e1166dc2f1cdf1,3575 - pantry-tree: - size: 674 - sha256: 093bdc85ed518c81724f5b6b81c24ab4ebdd231551861f4feaa43361136f70b7 - original: - hackage: ncurses-0.2.16@sha256:8ad9fe6562a80d28166d76adbac1eb4d40c6511fe4e9272ed6e1166dc2f1cdf1,3575 -snapshots: -- completed: - size: 523443 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/14/0.yaml - sha256: 283773e7120f5446d961eab35ea95c9af9c24187cc178537bd29273200a05171 - original: lts-14.0 diff --git a/test/Actions/EditStandingsSpec.hs b/test/Actions/EditStandingsSpec.hs index 23f1986..1126cb3 100644 --- a/test/Actions/EditStandingsSpec.hs +++ b/test/Actions/EditStandingsSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/Actions/NewGame/GoalieInputSpec.hs b/test/Actions/NewGame/GoalieInputSpec.hs index 3e2fe8f..1b0137a 100644 --- a/test/Actions/NewGame/GoalieInputSpec.hs +++ b/test/Actions/NewGame/GoalieInputSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/Actions/NewGameSpec.hs b/test/Actions/NewGameSpec.hs index ed343af..49caaa9 100644 --- a/test/Actions/NewGameSpec.hs +++ b/test/Actions/NewGameSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/ActionsSpec.hs b/test/ActionsSpec.hs index 5fd9884..afad41d 100644 --- a/test/ActionsSpec.hs +++ b/test/ActionsSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/FormatSpec.hs b/test/FormatSpec.hs index 7db175c..7ed22d0 100644 --- a/test/FormatSpec.hs +++ b/test/FormatSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/HandlersSpec.hs b/test/HandlersSpec.hs index 71aa355..625ba28 100644 --- a/test/HandlersSpec.hs +++ b/test/HandlersSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/Helpers/GoalieSpec.hs b/test/Helpers/GoalieSpec.hs index 56ab5c8..b1f24c6 100644 --- a/test/Helpers/GoalieSpec.hs +++ b/test/Helpers/GoalieSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/Helpers/PlayerSpec.hs b/test/Helpers/PlayerSpec.hs index a536856..ef6caa1 100644 --- a/test/Helpers/PlayerSpec.hs +++ b/test/Helpers/PlayerSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/Helpers/PositionSpec.hs b/test/Helpers/PositionSpec.hs index 2d04b1d..08cd129 100644 --- a/test/Helpers/PositionSpec.hs +++ b/test/Helpers/PositionSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/HelpersSpec.hs b/test/HelpersSpec.hs index 86a9e66..97f74a4 100644 --- a/test/HelpersSpec.hs +++ b/test/HelpersSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/ReportSpec.hs b/test/ReportSpec.hs index 7d7f6f5..00a2766 100644 --- a/test/ReportSpec.hs +++ b/test/ReportSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/Spec.hs b/test/Spec.hs index e4f98a7..17f6d30 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/Types/MenuSpec.hs b/test/Types/MenuSpec.hs index c5002b6..62dfbcd 100644 --- a/test/Types/MenuSpec.hs +++ b/test/Types/MenuSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index 4b45181..45b5331 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify diff --git a/test/UtilSpec.hs b/test/UtilSpec.hs index 8acfb92..3c73564 100644 --- a/test/UtilSpec.hs +++ b/test/UtilSpec.hs @@ -1,7 +1,7 @@ {- mtlstats -Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe +Copyright (C) Rhéal Lamothe This program is free software: you can redistribute it and/or modify From 458554bef2741eaf4c8c1ef3d651e1d7fb839199 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 23 May 2023 17:37:38 -0400 Subject: [PATCH 03/21] updated copyright notice on title screen --- src/Mtlstats/Control/TitleScreen.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mtlstats/Control/TitleScreen.hs b/src/Mtlstats/Control/TitleScreen.hs index db315d8..c535aab 100644 --- a/src/Mtlstats/Control/TitleScreen.hs +++ b/src/Mtlstats/Control/TitleScreen.hs @@ -41,7 +41,7 @@ titleScreenC = Controller ] ++ titleText ++ [ "" - , "Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe" + , "Copyright (C) 1984, 1985, 2019-2021, 2023 Rhéal Lamothe" , "" , "" , "Press any key to continue..." From 227401461b99cbca5ab4a315eae3e51dc31a088f Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 25 May 2023 19:36:03 -0400 Subject: [PATCH 04/21] wip: switching from ncurses to brick --- app/Main.hs | 7 +- package.yaml | 20 ++- src/Mtlstats.hs | 54 +++--- src/Mtlstats/Actions.hs | 26 +-- src/Mtlstats/Control/CreateGoalie.hs | 28 ++- src/Mtlstats/Control/CreatePlayer.hs | 26 +-- src/Mtlstats/Control/EditGoalie.hs | 58 +++--- src/Mtlstats/Control/EditPlayer.hs | 58 +++--- src/Mtlstats/Control/EditStandings.hs | 18 +- src/Mtlstats/Control/NewGame.hs | 185 ++++++++++---------- src/Mtlstats/Control/NewGame/GoalieInput.hs | 9 +- src/Mtlstats/Control/TitleScreen.hs | 40 ++--- src/Mtlstats/Handlers.hs | 7 +- src/Mtlstats/Helpers/Position.hs | 4 +- src/Mtlstats/Menu.hs | 52 +++--- src/Mtlstats/Menu/EditGoalie.hs | 2 +- src/Mtlstats/Menu/EditPlayer.hs | 2 +- src/Mtlstats/Menu/EditStandings.hs | 2 +- src/Mtlstats/Prompt.hs | 101 ++++++----- src/Mtlstats/Prompt/EditGoalie.hs | 2 +- src/Mtlstats/Prompt/EditPlayer.hs | 2 +- src/Mtlstats/Prompt/EditStandings.hs | 2 +- src/Mtlstats/Prompt/NewGame.hs | 2 +- src/Mtlstats/Prompt/NewGame/GoalieInput.hs | 2 +- src/Mtlstats/Types.hs | 41 +++-- src/Mtlstats/Util.hs | 10 +- stack.yaml | 2 +- stack.yaml.lock | 19 ++ test/Actions/NewGame/GoalieInputSpec.hs | 6 +- test/ActionsSpec.hs | 33 ---- test/HandlersSpec.hs | 24 ++- test/TypesSpec.hs | 2 +- 32 files changed, 400 insertions(+), 446 deletions(-) create mode 100644 stack.yaml.lock diff --git a/app/Main.hs b/app/Main.hs index 3716eb3..23e8e58 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -21,10 +21,11 @@ along with this program. If not, see . module Main where -import Control.Monad.Trans.State (evalStateT) -import UI.NCurses (runCurses) +import Brick.Main (defaultMain) +import Control.Monad (void) import Mtlstats +import Mtlstats.Types main :: IO () -main = runCurses $ initState >>= evalStateT mainLoop +main = void $ defaultMain app newProgState diff --git a/package.yaml b/package.yaml index e2b8c24..ee7b350 100644 --- a/package.yaml +++ b/package.yaml @@ -20,17 +20,19 @@ description: Please see the README on GitHub at = 4.7 && < 5 -- aeson >= 1.4.4.0 && < 1.5 +- aeson >= 2.0.3.0 && < 2.1 +- bytestring >= 0.11.4.0 && < 0.12 +- brick >= 1.4 && < 1.5 - containers >= 0.6.0.1 && < 0.7 - easy-file >= 0.2.2 && < 0.3 -- extra >= 1.6.17 && < 1.7 +- extra >= 1.7.13 && < 1.8 +- microlens >= 0.4.12.0 && < 0.5 +- microlens-mtl >= 0.2.0.3 && < 0.3 - microlens-th >= 0.4.2.3 && < 0.5 -- ncurses >= 0.2.16 && < 0.3 -- random >= 1.1 && < 1.2 -- time >= 1.8.0.2 && < 1.9 -- transformers >= 0.5.6.2 && < 0.6 -- bytestring -- microlens +- mtl >= 2.2.2 && < 2.3 +- random >= 1.2.1.1 && < 1.3 +- time >= 1.11.1.1 && < 1.12 +- vty >= 5.37 && < 5.38 ghc-options: - -Wall @@ -60,5 +62,5 @@ tests: - -with-rtsopts=-N dependencies: - mtlstats - - hspec >= 2.7.1 && < 2.8 + - hspec >= 2.9.7 && < 2.10 - unordered-containers diff --git a/src/Mtlstats.hs b/src/Mtlstats.hs index 13fead6..b6e6b02 100644 --- a/src/Mtlstats.hs +++ b/src/Mtlstats.hs @@ -19,40 +19,34 @@ along with this program. If not, see . -} -module Mtlstats (initState, mainLoop) where +module Mtlstats (app) where -import Control.Monad (void) -import Control.Monad.Extra (whenM) -import Control.Monad.Trans.Class (lift) -import Control.Monad.Trans.State (get, gets) -import Data.Maybe (fromJust) -import qualified UI.NCurses as C +import Brick.AttrMap (AttrMap, forceAttrMap) +import Brick.Main (App (..), showFirstCursor) +import Brick.Util (on) +import Graphics.Vty.Attributes.Color (blue, white) +import Lens.Micro (to) +import Lens.Micro.Mtl (use) import Mtlstats.Control import Mtlstats.Types --- | Initializes the progran -initState :: C.Curses ProgState -initState = do - C.setEcho False - void $ C.setCursorMode C.CursorInvisible - return newProgState +-- | The main application +app :: App ProgState () () +app = App + { appDraw = \s -> [drawController (dispatch s) s] + , appChooseCursor = showFirstCursor + , appHandleEvent = handler + , appStartEvent = return () + , appAttrMap = const myAttrMap + } --- | Main program loop -mainLoop :: Action () -mainLoop = do - c <- gets dispatch - get >>= lift . draw . drawController c - w <- lift C.defaultWindow - whenM (lift (fromJust <$> C.getEvent w Nothing) >>= handleController c) - mainLoop +handler :: Handler () +handler e = do + c <- use (to dispatch) + handleController c e -draw :: C.Update C.CursorMode -> C.Curses () -draw u = do - void $ C.setCursorMode C.CursorInvisible - w <- C.defaultWindow - cm <- C.updateWindow w $ do - C.clear - u - C.render - void $ C.setCursorMode cm +myAttrMap :: AttrMap +myAttrMap = forceAttrMap (white `on` blue) + +--jl diff --git a/src/Mtlstats/Actions.hs b/src/Mtlstats/Actions.hs index 019bb0d..00dfd13 100644 --- a/src/Mtlstats/Actions.hs +++ b/src/Mtlstats/Actions.hs @@ -41,18 +41,18 @@ module Mtlstats.Actions , resetCreatePlayerState , resetCreateGoalieState , backHome - , scrollUp - , scrollDown , loadDatabase , saveDatabase ) where +import Brick.Main (viewportScroll) import Control.Exception (IOException, catch) import Control.Monad.IO.Class (liftIO) -import Control.Monad.Trans.State (gets, modify) +import Control.Monad.State.Class (modify) import Data.Aeson (decodeFileStrict, encodeFile) import Data.Maybe (fromMaybe) import Lens.Micro ((^.), (&), (.~), (%~)) +import Lens.Micro.Mtl ((.=), use) import System.EasyFile ( createDirectoryIfMissing , getAppUserDataDirectory @@ -206,17 +206,9 @@ resetCreateGoalieState = progMode.createGoalieStateL -- | Resets the program state back to the main menu backHome :: ProgState -> ProgState backHome - = (progMode .~ MainMenu) - . (inputBuffer .~ "") - . (scrollOffset .~ 0) - --- | Scrolls the display up -scrollUp :: ProgState -> ProgState -scrollUp = scrollOffset %~ max 0 . pred - --- | Scrolls the display down -scrollDown :: ProgState -> ProgState -scrollDown = scrollOffset %~ succ + = (progMode .~ MainMenu) + . (inputBuffer .~ "") + . (scroller .~ viewportScroll ()) -- | Loads the database loadDatabase :: Action () @@ -226,18 +218,18 @@ loadDatabase = do (catch (decodeFileStrict dbFile) (\(_ :: IOException) -> return Nothing)) - >>= mapM_ (modify . (database .~)) + >>= mapM_ (database .=) -- | Saves the database saveDatabase :: Action () saveDatabase = do - db <- gets (^.database) + db <- use database dbFile <- dbSetup liftIO $ encodeFile dbFile db dbSetup :: Action String dbSetup = do - fn <- gets (^.dbName) + fn <- use dbName liftIO $ do dir <- getAppUserDataDirectory appName createDirectoryIfMissing True dir diff --git a/src/Mtlstats/Control/CreateGoalie.hs b/src/Mtlstats/Control/CreateGoalie.hs index 60866a6..9a5852e 100644 --- a/src/Mtlstats/Control/CreateGoalie.hs +++ b/src/Mtlstats/Control/CreateGoalie.hs @@ -21,15 +21,16 @@ along with this program. If not, see . module Mtlstats.Control.CreateGoalie (createGoalieC) where -import Control.Monad.Trans.State (gets, modify) +import Brick.Widgets.Core (str) +import Control.Monad.State.Class (gets, modify) import Lens.Micro ((^.), (.~), (?~), (%~), to) -import qualified UI.NCurses as C import Mtlstats.Actions import Mtlstats.Format import Mtlstats.Handlers import Mtlstats.Prompt import Mtlstats.Types +import Mtlstats.Util -- | Handles goalie creation createGoalieC :: CreateGoalieState -> Controller @@ -48,33 +49,28 @@ getGoalieNameC = promptController goalieNamePrompt getRookieFlagC :: Controller getRookieFlagC = Controller - { drawController = const $ do - C.drawString "Is this goalie a rookie? (Y/N)" - return C.CursorInvisible - , handleController = \e -> do + { drawController = const $ + str "Is this goalie a rookie? (Y/N)" + , handleController = \e -> modify $ case ynHandler e of Just True -> progMode.createGoalieStateL %~ (cgsRookieFlag ?~ True) . (cgsActiveFlag ?~ True) rf -> progMode.createGoalieStateL.cgsRookieFlag .~ rf - return True } getActiveFlagC :: Controller getActiveFlagC = Controller - { drawController = const $ do - C.drawString "Is this goalie active? (Y/N)" - return C.CursorInvisible - , handleController = \e -> do + { drawController = const $ str "Is this goalie active? (Y/N)" + , handleController = \e -> modify $ progMode.createGoalieStateL.cgsActiveFlag .~ ynHandler e - return True } confirmCreateGoalieC :: Controller confirmCreateGoalieC = Controller - { drawController = \s -> do - let cgs = s^.progMode.createGoalieStateL - C.drawString $ unlines + { drawController = \s -> let + cgs = s^.progMode.createGoalieStateL + in linesToWidget $ labelTable [ ( "Goalie number", maybe "?" show $ cgs^.cgsNumber ) , ( "Goalie name", cgs^.cgsName ) @@ -84,7 +80,6 @@ confirmCreateGoalieC = Controller ++ [ "" , "Create goalie: are you sure? (Y/N)" ] - return C.CursorInvisible , handleController = \e -> do cgs <- gets (^.progMode.createGoalieStateL) let @@ -103,5 +98,4 @@ confirmCreateGoalieC = Controller . (egsCallback .~ success) Just False -> failure Nothing -> return () - return True } diff --git a/src/Mtlstats/Control/CreatePlayer.hs b/src/Mtlstats/Control/CreatePlayer.hs index 14772ca..3f55751 100644 --- a/src/Mtlstats/Control/CreatePlayer.hs +++ b/src/Mtlstats/Control/CreatePlayer.hs @@ -21,15 +21,16 @@ along with this program. If not, see . module Mtlstats.Control.CreatePlayer (createPlayerC) where -import Control.Monad.Trans.State (gets, modify) +import Brick.Widgets.Core (str) +import Control.Monad.State.Class (gets, modify) import Lens.Micro ((^.), (.~), (?~), (%~), to) -import qualified UI.NCurses as C import Mtlstats.Actions import Mtlstats.Format import Mtlstats.Handlers import Mtlstats.Prompt import Mtlstats.Types +import Mtlstats.Util -- | Handles player creation createPlayerC :: CreatePlayerState -> Controller @@ -52,33 +53,26 @@ getPlayerPosC = promptController playerPosPrompt getRookieFlagC :: Controller getRookieFlagC = Controller - { drawController = const $ do - C.drawString "Is this player a rookie? (Y/N)" - return C.CursorInvisible - , handleController = \e -> do + { drawController = const $ str "Is this player a rookie? (Y/N)" + , handleController = \e -> modify $ case ynHandler e of Just True -> progMode.createPlayerStateL %~ (cpsRookieFlag ?~ True) . (cpsActiveFlag ?~ True) rf -> progMode.createPlayerStateL.cpsRookieFlag .~ rf - return True } getActiveFlagC :: Controller getActiveFlagC = Controller - { drawController = const $ do - C.drawString "Is the player active? (Y/N)" - return C.CursorInvisible - , handleController = \e -> do + { drawController = const $ str "Is the player active? (Y/N)" + , handleController = \e -> modify $ progMode.createPlayerStateL.cpsActiveFlag .~ ynHandler e - return True } confirmCreatePlayerC :: Controller confirmCreatePlayerC = Controller - { drawController = \s -> do - let cps = s^.progMode.createPlayerStateL - C.drawString $ unlines + { drawController = \s -> let cps = s^.progMode.createPlayerStateL + in linesToWidget $ labelTable [ ( "Player number", maybe "?" show $ cps^.cpsNumber ) , ( "Player name", cps^.cpsName ) @@ -89,7 +83,6 @@ confirmCreatePlayerC = Controller ++ [ "" , "Create player: are you sure? (Y/N)" ] - return C.CursorInvisible , handleController = \e -> do cps <- gets (^.progMode.createPlayerStateL) let @@ -108,5 +101,4 @@ confirmCreatePlayerC = Controller . (epsCallback .~ success) Just False -> failure Nothing -> return () - return True } diff --git a/src/Mtlstats/Control/EditGoalie.hs b/src/Mtlstats/Control/EditGoalie.hs index ecd8084..179db42 100644 --- a/src/Mtlstats/Control/EditGoalie.hs +++ b/src/Mtlstats/Control/EditGoalie.hs @@ -23,10 +23,11 @@ along with this program. If not, see . module Mtlstats.Control.EditGoalie (editGoalieC) where -import Control.Monad.Trans.State (gets, modify) +import Brick.Types (Widget) +import Brick.Widgets.Core (str, vBox) +import Control.Monad.State.Class (gets, modify) import Data.Maybe (fromMaybe) import Lens.Micro ((^.), (.~), (%~)) -import UI.NCurses as C import Mtlstats.Actions import Mtlstats.Handlers @@ -90,33 +91,19 @@ lifetimeMenuC _ = menuControllerWith header editGoalieLtMenu deleteC :: Action () -> Controller deleteC _ = Controller - { drawController = \s -> do - - C.drawString $ let - - hdr = fromMaybe [] $ do - gid <- s^.progMode.editGoalieStateL.egsSelectedGoalie - goalie <- nth gid $ s^.database.dbGoalies - Just $ "Goalie: " ++ goalieDetails goalie ++ "\n\n" - - in hdr ++ "Are you sure you want to delete this goalie? (Y/N)" - - return C.CursorInvisible - - , handleController = \e -> do - - case ynHandler e of - - Just True -> do - gets (^.progMode.editGoalieStateL.egsSelectedGoalie) >>= mapM_ - (\gid -> modify $ database.dbGoalies %~ dropNth gid) - modify edit - - Just False -> modify $ progMode.editGoalieStateL.egsMode .~ EGMenu - Nothing -> return () - - return True - + { drawController = \s -> let + hdr = fromMaybe "" $ do + gid <- s^.progMode.editGoalieStateL.egsSelectedGoalie + goalie <- nth gid $ s^.database.dbGoalies + Just $ "Goalie: " ++ goalieDetails goalie ++ "\n\n" + in str $ hdr ++ "Are you sure you want to delete this goalie? (Y/N)" + , handleController = \e -> case ynHandler e of + Just True -> do + gets (^.progMode.editGoalieStateL.egsSelectedGoalie) >>= mapM_ + (\gid -> modify $ database.dbGoalies %~ dropNth gid) + modify edit + Just False -> modify $ progMode.editGoalieStateL.egsMode .~ EGMenu + Nothing -> return () } ytdGamesC :: Bool -> Action () -> Controller @@ -173,8 +160,11 @@ ltLossesC = curry $ promptController . ltTiesC :: Action () -> Controller ltTiesC = promptController . editGoalieLtTiesPrompt -header :: ProgState -> C.Update () -header s = C.drawString $ fromMaybe "" $ do - gid <- s^.progMode.editGoalieStateL.egsSelectedGoalie - g <- nth gid $ s^.database.dbGoalies - Just $ goalieDetails g ++ "\n" +header :: ProgState -> Widget () -> Widget () +header s w = vBox + [ str $ fromMaybe "" $ do + gid <- s^.progMode.editGoalieStateL.egsSelectedGoalie + g <- nth gid $ s^.database.dbGoalies + Just $ goalieDetails g + , w + ] diff --git a/src/Mtlstats/Control/EditPlayer.hs b/src/Mtlstats/Control/EditPlayer.hs index 6d1bd47..fb348b5 100644 --- a/src/Mtlstats/Control/EditPlayer.hs +++ b/src/Mtlstats/Control/EditPlayer.hs @@ -21,10 +21,11 @@ along with this program. If not, see . module Mtlstats.Control.EditPlayer (editPlayerC) where -import Control.Monad.Trans.State (gets, modify) +import Brick.Types (Widget) +import Brick.Widgets.Core (emptyWidget, str, vBox) +import Control.Monad.State.Class (gets, modify) import Data.Maybe (fromMaybe) import Lens.Micro ((^.), (.~), (%~)) -import qualified UI.NCurses as C import Mtlstats.Actions import Mtlstats.Handlers @@ -81,33 +82,19 @@ lifetimeC _ = menuControllerWith header editPlayerLtMenu deleteC :: Action () -> Controller deleteC _ = Controller - { drawController = \s -> do - - C.drawString $ let - - hdr = fromMaybe [] $ do - pid <- s^.progMode.editPlayerStateL.epsSelectedPlayer - player <- nth pid $ s^.database.dbPlayers - Just $ "Player: " ++ playerDetails player ++ "\n\n" - - in hdr ++ "Are you sure you want to delete this player? (Y/N)" - - return C.CursorInvisible - - , handleController = \e -> do - - case ynHandler e of - - Just True -> do - gets (^.progMode.editPlayerStateL.epsSelectedPlayer) >>= mapM_ - (\pid -> modify $ database.dbPlayers %~ dropNth pid) - modify edit - - Just False -> modify $ progMode.editPlayerStateL.epsMode .~ EPMenu - Nothing -> return () - - return True - + { drawController = \s -> let + hdr = fromMaybe [] $ do + pid <- s^.progMode.editPlayerStateL.epsSelectedPlayer + player <- nth pid $ s^.database.dbPlayers + Just $ "Player: " ++ playerDetails player ++ "\n" + in str $ hdr ++ "Are you sure you want to delete this player? (Y/N)" + , handleController = \e -> case ynHandler e of + Just True -> do + gets (^.progMode.editPlayerStateL.epsSelectedPlayer) >>= mapM_ + (\pid -> modify $ database.dbPlayers %~ dropNth pid) + modify edit + Just False -> modify $ progMode.editPlayerStateL.epsMode .~ EPMenu + Nothing -> return () } ytdGoalsC :: Bool -> Action () -> Controller @@ -132,8 +119,11 @@ ltAssistsC batchMode callback = promptController $ ltPMinC :: Action () -> Controller ltPMinC = promptController . editPlayerLtPMinPrompt -header :: ProgState -> C.Update () -header s = C.drawString $ fromMaybe "" $ do - pid <- s^.progMode.editPlayerStateL.epsSelectedPlayer - player <- nth pid $ s^.database.dbPlayers - Just $ playerDetails player ++ "\n" +header :: ProgState -> Widget () -> Widget () +header s w = vBox + [ fromMaybe emptyWidget $ do + pid <- s^.progMode.editPlayerStateL.epsSelectedPlayer + player <- nth pid $ s^.database.dbPlayers + Just $ str $ playerDetails player + , w + ] diff --git a/src/Mtlstats/Control/EditStandings.hs b/src/Mtlstats/Control/EditStandings.hs index 85201e3..1ad18ae 100644 --- a/src/Mtlstats/Control/EditStandings.hs +++ b/src/Mtlstats/Control/EditStandings.hs @@ -23,8 +23,9 @@ along with this program. If not, see . module Mtlstats.Control.EditStandings (editStandingsC) where +import Brick.Types (Widget) +import Brick.Widgets.Core (str, vBox) import Lens.Micro ((^.)) -import qualified UI.NCurses as C import Mtlstats.Format import Mtlstats.Menu @@ -65,17 +66,16 @@ menuC = menuControllerWith header promptC :: Prompt -> Controller promptC = promptControllerWith header -header :: ProgState -> C.Update () -header = do - db <- (^.database) - let - home = db^.dbHomeGameStats - away = db^.dbAwayGameStats - table = numTable [" W", " L", " OT", " GF", " GA"] +header :: ProgState -> Widget () -> Widget () +header s w = let + db = s^.database + home = db^.dbHomeGameStats + away = db^.dbAwayGameStats + table = numTable [" W", " L", " OT", " GF", " GA"] [ ( "HOME", valsFor home ) , ( "ROAD", valsFor away ) ] - return $ C.drawString $ unlines $ table ++ [""] + in vBox $ map str (table ++ [""]) ++ [w] valsFor :: GameStats -> [Int] valsFor gs = diff --git a/src/Mtlstats/Control/NewGame.hs b/src/Mtlstats/Control/NewGame.hs index 77080bc..318a60d 100644 --- a/src/Mtlstats/Control/NewGame.hs +++ b/src/Mtlstats/Control/NewGame.hs @@ -21,11 +21,23 @@ along with this program. If not, see . module Mtlstats.Control.NewGame (newGameC) where +import Brick.Main (vScrollBy, vScrollToBeginning) +import Brick.Types + ( BrickEvent (VtyEvent) + , ViewportType (Vertical) + , Widget + ) +import Brick.Widgets.Center (hCenter) +import Brick.Widgets.Core (str, vBox, viewport) import Control.Monad.IO.Class (liftIO) -import Control.Monad.Trans.State (get, gets, modify) +import Control.Monad.State.Class (get, gets, modify) import Data.Maybe (fromJust, fromMaybe, isJust) +import Graphics.Vty.Input.Events + ( Event (EvKey) + , Key (KDown, KHome, KEnter, KUp) + ) import Lens.Micro ((^.), (.~)) -import qualified UI.NCurses as C +import Lens.Micro.Mtl (use) import Mtlstats.Actions import Mtlstats.Actions.NewGame @@ -81,32 +93,30 @@ awayScoreC = promptControllerWith header awayScorePrompt overtimeFlagC :: Controller overtimeFlagC = Controller - { drawController = \s -> do - header s - C.drawString "Did the game go into overtime? (Y/N)" - return C.CursorInvisible - , handleController = \e -> do + { drawController = \s -> header s $ + str "Did the game go into overtime? (Y/N)" + , handleController = \e -> modify $ progMode.gameStateL.overtimeFlag .~ ynHandler e - return True } verifyDataC :: Controller verifyDataC = Controller - { drawController = \s -> do - let gs = s^.progMode.gameStateL - header s - C.drawString "\n" - C.drawString $ unlines $ labelTable + { drawController = \s -> let + gs = s^.progMode.gameStateL + in header s $ vBox $ map str $ + [""] ++ + labelTable [ ( "Date", gameDate gs ) , ( "Game type", show $ fromJust $ gs^.gameType ) , ( "Other team", gs^.otherTeam ) , ( "Home score", show $ fromJust $ gs^.homeScore ) , ( "Away score", show $ fromJust $ gs^.awayScore ) , ( "Overtime", show $ fromJust $ gs^.overtimeFlag ) - ] - C.drawString "\nIs the above information correct? (Y/N)" - return C.CursorInvisible - , handleController = \e -> do + ] ++ + [ "" + , "Is the above information correct? (Y/N)" + ] + , handleController = \e -> case ynHandler e of Just True -> modify $ (progMode.gameStateL.dataVerified .~ True) @@ -114,7 +124,6 @@ verifyDataC = Controller . awardShutouts Just False -> modify $ progMode.gameStateL .~ newGameState Nothing -> return () - return True } goalInput :: GameState -> Controller @@ -131,7 +140,6 @@ recordGoalC = Controller , handleController = \e -> do (game, goal) <- gets gameGoal promptHandler (recordGoalPrompt game goal) e - return True } recordAssistC :: Controller @@ -142,114 +150,97 @@ recordAssistC = Controller , handleController = \e -> do (game, goal, assist) <- gets gameGoalAssist promptHandler (recordAssistPrompt game goal assist) e - return True } confirmGoalDataC :: Controller confirmGoalDataC = Controller - { drawController = \s -> do - let - (game, goal) = gameGoal s - gs = s^.progMode.gameStateL - players = s^.database.dbPlayers - msg = unlines $ - [ " Game: " ++ padNum 2 game - , " Goal: " ++ show goal - , "Goal scored by: " ++ - playerSummary (fromJust $ gs^.goalBy >>= flip nth players) - ] ++ - map - (\pid -> " Assisted by: " ++ - playerSummary (fromJust $ nth pid players)) - (gs^.assistsBy) ++ - [ "" - , "Is the above information correct? (Y/N)" - ] - C.drawString msg - return C.CursorInvisible + { drawController = \s -> let + (game, goal) = gameGoal s + gs = s^.progMode.gameStateL + players = s^.database.dbPlayers + msg = + [ " Game: " ++ padNum 2 game + , " Goal: " ++ show goal + , "Goal scored by: " ++ + playerSummary (fromJust $ gs^.goalBy >>= flip nth players) + ] ++ + map + ( \pid -> " Assisted by: " ++ + playerSummary (fromJust $ nth pid players) + ) + (gs^.assistsBy) ++ + [ "" + , "Is the above information correct? (Y/N)" + ] + in vBox $ map str msg , handleController = \e -> do case ynHandler e of Just True -> modify recordGoalAssists Just False -> modify resetGoalData Nothing -> return () - return True } pMinPlayerC :: Controller pMinPlayerC = Controller - { drawController = \s -> do - header s + { drawController = \s -> header s $ drawPrompt pMinPlayerPrompt s - , handleController = \e -> do - promptHandler pMinPlayerPrompt e - return True + , handleController = promptHandler pMinPlayerPrompt } getPMinsC :: Controller getPMinsC = Controller - { drawController = \s -> do - header s - C.drawString $ fromMaybe "" $ do + { drawController = \s -> header s $ vBox + [ str $ fromMaybe "" $ do pid <- s^.progMode.gameStateL.gameSelectedPlayer player <- nth pid $ s^.database.dbPlayers - Just $ playerSummary player ++ "\n" - drawPrompt assignPMinsPrompt s - , handleController = \e -> do - promptHandler assignPMinsPrompt e - return True + Just $ playerSummary player + , drawPrompt assignPMinsPrompt s + ] + , handleController = promptHandler assignPMinsPrompt } reportC :: Controller reportC = Controller - { drawController = \s -> do - (rows, cols) <- C.windowSize - C.drawString $ unlines $ slice - (s^.scrollOffset) - (fromInteger $ pred rows) - (displayReport (fromInteger $ pred cols) s) - return C.CursorInvisible + { drawController = viewport () Vertical . hCenter . vBox . map str . + displayReport reportCols , handleController = \e -> do + scr <- use scroller case e of - C.EventSpecialKey C.KeyUpArrow -> modify scrollUp - C.EventSpecialKey C.KeyDownArrow -> modify scrollDown - C.EventSpecialKey C.KeyHome -> modify $ scrollOffset .~ 0 - - C.EventCharacter '\n' -> do - get >>= liftIO . writeFile reportFilename . exportReport reportCols - modify backHome - + VtyEvent (EvKey k []) -> case k of + KUp -> vScrollBy scr (-1) + KDown -> vScrollBy scr 1 + KHome -> vScrollToBeginning scr + KEnter -> do + get >>= liftIO . writeFile reportFilename . exportReport reportCols + modify backHome + _ -> return () _ -> return () - return True } -header :: ProgState -> C.Update () -header s = C.drawString $ - "*** GAME " ++ padNum 2 (s^.database.dbGames) ++ " ***\n" +header :: ProgState -> Widget () -> Widget () +header s w = vBox + [ str $ "*** GAME " ++ padNum 2 (s^.database.dbGames) ++ " ***\n" + , w + ] -monthHeader :: ProgState -> C.Update () -monthHeader s = do - (_, cols) <- C.windowSize - header s - - let - table = labelTable $ zip (map show ([1..] :: [Int])) - [ "JANUARY" - , "FEBRUARY" - , "MARCH" - , "APRIL" - , "MAY" - , "JUNE" - , "JULY" - , "AUGUST" - , "SEPTEMBER" - , "OCTOBER" - , "NOVEMBER" - , "DECEMBER" - ] - - C.drawString $ unlines $ - map (centre $ fromIntegral $ pred cols) $ - ["MONTH:", ""] ++ table ++ [""] +monthHeader :: ProgState -> Widget () -> Widget () +monthHeader s w = let + table = labelTable $ zip (map show ([1..] :: [Int])) + [ "JANUARY" + , "FEBRUARY" + , "MARCH" + , "APRIL" + , "MAY" + , "JUNE" + , "JULY" + , "AUGUST" + , "SEPTEMBER" + , "OCTOBER" + , "NOVEMBER" + , "DECEMBER" + ] + in header s $ vBox $ map (hCenter . str) + (["MONTH:", ""] ++ table ++ [""]) ++ [w] gameGoal :: ProgState -> (Int, Int) gameGoal s = diff --git a/src/Mtlstats/Control/NewGame/GoalieInput.hs b/src/Mtlstats/Control/NewGame/GoalieInput.hs index d436b5b..b807750 100644 --- a/src/Mtlstats/Control/NewGame/GoalieInput.hs +++ b/src/Mtlstats/Control/NewGame/GoalieInput.hs @@ -21,9 +21,10 @@ along with this program. If not, see . module Mtlstats.Control.NewGame.GoalieInput (goalieInputC) where +import Brick.Types (Widget) +import Brick.Widgets.Core (str, vBox) import Data.Maybe (fromMaybe) import Lens.Micro ((^.)) -import qualified UI.NCurses as C import Mtlstats.Format import Mtlstats.Menu @@ -52,11 +53,11 @@ goalsAllowedC = promptControllerWith header goalsAllowedPrompt selectGameGoalieC :: Controller selectGameGoalieC = menuStateController gameGoalieMenu -header :: ProgState -> C.Update () -header s = C.drawString $ unlines +header :: ProgState -> Widget () -> Widget () +header s w = vBox $ map str [ "*** GAME " ++ padNum 2 (s^.database.dbGames) ++ " ***" , fromMaybe "" $ do n <- s^.progMode.gameStateL.gameSelectedGoalie g <- nth n $ s^.database.dbGoalies Just $ goalieSummary g - ] + ] ++ [w] diff --git a/src/Mtlstats/Control/TitleScreen.hs b/src/Mtlstats/Control/TitleScreen.hs index c535aab..23e5bfc 100644 --- a/src/Mtlstats/Control/TitleScreen.hs +++ b/src/Mtlstats/Control/TitleScreen.hs @@ -23,34 +23,32 @@ along with this program. If not, see . module Mtlstats.Control.TitleScreen (titleScreenC) where -import Control.Monad.Trans.State (modify) +import Brick.Types (BrickEvent (VtyEvent)) +import Brick.Widgets.Center (hCenter) +import Brick.Widgets.Core (str, vBox) +import Control.Monad.State.Class (modify) import Data.Char (chr) -import qualified UI.NCurses as C +import Graphics.Vty.Input.Events (Event (EvKey)) import Mtlstats.Actions -import Mtlstats.Format import Mtlstats.Types titleScreenC :: Controller titleScreenC = Controller - { drawController = const $ do - (_, cols) <- C.windowSize - C.drawString $ unlines $ map (centre $ fromIntegral $ pred cols) - $ [ "" - , "MONTREAL CANADIENS STATISTICS" - ] - ++ titleText - ++ [ "" - , "Copyright (C) 1984, 1985, 2019-2021, 2023 Rhéal Lamothe" - , "" - , "" - , "Press any key to continue..." - ] - return C.CursorInvisible + { drawController = const $ vBox $ map (hCenter . str) + $ [ "" + , "MONTREAL CANADIENS STATISTICS" + ] + ++ titleText + ++ [ "" + , "Copyright (C) 1984, 1985, 2019-2021, 2023 Rhéal Lamothe" + , "" + , "" + , "Press any key to continue..." + ] , handleController = \case - C.EventCharacter _ -> modify backHome >> return True - C.EventSpecialKey _ -> modify backHome >> return True - _ -> return True + VtyEvent (EvKey _ _) -> modify backHome + _ -> return () } titleText :: [String] @@ -60,7 +58,7 @@ titleText = box $ map (map blockify) $ foldl joinBlocks (repeat "") box :: [String] -> [String] box strs = [[tl] ++ replicate width horiz ++ [tr]] - ++ map (\str -> [vert] ++ str ++ [vert]) strs + ++ map (\s -> [vert] ++ s ++ [vert]) strs ++ [[bl] ++ replicate width horiz ++ [br]] where width = length $ head strs diff --git a/src/Mtlstats/Handlers.hs b/src/Mtlstats/Handlers.hs index 0d70cbc..59177c9 100644 --- a/src/Mtlstats/Handlers.hs +++ b/src/Mtlstats/Handlers.hs @@ -21,12 +21,13 @@ along with this program. If not, see . module Mtlstats.Handlers (ynHandler) where +import Brick.Types (BrickEvent (VtyEvent)) import Data.Char (toUpper) -import qualified UI.NCurses as C +import Graphics.Vty.Input.Events (Event (EvKey), Key (KChar)) -- | Handler for a yes/no prompt -ynHandler :: C.Event -> Maybe Bool -ynHandler (C.EventCharacter c) = case toUpper c of +ynHandler :: BrickEvent () () -> Maybe Bool +ynHandler (VtyEvent (EvKey (KChar c) _)) = case toUpper c of 'Y' -> Just True 'N' -> Just False _ -> Nothing diff --git a/src/Mtlstats/Helpers/Position.hs b/src/Mtlstats/Helpers/Position.hs index 08ca69b..9fac6d9 100644 --- a/src/Mtlstats/Helpers/Position.hs +++ b/src/Mtlstats/Helpers/Position.hs @@ -28,12 +28,12 @@ module Mtlstats.Helpers.Position , getPositions ) where -import Control.Monad.Trans.State (gets) import Data.Char (toUpper) import Data.List (isInfixOf) import Data.Maybe (fromMaybe) import qualified Data.Set as S import Lens.Micro ((^.), to) +import Lens.Micro.Mtl (use) import Mtlstats.Types import Mtlstats.Util @@ -78,7 +78,7 @@ posCallback posCallback callback = \case Nothing -> callback "" Just n -> do - ps <- gets (^.database.to getPositions) + ps <- use (database.to getPositions) let pos = fromMaybe "" $ nth n ps callback pos diff --git a/src/Mtlstats/Menu.hs b/src/Mtlstats/Menu.hs index a0ca403..aad2274 100644 --- a/src/Mtlstats/Menu.hs +++ b/src/Mtlstats/Menu.hs @@ -34,40 +34,39 @@ module Mtlstats.Menu ( editMenu ) where -import Control.Monad.Trans.State (gets, modify) +import Brick.Main (halt) +import Brick.Types (BrickEvent (VtyEvent), Widget) +import Brick.Widgets.Center (hCenter) +import Brick.Widgets.Core (str, vBox) +import Control.Monad.State.Class (gets, modify) import Data.Char (toUpper) import qualified Data.Map as M import Data.Maybe (mapMaybe) +import Graphics.Vty.Input.Events (Event (EvKey), Key (KChar)) import Lens.Micro ((^.), (?~)) -import qualified UI.NCurses as C import Mtlstats.Actions import qualified Mtlstats.Actions.NewGame.GoalieInput as GI import Mtlstats.Actions.EditStandings -import Mtlstats.Format import Mtlstats.Types import Mtlstats.Types.Menu import Mtlstats.Util -- | Generates a simple 'Controller' for a Menu menuController :: Menu () -> Controller -menuController = menuControllerWith $ const $ return () +menuController = menuControllerWith $ const id -- | Generate a simple 'Controller' for a 'Menu' with a header menuControllerWith - :: (ProgState -> C.Update ()) - -- ^ Generates the header + :: (ProgState -> Widget () -> Widget()) + -- ^ Function to attach the header -> Menu () -- ^ The menu -> Controller -- ^ The resulting controller menuControllerWith header menu = Controller - { drawController = \s -> do - header s - drawMenu menu - , handleController = \e -> do - menuHandler menu e - return True + { drawController = \s -> header s $ drawMenu menu + , handleController = menuHandler menu } -- | Generate and create a controller for a menu based on the current @@ -82,38 +81,33 @@ menuStateController menuFunc = Controller , handleController = \e -> do menu <- gets menuFunc menuHandler menu e - return True } -- | The draw function for a 'Menu' -drawMenu :: Menu a -> C.Update C.CursorMode -drawMenu m = do - (_, cols) <- C.windowSize - let - width = fromIntegral $ pred cols - menuText = map (centre width) $ lines $ show m - C.drawString $ unlines menuText - return C.CursorInvisible +drawMenu :: Menu a -> Widget () +drawMenu m = let + menuLines = lines $ show m + in hCenter $ vBox $ map str menuLines -- | The event handler for a 'Menu' -menuHandler :: Menu a -> C.Event -> Action a -menuHandler m (C.EventCharacter c) = +menuHandler :: Menu a -> Handler a +menuHandler m (VtyEvent (EvKey (KChar c) [])) = case filter (\i -> i^.miKey == toUpper c) $ m^.menuItems of i:_ -> i^.miAction [] -> return $ m^.menuDefault menuHandler m _ = return $ m^.menuDefault -- | The main menu -mainMenu :: Menu Bool -mainMenu = Menu "MASTER MENU" True +mainMenu :: Menu () +mainMenu = Menu "MASTER MENU" () [ MenuItem 'A' "NEW SEASON" $ - modify startNewSeason >> return True + modify startNewSeason , MenuItem 'B' "NEW GAME" $ - modify startNewGame >> return True + modify startNewGame , MenuItem 'C' "EDIT MENU" $ - modify edit >> return True + modify edit , MenuItem 'E' "EXIT" $ - saveDatabase >> return False + saveDatabase >> halt ] -- | The new season menu diff --git a/src/Mtlstats/Menu/EditGoalie.hs b/src/Mtlstats/Menu/EditGoalie.hs index 838c799..983017b 100644 --- a/src/Mtlstats/Menu/EditGoalie.hs +++ b/src/Mtlstats/Menu/EditGoalie.hs @@ -25,7 +25,7 @@ module Mtlstats.Menu.EditGoalie , editGoalieLtMenu ) where -import Control.Monad.Trans.State (modify) +import Control.Monad.State.Class (modify) import Lens.Micro ((.~), (%~)) import Mtlstats.Actions diff --git a/src/Mtlstats/Menu/EditPlayer.hs b/src/Mtlstats/Menu/EditPlayer.hs index 0363d47..a3423ae 100644 --- a/src/Mtlstats/Menu/EditPlayer.hs +++ b/src/Mtlstats/Menu/EditPlayer.hs @@ -25,7 +25,7 @@ module Mtlstats.Menu.EditPlayer , editPlayerLtMenu ) where -import Control.Monad.Trans.State (modify) +import Control.Monad.State.Class (modify) import Lens.Micro ((.~), (%~)) import Mtlstats.Actions diff --git a/src/Mtlstats/Menu/EditStandings.hs b/src/Mtlstats/Menu/EditStandings.hs index 328549c..1256af1 100644 --- a/src/Mtlstats/Menu/EditStandings.hs +++ b/src/Mtlstats/Menu/EditStandings.hs @@ -25,7 +25,7 @@ module Mtlstats.Menu.EditStandings , editAwayStandingsMenu ) where -import Control.Monad.Trans.State (modify) +import Control.Monad.State.Class (modify) import Mtlstats.Actions import Mtlstats.Actions.EditStandings diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 0ca366e..5df3392 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -19,11 +19,8 @@ along with this program. If not, see . -} -{-# LANGUAGE LambdaCase #-} - module Mtlstats.Prompt ( -- * Prompt Functions - drawPrompt, promptHandler, promptControllerWith, promptController, @@ -51,14 +48,20 @@ module Mtlstats.Prompt ( playerToEditPrompt ) where +import Brick.Types (BrickEvent (VtyEvent), Location (Location), Widget) +import Brick.Widgets.Core (hBox, showCursor, str) import Control.Monad (when) import Control.Monad.Extra (whenJust) -import Control.Monad.Trans.State (gets, modify) +import Control.Monad.State.Class (gets, modify) import Data.Char (isAlphaNum, isDigit, toUpper) +import Graphics.Text.Width (safeWcswidth) +import Graphics.Vty.Input.Events + ( Event (EvKey) + , Key (KChar, KEnter, KEsc, KFun) + ) import Lens.Micro ((^.), (&), (.~), (?~), (%~)) -import Lens.Micro.Extras (view) +import Lens.Micro.Mtl ((.=), use) import Text.Read (readMaybe) -import qualified UI.NCurses as C import Mtlstats.Actions import Mtlstats.Config @@ -66,41 +69,31 @@ import Mtlstats.Helpers.Position import Mtlstats.Types import Mtlstats.Util --- | Draws the prompt to the screen -drawPrompt :: Prompt -> ProgState -> C.Update C.CursorMode -drawPrompt p s = do - promptDrawer p s - return C.CursorVisible - -- | Event handler for a prompt -promptHandler :: Prompt -> C.Event -> Action () -promptHandler p (C.EventCharacter '\n') = do - val <- gets $ view inputBuffer - modify $ inputBuffer .~ "" +promptHandler :: Prompt -> Handler () +promptHandler p (VtyEvent (EvKey KEnter [])) = do + val <- use inputBuffer + inputBuffer .= "" promptAction p val -promptHandler p (C.EventCharacter c) = +promptHandler p (VtyEvent (EvKey (KChar c) [])) = modify $ inputBuffer %~ promptProcessChar p c -promptHandler _ (C.EventSpecialKey C.KeyBackspace) = +promptHandler _ (VtyEvent (EvKey KEsc [])) = modify removeChar -promptHandler p (C.EventSpecialKey k) = - promptSpecialKey p k +promptHandler p (VtyEvent (EvKey k m)) = + promptSpecialKey p k m promptHandler _ _ = return () -- | Builds a controller out of a prompt with a header promptControllerWith - :: (ProgState -> C.Update ()) + :: (ProgState -> Widget () -> Widget ()) -- ^ The header -> Prompt -- ^ The prompt to use -> Controller -- ^ The resulting controller promptControllerWith header prompt = Controller - { drawController = \s -> do - header s - drawPrompt prompt s - , handleController = \e -> do - promptHandler prompt e - return True + { drawController = \s -> header s $ drawPrompt prompt s + , handleController = promptHandler prompt } -- | Builds a controller out of a prompt @@ -109,7 +102,7 @@ promptController -- ^ The prompt to use -> Controller -- ^ The resulting controller -promptController = promptControllerWith (const $ return ()) +promptController = promptControllerWith $ const id -- | Builds a string prompt strPrompt @@ -119,10 +112,10 @@ strPrompt -- ^ The callback function for the result -> Prompt strPrompt pStr act = Prompt - { promptDrawer = drawSimplePrompt pStr + { drawPrompt = drawSimplePrompt pStr , promptProcessChar = \ch -> (++ [ch]) , promptAction = act - , promptSpecialKey = const $ return () + , promptSpecialKey = \_ _ -> return () } -- | Creates an upper case string prompt @@ -179,12 +172,12 @@ numPromptWithFallback -- ^ The callback function for the result -> Prompt numPromptWithFallback pStr fallback act = Prompt - { promptDrawer = drawSimplePrompt pStr - , promptProcessChar = \ch str -> if isDigit ch - then str ++ [ch] - else str + { drawPrompt = drawSimplePrompt pStr + , promptProcessChar = \ch existing -> if isDigit ch + then existing ++ [ch] + else existing , promptAction = maybe fallback act . readMaybe - , promptSpecialKey = const $ return () + , promptSpecialKey = \_ _ -> return () } -- | Prompts for a database name @@ -215,18 +208,21 @@ newSeasonPrompt = dbNamePrompt "Filename for new season: " $ \fn -> -- | Builds a selection prompt selectPrompt :: SelectParams a -> Prompt selectPrompt params = Prompt - { promptDrawer = \s -> do - let sStr = s^.inputBuffer - C.drawString $ spPrompt params ++ sStr - (row, col) <- C.cursorPosition - C.drawString $ "\n\n" ++ spSearchHeader params ++ "\n" - let results = zip [1..maxFunKeys] $ spSearch params sStr (s^.database) - C.drawString $ unlines $ map + { drawPrompt = \s -> let + sStr = s^.inputBuffer + pStr = spPrompt params ++ sStr + pWidth = safeWcswidth pStr + results = zip [1..maxFunKeys] $ spSearch params sStr (s^.database) + fmtRes = map (\(n, (_, x)) -> let desc = spElemDesc params x - in "F" ++ show n ++ ") " ++ desc) + in str $ "F" ++ show n ++ ") " ++ desc) results - C.moveCursor row col + in hBox $ + [ showCursor () (Location (0, pWidth)) $ str pStr + , str "" + , str $ spSearchHeader params + ] ++ fmtRes , promptProcessChar = spProcessChar params , promptAction = \sStr -> if null sStr then spCallback params Nothing @@ -235,12 +231,12 @@ selectPrompt params = Prompt case spSearchExact params sStr db of Nothing -> spNotFound params sStr Just n -> spCallback params $ Just n - , promptSpecialKey = \case - C.KeyFunction rawK -> do - sStr <- gets (^.inputBuffer) - db <- gets (^.database) + , promptSpecialKey = \key _ -> case key of + KFun rawK -> do + sStr <- use inputBuffer + db <- use database let - n = pred $ fromInteger rawK + n = pred rawK results = spSearch params sStr db when (n < maxFunKeys) $ whenJust (nth n results) $ \(sel, _) -> do @@ -406,5 +402,8 @@ playerToEditPrompt :: Prompt playerToEditPrompt = selectPlayerPrompt "Player to edit: " $ modify . (progMode.editPlayerStateL.epsSelectedPlayer .~) -drawSimplePrompt :: String -> ProgState -> C.Update () -drawSimplePrompt pStr s = C.drawString $ pStr ++ s^.inputBuffer +drawSimplePrompt :: String -> Renderer +drawSimplePrompt pStr s = let + fullStr = pStr ++ s^.inputBuffer + strWidth = safeWcswidth fullStr + in showCursor () (Location (0, strWidth)) $ str fullStr diff --git a/src/Mtlstats/Prompt/EditGoalie.hs b/src/Mtlstats/Prompt/EditGoalie.hs index fdf5835..82544df 100644 --- a/src/Mtlstats/Prompt/EditGoalie.hs +++ b/src/Mtlstats/Prompt/EditGoalie.hs @@ -39,7 +39,7 @@ module Mtlstats.Prompt.EditGoalie , editGoalieLtTiesPrompt ) where -import Control.Monad.Trans.State (modify) +import Control.Monad.State.Class (modify) import Lens.Micro ((.~)) import Mtlstats.Actions diff --git a/src/Mtlstats/Prompt/EditPlayer.hs b/src/Mtlstats/Prompt/EditPlayer.hs index 1191764..43d93df 100644 --- a/src/Mtlstats/Prompt/EditPlayer.hs +++ b/src/Mtlstats/Prompt/EditPlayer.hs @@ -31,7 +31,7 @@ module Mtlstats.Prompt.EditPlayer , editPlayerLtPMinPrompt ) where -import Control.Monad.Trans.State (modify) +import Control.Monad.State.Class (modify) import Lens.Micro ((.~)) import Mtlstats.Actions diff --git a/src/Mtlstats/Prompt/EditStandings.hs b/src/Mtlstats/Prompt/EditStandings.hs index 6a67702..6e8df45 100644 --- a/src/Mtlstats/Prompt/EditStandings.hs +++ b/src/Mtlstats/Prompt/EditStandings.hs @@ -32,7 +32,7 @@ module Mtlstats.Prompt.EditStandings , editAwayGoalsAgainstPrompt ) where -import Control.Monad.Trans.State (modify) +import Control.Monad.State.Class (modify) import Lens.Micro ((.~), (%~)) import Mtlstats.Prompt diff --git a/src/Mtlstats/Prompt/NewGame.hs b/src/Mtlstats/Prompt/NewGame.hs index 478756d..ccd20d8 100644 --- a/src/Mtlstats/Prompt/NewGame.hs +++ b/src/Mtlstats/Prompt/NewGame.hs @@ -35,7 +35,7 @@ module Mtlstats.Prompt.NewGame ) where import Control.Monad (when) -import Control.Monad.Trans.State (gets, modify) +import Control.Monad.State.Class (gets, modify) import Lens.Micro ((^.), (.~), (?~), (%~)) import Mtlstats.Actions.NewGame diff --git a/src/Mtlstats/Prompt/NewGame/GoalieInput.hs b/src/Mtlstats/Prompt/NewGame/GoalieInput.hs index 19ea0f2..78e1a63 100644 --- a/src/Mtlstats/Prompt/NewGame/GoalieInput.hs +++ b/src/Mtlstats/Prompt/NewGame/GoalieInput.hs @@ -27,7 +27,7 @@ module Mtlstats.Prompt.NewGame.GoalieInput , goalsAllowedPrompt ) where -import Control.Monad.Trans.State (modify) +import Control.Monad.State.Class (modify) import Lens.Micro ((?~)) import Mtlstats.Actions.NewGame.GoalieInput diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index a810f6e..ecef2de 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -24,7 +24,9 @@ along with this program. If not, see . module Mtlstats.Types ( -- * Types Controller (..), + Renderer, Action, + Handler, ProgState (..), ProgMode (..), GameState (..), @@ -52,7 +54,7 @@ module Mtlstats.Types ( progMode, dbName, inputBuffer, - scrollOffset, + scroller, -- ** ProgMode Lenses gameStateL, createPlayerStateL, @@ -195,7 +197,8 @@ module Mtlstats.Types ( gsAverage ) where -import Control.Monad.Trans.State (StateT) +import Brick.Main (ViewportScroll, viewportScroll) +import Brick.Types (BrickEvent, EventM, Widget) import Data.Aeson ( FromJSON , ToJSON @@ -213,22 +216,28 @@ import Data.Aeson import Data.Char (toUpper) import Data.List (find, isInfixOf) import qualified Data.Map as M +import Graphics.Vty.Input.Events (Key, Modifier) import Lens.Micro (Lens', lens, (&), (^.), (.~)) import Lens.Micro.TH (makeLenses) -import qualified UI.NCurses as C import Mtlstats.Config -- | Controls the program flow data Controller = Controller - { drawController :: ProgState -> C.Update C.CursorMode - -- ^ The drawing phase - , handleController :: C.Event -> Action Bool + { drawController :: Renderer + -- ^ The drawing routine + , handleController :: Handler () -- ^ The event handler } +-- | Renders a view based on a "ProgState" +type Renderer = ProgState -> Widget () + -- | Action which maintains program state -type Action a = StateT ProgState C.Curses a +type Action a = EventM () ProgState a + +-- | Handles an event +type Handler a = BrickEvent () () -> Action a -- | Represents the program state data ProgState = ProgState @@ -240,8 +249,8 @@ data ProgState = ProgState -- ^ The name of the database file , _inputBuffer :: String -- ^ Buffer for user input - , _scrollOffset :: Int - -- ^ The scrolling offset for the display + , _scroller :: ViewportScroll () + -- ^ Scroller for the reports } -- | The program mode @@ -532,13 +541,13 @@ data GameStats = GameStats -- | Defines a user prompt data Prompt = Prompt - { promptDrawer :: ProgState -> C.Update () + { drawPrompt :: ProgState -> Widget () -- ^ Draws the prompt to the screen , promptProcessChar :: Char -> String -> String -- ^ Modifies the string based on the character entered , promptAction :: String -> Action () -- ^ Action to perform when the value is entered - , promptSpecialKey :: C.Key -> Action () + , promptSpecialKey :: Key -> [Modifier] -> Action () -- ^ Action to perform when a special key is pressed } @@ -786,11 +795,11 @@ esmSubModeL = lens -- | Constructor for a 'ProgState' newProgState :: ProgState newProgState = ProgState - { _database = newDatabase - , _progMode = TitleScreen - , _dbName = "" - , _inputBuffer = "" - , _scrollOffset = 0 + { _database = newDatabase + , _progMode = TitleScreen + , _dbName = "" + , _inputBuffer = "" + , _scroller = viewportScroll () } -- | Constructor for a 'GameState' diff --git a/src/Mtlstats/Util.hs b/src/Mtlstats/Util.hs index 6616a0f..c06e520 100644 --- a/src/Mtlstats/Util.hs +++ b/src/Mtlstats/Util.hs @@ -26,8 +26,11 @@ module Mtlstats.Util , updateMap , slice , capitalizeName + , linesToWidget ) where +import Brick.Types (Widget) +import Brick.Widgets.Core (str, vBox) import Data.Char (isSpace, toUpper) import qualified Data.Map as M @@ -105,9 +108,9 @@ capitalizeName -- ^ The current string -> String -- ^ The resulting string -capitalizeName ch str = str ++ [ch'] +capitalizeName ch s = s ++ [ch'] where - ch' = if lockFlag str + ch' = if lockFlag s then toUpper ch else ch lockFlag "" = True @@ -118,3 +121,6 @@ capitalizeName ch str = str ++ [ch'] lockFlag' (c:cs) | isSpace c = lockFlag' cs | otherwise = False + +linesToWidget :: [String] -> Widget () +linesToWidget = vBox . map str diff --git a/stack.yaml b/stack.yaml index 678c723..8367588 100644 --- a/stack.yaml +++ b/stack.yaml @@ -17,7 +17,7 @@ # # resolver: ./custom-snapshot.yaml # resolver: https://example.com/snapshots/2018-01-01.yaml -resolver: lts-14.0 +resolver: lts-20.22 # User packages to be built. # Various formats can be used as shown in the example below. diff --git a/stack.yaml.lock b/stack.yaml.lock new file mode 100644 index 0000000..3e0bd6b --- /dev/null +++ b/stack.yaml.lock @@ -0,0 +1,19 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: +- completed: + hackage: ncurses-0.2.16@sha256:8ad9fe6562a80d28166d76adbac1eb4d40c6511fe4e9272ed6e1166dc2f1cdf1,3575 + pantry-tree: + sha256: 093bdc85ed518c81724f5b6b81c24ab4ebdd231551861f4feaa43361136f70b7 + size: 674 + original: + hackage: ncurses-0.2.16@sha256:8ad9fe6562a80d28166d76adbac1eb4d40c6511fe4e9272ed6e1166dc2f1cdf1,3575 +snapshots: +- completed: + sha256: dcf4fc28f12d805480ddbe8eb8c370e11db12f0461d0110a4240af27ac88d725 + size: 650255 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/22.yaml + original: lts-20.22 diff --git a/test/Actions/NewGame/GoalieInputSpec.hs b/test/Actions/NewGame/GoalieInputSpec.hs index 1b0137a..72e3a7d 100644 --- a/test/Actions/NewGame/GoalieInputSpec.hs +++ b/test/Actions/NewGame/GoalieInputSpec.hs @@ -215,7 +215,7 @@ setGameGoalieSpec = describe "setGameGoalie" $ mapM_ let ps' = setGameGoalie goalieId ps - [joe', bob'] = ps'^.database.dbGoalies + (joe', bob') = getFirstTwo $ ps'^.database.dbGoalies gStats' = ps'^.progMode.gameStateL.gameGoalieStats context "Joe" $ joe' `TS.compareTest` expectedJoe @@ -380,3 +380,7 @@ setGameGoalieSpec = describe "setGameGoalie" $ mapM_ incSO = gsShutouts %~ succ incLoss = gsLosses %~ succ incOT = gsTies %~ succ + +getFirstTwo :: [a] -> (a, a) +getFirstTwo (x:y:_) = (x, y) +getFirstTwo _ = error "insufficient members of list" diff --git a/test/ActionsSpec.hs b/test/ActionsSpec.hs index afad41d..7f17eef 100644 --- a/test/ActionsSpec.hs +++ b/test/ActionsSpec.hs @@ -63,8 +63,6 @@ spec = describe "Mtlstats.Actions" $ do resetCreatePlayerStateSpec resetCreateGoalieStateSpec backHomeSpec - scrollUpSpec - scrollDownSpec NewGame.spec EditStandings.spec @@ -425,7 +423,6 @@ backHomeSpec = describe "backHome" $ do input = newProgState & progMode.gameStateL .~ newGameState & inputBuffer .~ "foo" - & scrollOffset .~ 123 result = backHome input it "should set the program mode back to MainMenu" $ @@ -435,33 +432,3 @@ backHomeSpec = describe "backHome" $ do it "should clear the input buffer" $ result^.inputBuffer `shouldBe` "" - - it "should reset the scroll offset" $ - result^.scrollOffset `shouldBe` 0 - -scrollUpSpec :: Spec -scrollUpSpec = describe "scrollUp" $ do - - context "scrolled down" $ - it "should decrease the scroll offset by one" $ let - ps = newProgState & scrollOffset .~ 10 - ps' = scrollUp ps - in ps'^.scrollOffset `shouldBe` 9 - - context "at top" $ - it "should keep the scroll offset at zero" $ let - ps = scrollUp newProgState - in ps^.scrollOffset `shouldBe` 0 - - context "above top" $ - it "should return the scroll offset to zero" $ let - ps = newProgState & scrollOffset .~ (-10) - ps' = scrollUp ps - in ps'^.scrollOffset `shouldBe` 0 - -scrollDownSpec :: Spec -scrollDownSpec = describe "scrollDown" $ - it "should increase the scroll offset" $ let - ps = newProgState & scrollOffset .~ 10 - ps' = scrollDown ps - in ps'^.scrollOffset `shouldBe` 11 diff --git a/test/HandlersSpec.hs b/test/HandlersSpec.hs index 625ba28..0e4dbcb 100644 --- a/test/HandlersSpec.hs +++ b/test/HandlersSpec.hs @@ -22,7 +22,9 @@ along with this program. If not, see . module HandlersSpec (spec) where import Test.Hspec (Spec, context, describe, it, shouldBe) -import qualified UI.NCurses as C + +import Brick.Types (BrickEvent (VtyEvent)) +import Graphics.Vty.Input.Events (Event (EvKey, EvResize), Key (KChar)) import Mtlstats.Handlers @@ -37,10 +39,18 @@ ynHandlerSpec = describe "ynHandler" $ mapM_ it ("should be " ++ show expected) $ ynHandler event `shouldBe` expected) -- description, event, expected - [ ( "Y pressed", C.EventCharacter 'Y', Just True ) - , ( "y pressed", C.EventCharacter 'y', Just True ) - , ( "N pressed", C.EventCharacter 'N', Just False ) - , ( "n pressed", C.EventCharacter 'n', Just False ) - , ( "x pressed", C.EventCharacter 'x', Nothing ) - , ( "other event", C.EventResized, Nothing ) + [ ( "Y pressed", capitalY, Just True ) + , ( "y pressed", lowerY, Just True ) + , ( "N pressed", capitalN, Just False ) + , ( "n pressed", lowerN, Just False ) + , ( "x pressed", lowerX, Nothing ) + , ( "other event", otherEvent, Nothing ) ] + where + capitalY = chE 'Y' + lowerY = chE 'y' + capitalN = chE 'N' + lowerN = chE 'n' + lowerX = chE 'x' + otherEvent = VtyEvent $ EvResize 0 0 + chE c = VtyEvent $ EvKey (KChar c) [] diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index 45b5331..509fa8f 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -34,8 +34,8 @@ import Control.Monad (replicateM) import Data.Aeson (FromJSON, ToJSON, decode, encode, toJSON) import Data.Aeson.Types (Value (Object)) import qualified Data.Map.Lazy as M -import qualified Data.HashMap.Strict as HM import Data.Ratio ((%)) +import qualified GHC.Exts as HM import Lens.Micro (Lens', (&), (^.), (.~), (?~)) import System.Random (randomIO, randomRIO) import Test.Hspec (Spec, context, describe, it, shouldBe) From 5ea2d77921d2303e7fa66173c05fbb231f07e5a7 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 30 May 2023 17:58:45 -0400 Subject: [PATCH 05/21] bugfix: make the whole background blue --- src/Mtlstats.hs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats.hs b/src/Mtlstats.hs index b6e6b02..339d2e9 100644 --- a/src/Mtlstats.hs +++ b/src/Mtlstats.hs @@ -23,7 +23,9 @@ module Mtlstats (app) where import Brick.AttrMap (AttrMap, forceAttrMap) import Brick.Main (App (..), showFirstCursor) +import Brick.Types (Widget) import Brick.Util (on) +import Brick.Widgets.Core (fill) import Graphics.Vty.Attributes.Color (blue, white) import Lens.Micro (to) import Lens.Micro.Mtl (use) @@ -34,13 +36,19 @@ import Mtlstats.Types -- | The main application app :: App ProgState () () app = App - { appDraw = \s -> [drawController (dispatch s) s] + { appDraw = draw , appChooseCursor = showFirstCursor , appHandleEvent = handler , appStartEvent = return () , appAttrMap = const myAttrMap } +draw :: ProgState -> [Widget ()] +draw s = + [ drawController (dispatch s) s + , fill ' ' + ] + handler :: Handler () handler e = do c <- use (to dispatch) From d40b56da375dda09ec1bf0d386c0479116609ffa Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 30 May 2023 18:06:32 -0400 Subject: [PATCH 06/21] bail on CTRL-C --- src/Mtlstats.hs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Mtlstats.hs b/src/Mtlstats.hs index 339d2e9..3e7406a 100644 --- a/src/Mtlstats.hs +++ b/src/Mtlstats.hs @@ -22,11 +22,16 @@ along with this program. If not, see . module Mtlstats (app) where import Brick.AttrMap (AttrMap, forceAttrMap) -import Brick.Main (App (..), showFirstCursor) -import Brick.Types (Widget) +import Brick.Main (App (..), halt, showFirstCursor) +import Brick.Types (BrickEvent (VtyEvent), Widget) import Brick.Util (on) import Brick.Widgets.Core (fill) import Graphics.Vty.Attributes.Color (blue, white) +import Graphics.Vty.Input.Events + ( Event (EvKey) + , Modifier (MCtrl) + , Key (KChar) + ) import Lens.Micro (to) import Lens.Micro.Mtl (use) @@ -50,6 +55,7 @@ draw s = ] handler :: Handler () +handler (VtyEvent (EvKey (KChar 'c') [MCtrl])) = halt handler e = do c <- use (to dispatch) handleController c e From ea9a9c6a85617c2a3275b10841779ae03bece6f9 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 30 May 2023 18:11:54 -0400 Subject: [PATCH 07/21] bugfix: backspace backspace functionality was mistakenly mapped to the escape key for some reason --- src/Mtlstats/Prompt.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 5df3392..1831f7d 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -57,7 +57,7 @@ import Data.Char (isAlphaNum, isDigit, toUpper) import Graphics.Text.Width (safeWcswidth) import Graphics.Vty.Input.Events ( Event (EvKey) - , Key (KChar, KEnter, KEsc, KFun) + , Key (KBS, KChar, KEnter, KFun) ) import Lens.Micro ((^.), (&), (.~), (?~), (%~)) import Lens.Micro.Mtl ((.=), use) @@ -77,7 +77,7 @@ promptHandler p (VtyEvent (EvKey KEnter [])) = do promptAction p val promptHandler p (VtyEvent (EvKey (KChar c) [])) = modify $ inputBuffer %~ promptProcessChar p c -promptHandler _ (VtyEvent (EvKey KEsc [])) = +promptHandler _ (VtyEvent (EvKey KBS [])) = modify removeChar promptHandler p (VtyEvent (EvKey k m)) = promptSpecialKey p k m From afae5ea14a3326a285ddd94f5247a74ced5f24e0 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 30 May 2023 18:21:56 -0400 Subject: [PATCH 08/21] updated ChangeLog --- ChangeLog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 0e5e67c..67470d8 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,8 @@ # Changelog for mtlstats +## current +- updated code to use brick instead of ncurses + ## 0.16.1 - Don't automatically start a new game on new season From 08e0f96a8179f71e229638e84cb44411b2678741 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 30 May 2023 18:30:49 -0400 Subject: [PATCH 09/21] cursor position fix cursor X and Y coordinates were transposed for the simple string prompts --- src/Mtlstats/Prompt.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 1831f7d..3ff8105 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -406,4 +406,4 @@ drawSimplePrompt :: String -> Renderer drawSimplePrompt pStr s = let fullStr = pStr ++ s^.inputBuffer strWidth = safeWcswidth fullStr - in showCursor () (Location (0, strWidth)) $ str fullStr + in showCursor () (Location (strWidth, 0)) $ str fullStr From 166483dc50e3a57ae6f920d8afb5d22b08253d99 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 30 May 2023 18:45:03 -0400 Subject: [PATCH 10/21] fixed missing blank line between menu header and options --- src/Mtlstats/Menu.hs | 3 +-- src/Mtlstats/Util.hs | 8 +++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Mtlstats/Menu.hs b/src/Mtlstats/Menu.hs index aad2274..7c42a91 100644 --- a/src/Mtlstats/Menu.hs +++ b/src/Mtlstats/Menu.hs @@ -37,7 +37,6 @@ module Mtlstats.Menu ( import Brick.Main (halt) import Brick.Types (BrickEvent (VtyEvent), Widget) import Brick.Widgets.Center (hCenter) -import Brick.Widgets.Core (str, vBox) import Control.Monad.State.Class (gets, modify) import Data.Char (toUpper) import qualified Data.Map as M @@ -87,7 +86,7 @@ menuStateController menuFunc = Controller drawMenu :: Menu a -> Widget () drawMenu m = let menuLines = lines $ show m - in hCenter $ vBox $ map str menuLines + in hCenter $ linesToWidget menuLines -- | The event handler for a 'Menu' menuHandler :: Menu a -> Handler a diff --git a/src/Mtlstats/Util.hs b/src/Mtlstats/Util.hs index c06e520..6aaae46 100644 --- a/src/Mtlstats/Util.hs +++ b/src/Mtlstats/Util.hs @@ -19,6 +19,8 @@ along with this program. If not, see . -} +{-# LANGUAGE LambdaCase #-} + module Mtlstats.Util ( nth , modifyNth @@ -123,4 +125,8 @@ capitalizeName ch s = s ++ [ch'] | otherwise = False linesToWidget :: [String] -> Widget () -linesToWidget = vBox . map str +linesToWidget = vBox . map + ( str . \case + "" -> " " + s -> s + ) From 097d51f34beb08784a6081d04615e864bdae5df0 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 30 May 2023 18:56:44 -0400 Subject: [PATCH 11/21] properly centre menu headings --- src/Mtlstats/Menu.hs | 3 +-- src/Mtlstats/Util.hs | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Mtlstats/Menu.hs b/src/Mtlstats/Menu.hs index 7c42a91..a6d6b5d 100644 --- a/src/Mtlstats/Menu.hs +++ b/src/Mtlstats/Menu.hs @@ -36,7 +36,6 @@ module Mtlstats.Menu ( import Brick.Main (halt) import Brick.Types (BrickEvent (VtyEvent), Widget) -import Brick.Widgets.Center (hCenter) import Control.Monad.State.Class (gets, modify) import Data.Char (toUpper) import qualified Data.Map as M @@ -86,7 +85,7 @@ menuStateController menuFunc = Controller drawMenu :: Menu a -> Widget () drawMenu m = let menuLines = lines $ show m - in hCenter $ linesToWidget menuLines + in linesToWidgetC menuLines -- | The event handler for a 'Menu' menuHandler :: Menu a -> Handler a diff --git a/src/Mtlstats/Util.hs b/src/Mtlstats/Util.hs index 6aaae46..4a1dda1 100644 --- a/src/Mtlstats/Util.hs +++ b/src/Mtlstats/Util.hs @@ -19,8 +19,6 @@ along with this program. If not, see . -} -{-# LANGUAGE LambdaCase #-} - module Mtlstats.Util ( nth , modifyNth @@ -29,9 +27,11 @@ module Mtlstats.Util , slice , capitalizeName , linesToWidget + , linesToWidgetC ) where import Brick.Types (Widget) +import Brick.Widgets.Center (hCenter) import Brick.Widgets.Core (str, vBox) import Data.Char (isSpace, toUpper) import qualified Data.Map as M @@ -124,9 +124,15 @@ capitalizeName ch s = s ++ [ch'] | isSpace c = lockFlag' cs | otherwise = False +-- | Converts a list of lines to a widget linesToWidget :: [String] -> Widget () -linesToWidget = vBox . map - ( str . \case - "" -> " " - s -> s - ) +linesToWidget = vBox . map (str . keepBlank) + +-- | Converts a list of lines to a widget with each line horizontally +-- centered +linesToWidgetC :: [String] -> Widget () +linesToWidgetC = vBox . map (hCenter . str . keepBlank) + +keepBlank :: String -> String +keepBlank "" = " " +keepBlank s = s From 2d5c4e64712b2b41a8f35b9c7359d84e39894243 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 30 May 2023 19:01:30 -0400 Subject: [PATCH 12/21] fixed spacing on title screen --- src/Mtlstats/Control/TitleScreen.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Mtlstats/Control/TitleScreen.hs b/src/Mtlstats/Control/TitleScreen.hs index 23e5bfc..f727add 100644 --- a/src/Mtlstats/Control/TitleScreen.hs +++ b/src/Mtlstats/Control/TitleScreen.hs @@ -24,18 +24,17 @@ along with this program. If not, see . module Mtlstats.Control.TitleScreen (titleScreenC) where import Brick.Types (BrickEvent (VtyEvent)) -import Brick.Widgets.Center (hCenter) -import Brick.Widgets.Core (str, vBox) import Control.Monad.State.Class (modify) import Data.Char (chr) import Graphics.Vty.Input.Events (Event (EvKey)) import Mtlstats.Actions import Mtlstats.Types +import Mtlstats.Util titleScreenC :: Controller titleScreenC = Controller - { drawController = const $ vBox $ map (hCenter . str) + { drawController = const $ linesToWidgetC $ [ "" , "MONTREAL CANADIENS STATISTICS" ] From 820aab5e964762b2d52d51d0285be91f9b6016dc Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 31 May 2023 13:21:49 -0400 Subject: [PATCH 13/21] fix layout of selection prompt --- src/Mtlstats/Prompt.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 3ff8105..ce14916 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -49,7 +49,7 @@ module Mtlstats.Prompt ( ) where import Brick.Types (BrickEvent (VtyEvent), Location (Location), Widget) -import Brick.Widgets.Core (hBox, showCursor, str) +import Brick.Widgets.Core (showCursor, str, vBox) import Control.Monad (when) import Control.Monad.Extra (whenJust) import Control.Monad.State.Class (gets, modify) @@ -218,9 +218,9 @@ selectPrompt params = Prompt desc = spElemDesc params x in str $ "F" ++ show n ++ ") " ++ desc) results - in hBox $ - [ showCursor () (Location (0, pWidth)) $ str pStr - , str "" + in vBox $ + [ showCursor () (Location (pWidth, 0)) $ str pStr + , str " " , str $ spSearchHeader params ] ++ fmtRes , promptProcessChar = spProcessChar params From d92722be9c3bac02add35aab1426b2792ebabf22 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 31 May 2023 20:08:49 -0400 Subject: [PATCH 14/21] use Editor istead of String --- package.yaml | 1 + src/Mtlstats/Actions.hs | 27 +++++++++------------- src/Mtlstats/Prompt.hs | 51 ++++++++++++++++++++++------------------- src/Mtlstats/Types.hs | 31 +++++++++++++------------ src/Mtlstats/Util.hs | 17 +++++++++++--- test/ActionsSpec.hs | 33 ++++---------------------- test/SpecHelpers.hs | 29 +++++++++++++++++++++++ test/UtilSpec.hs | 4 +++- 8 files changed, 106 insertions(+), 87 deletions(-) create mode 100644 test/SpecHelpers.hs diff --git a/package.yaml b/package.yaml index ee7b350..e51d753 100644 --- a/package.yaml +++ b/package.yaml @@ -31,6 +31,7 @@ dependencies: - microlens-th >= 0.4.2.3 && < 0.5 - mtl >= 2.2.2 && < 2.3 - random >= 1.2.1.1 && < 1.3 +- text-zipper >= 0.12 && < 0.13 - time >= 1.11.1.1 && < 1.12 - vty >= 5.37 && < 5.38 diff --git a/src/Mtlstats/Actions.hs b/src/Mtlstats/Actions.hs index 00dfd13..2b6bdf2 100644 --- a/src/Mtlstats/Actions.hs +++ b/src/Mtlstats/Actions.hs @@ -19,7 +19,7 @@ along with this program. If not, see . -} -{-# LANGUAGE LambdaCase, ScopedTypeVariables #-} +{-# LANGUAGE ScopedTypeVariables #-} module Mtlstats.Actions ( startNewSeason @@ -27,8 +27,6 @@ module Mtlstats.Actions , clearRookies , resetStandings , startNewGame - , addChar - , removeChar , createPlayer , createGoalie , edit @@ -41,16 +39,19 @@ module Mtlstats.Actions , resetCreatePlayerState , resetCreateGoalieState , backHome + , clearEditor , loadDatabase , saveDatabase ) where import Brick.Main (viewportScroll) +import Brick.Widgets.Edit (Editor, applyEdit) import Control.Exception (IOException, catch) import Control.Monad.IO.Class (liftIO) import Control.Monad.State.Class (modify) import Data.Aeson (decodeFileStrict, encodeFile) import Data.Maybe (fromMaybe) +import Data.Text.Zipper (gotoBOF, killToEOF) import Lens.Micro ((^.), (&), (.~), (%~)) import Lens.Micro.Mtl ((.=), use) import System.EasyFile @@ -93,16 +94,6 @@ startNewGame = (progMode .~ NewGame newGameState) . (database . dbGames %~ succ) --- | Adds a character to the input buffer -addChar :: Char -> ProgState -> ProgState -addChar c = inputBuffer %~ (++[c]) - --- | Removes a character from the input buffer (if possible) -removeChar :: ProgState -> ProgState -removeChar = inputBuffer %~ \case - "" -> "" - str -> init str - -- | Starts player creation mode createPlayer :: ProgState -> ProgState createPlayer = let @@ -206,9 +197,13 @@ resetCreateGoalieState = progMode.createGoalieStateL -- | Resets the program state back to the main menu backHome :: ProgState -> ProgState backHome - = (progMode .~ MainMenu) - . (inputBuffer .~ "") - . (scroller .~ viewportScroll ()) + = (progMode .~ MainMenu) + . (editorW %~ clearEditor) + . (scroller .~ viewportScroll ()) + +-- | Clears an editor +clearEditor :: Editor String () -> Editor String () +clearEditor = applyEdit $ killToEOF . gotoBOF -- | Loads the database loadDatabase :: Action () diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index ce14916..8b26164 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -48,19 +48,20 @@ module Mtlstats.Prompt ( playerToEditPrompt ) where -import Brick.Types (BrickEvent (VtyEvent), Location (Location), Widget) -import Brick.Widgets.Core (showCursor, str, vBox) +import Brick.Types (BrickEvent (VtyEvent), Widget) +import Brick.Widgets.Core (hBox, str, vBox) +import Brick.Widgets.Edit (editContentsL, renderEditor) import Control.Monad (when) import Control.Monad.Extra (whenJust) import Control.Monad.State.Class (gets, modify) import Data.Char (isAlphaNum, isDigit, toUpper) -import Graphics.Text.Width (safeWcswidth) +import Data.Text.Zipper (deletePrevChar, insertChar) import Graphics.Vty.Input.Events ( Event (EvKey) , Key (KBS, KChar, KEnter, KFun) ) -import Lens.Micro ((^.), (&), (.~), (?~), (%~)) -import Lens.Micro.Mtl ((.=), use) +import Lens.Micro ((^.), (&), (.~), (?~), (%~), to) +import Lens.Micro.Mtl ((%=), use) import Text.Read (readMaybe) import Mtlstats.Actions @@ -72,13 +73,13 @@ import Mtlstats.Util -- | Event handler for a prompt promptHandler :: Prompt -> Handler () promptHandler p (VtyEvent (EvKey KEnter [])) = do - val <- use inputBuffer - inputBuffer .= "" + val <- use $ editorW.to userText + editorW %= clearEditor promptAction p val promptHandler p (VtyEvent (EvKey (KChar c) [])) = - modify $ inputBuffer %~ promptProcessChar p c + modify $ editorW %~ promptProcessChar p c promptHandler _ (VtyEvent (EvKey KBS [])) = - modify removeChar + modify (editorW.editContentsL %~ deletePrevChar) promptHandler p (VtyEvent (EvKey k m)) = promptSpecialKey p k m promptHandler _ _ = return () @@ -113,7 +114,7 @@ strPrompt -> Prompt strPrompt pStr act = Prompt { drawPrompt = drawSimplePrompt pStr - , promptProcessChar = \ch -> (++ [ch]) + , promptProcessChar = \ch -> editContentsL %~ insertChar ch , promptAction = act , promptSpecialKey = \_ _ -> return () } @@ -126,7 +127,7 @@ ucStrPrompt -- ^ The callback function for the result -> Prompt ucStrPrompt pStr act = (strPrompt pStr act) - { promptProcessChar = \ch -> (++ [toUpper ch]) } + { promptProcessChar = \ch -> editContentsL %~ insertChar ch } -- | Creates a prompt which forces capitalization of input to -- accomodate a player or goalie name @@ -174,7 +175,7 @@ numPromptWithFallback numPromptWithFallback pStr fallback act = Prompt { drawPrompt = drawSimplePrompt pStr , promptProcessChar = \ch existing -> if isDigit ch - then existing ++ [ch] + then existing & editContentsL %~ insertChar ch else existing , promptAction = maybe fallback act . readMaybe , promptSpecialKey = \_ _ -> return () @@ -189,7 +190,7 @@ dbNamePrompt -> Prompt dbNamePrompt pStr act = (strPrompt pStr act) { promptProcessChar = \ch -> if isAlphaNum ch || ch == '-' - then (++[toUpper ch]) + then editContentsL %~ insertChar (toUpper ch) else id } @@ -209,9 +210,8 @@ newSeasonPrompt = dbNamePrompt "Filename for new season: " $ \fn -> selectPrompt :: SelectParams a -> Prompt selectPrompt params = Prompt { drawPrompt = \s -> let - sStr = s^.inputBuffer - pStr = spPrompt params ++ sStr - pWidth = safeWcswidth pStr + sStr = s^.editorW.to userText + pStr = spPrompt params results = zip [1..maxFunKeys] $ spSearch params sStr (s^.database) fmtRes = map (\(n, (_, x)) -> let @@ -219,7 +219,10 @@ selectPrompt params = Prompt in str $ "F" ++ show n ++ ") " ++ desc) results in vBox $ - [ showCursor () (Location (pWidth, 0)) $ str pStr + [ hBox + [ str pStr + , renderEditor linesToWidget True (s^.editorW) + ] , str " " , str $ spSearchHeader params ] ++ fmtRes @@ -233,14 +236,14 @@ selectPrompt params = Prompt Just n -> spCallback params $ Just n , promptSpecialKey = \key _ -> case key of KFun rawK -> do - sStr <- use inputBuffer + sStr <- use $ editorW . to userText db <- use database let n = pred rawK results = spSearch params sStr db when (n < maxFunKeys) $ whenJust (nth n results) $ \(sel, _) -> do - modify $ inputBuffer .~ "" + modify $ editorW %~ clearEditor spCallback params $ Just sel _ -> return () } @@ -393,7 +396,7 @@ selectPositionPrompt pStr callback = selectPrompt SelectParams , spSearch = posSearch , spSearchExact = posSearchExact , spElemDesc = id - , spProcessChar = \ch -> (++ [toUpper ch]) + , spProcessChar = \c -> editContentsL %~ insertChar (toUpper c) , spCallback = posCallback callback , spNotFound = callback } @@ -403,7 +406,7 @@ playerToEditPrompt = selectPlayerPrompt "Player to edit: " $ modify . (progMode.editPlayerStateL.epsSelectedPlayer .~) drawSimplePrompt :: String -> Renderer -drawSimplePrompt pStr s = let - fullStr = pStr ++ s^.inputBuffer - strWidth = safeWcswidth fullStr - in showCursor () (Location (strWidth, 0)) $ str fullStr +drawSimplePrompt pStr s = hBox + [ str pStr + , renderEditor linesToWidget True (s^.editorW) + ] diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index ecef2de..db53b36 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -53,7 +53,7 @@ module Mtlstats.Types ( database, progMode, dbName, - inputBuffer, + editorW, scroller, -- ** ProgMode Lenses gameStateL, @@ -199,6 +199,7 @@ module Mtlstats.Types ( import Brick.Main (ViewportScroll, viewportScroll) import Brick.Types (BrickEvent, EventM, Widget) +import Brick.Widgets.Edit (Editor, editor) import Data.Aeson ( FromJSON , ToJSON @@ -241,15 +242,15 @@ type Handler a = BrickEvent () () -> Action a -- | Represents the program state data ProgState = ProgState - { _database :: Database + { _database :: Database -- ^ The data to be saved - , _progMode :: ProgMode + , _progMode :: ProgMode -- ^ The program's mode - , _dbName :: String + , _dbName :: String -- ^ The name of the database file - , _inputBuffer :: String - -- ^ Buffer for user input - , _scroller :: ViewportScroll () + , _editorW :: Editor String () + -- ^ Editor widget + , _scroller :: ViewportScroll () -- ^ Scroller for the reports } @@ -543,8 +544,8 @@ data GameStats = GameStats data Prompt = Prompt { drawPrompt :: ProgState -> Widget () -- ^ Draws the prompt to the screen - , promptProcessChar :: Char -> String -> String - -- ^ Modifies the string based on the character entered + , promptProcessChar :: Char -> Editor String () -> Editor String () + -- ^ Modifies an editor based on the character entered , promptAction :: String -> Action () -- ^ Action to perform when the value is entered , promptSpecialKey :: Key -> [Modifier] -> Action () @@ -563,7 +564,7 @@ data SelectParams a = SelectParams -- ^ Search function looking for an exact match , spElemDesc :: a -> String -- ^ Provides a string description of an element - , spProcessChar :: Char -> String -> String + , spProcessChar :: Char -> Editor String () -> Editor String () -- ^ Processes a character entered by the user , spCallback :: Maybe Int -> Action () -- ^ The function when the selection is made @@ -795,11 +796,11 @@ esmSubModeL = lens -- | Constructor for a 'ProgState' newProgState :: ProgState newProgState = ProgState - { _database = newDatabase - , _progMode = TitleScreen - , _dbName = "" - , _inputBuffer = "" - , _scroller = viewportScroll () + { _database = newDatabase + , _progMode = TitleScreen + , _dbName = "" + , _editorW = editor () (Just 1) "" + , _scroller = viewportScroll () } -- | Constructor for a 'GameState' diff --git a/src/Mtlstats/Util.hs b/src/Mtlstats/Util.hs index 4a1dda1..06d6fff 100644 --- a/src/Mtlstats/Util.hs +++ b/src/Mtlstats/Util.hs @@ -28,13 +28,17 @@ module Mtlstats.Util , capitalizeName , linesToWidget , linesToWidgetC + , userText ) where import Brick.Types (Widget) import Brick.Widgets.Center (hCenter) import Brick.Widgets.Core (str, vBox) +import Brick.Widgets.Edit (Editor, editContentsL, getEditContents) import Data.Char (isSpace, toUpper) import qualified Data.Map as M +import Data.Text.Zipper (insertChar) +import Lens.Micro ((^.), (&), (%~), to) -- | Attempt to select the element from a list at a given index nth @@ -106,12 +110,13 @@ slice offset len = take len . drop offset capitalizeName :: Char -- ^ The character being input - -> String + -> Editor String () -- ^ The current string - -> String + -> Editor String () -- ^ The resulting string -capitalizeName ch s = s ++ [ch'] +capitalizeName ch e = e & editContentsL %~ insertChar ch' where + s = e^.to userText ch' = if lockFlag s then toUpper ch else ch @@ -133,6 +138,12 @@ linesToWidget = vBox . map (str . keepBlank) linesToWidgetC :: [String] -> Widget () linesToWidgetC = vBox . map (hCenter . str . keepBlank) +-- | Fetches the text from an editor widget +userText :: Editor String () -> String +userText w = case getEditContents w of + (x:_) -> x + [] -> "" + keepBlank :: String -> String keepBlank "" = " " keepBlank s = s diff --git a/test/ActionsSpec.hs b/test/ActionsSpec.hs index 7f17eef..8ac0919 100644 --- a/test/ActionsSpec.hs +++ b/test/ActionsSpec.hs @@ -24,7 +24,7 @@ along with this program. If not, see . module ActionsSpec (spec) where import Control.Monad (replicateM) -import Lens.Micro ((^.), (&), (.~), (?~), (%~)) +import Lens.Micro ((^.), (&), (.~), (?~), (%~), to) import Test.Hspec ( Spec , context @@ -37,9 +37,11 @@ import Test.Hspec import Mtlstats.Actions import Mtlstats.Types +import Mtlstats.Util import qualified Actions.NewGameSpec as NewGame import qualified Actions.EditStandingsSpec as EditStandings +import SpecHelpers import qualified TypesSpec as TS spec :: Spec @@ -49,8 +51,6 @@ spec = describe "Mtlstats.Actions" $ do resetYtdSpec clearRookiesSpec resetStandingsSpec - addCharSpec - removeCharSpec createPlayerSpec createGoalieSpec editSpec @@ -204,29 +204,6 @@ resetStandingsSpec = describe "resetStandings" $ do it "should be reset" $ ps^.database.dbAwayGameStats `shouldBe` newGameStats -addCharSpec :: Spec -addCharSpec = describe "addChar" $ - it "should add the character to the input buffer" $ let - s = newProgState - & inputBuffer .~ "foo" - & addChar 'd' - in s ^. inputBuffer `shouldBe` "food" - -removeCharSpec :: Spec -removeCharSpec = describe "removeChar" $ do - - context "empty" $ - it "should remove the character from the input buffer" $ let - s = removeChar newProgState - in s ^. inputBuffer `shouldBe` "" - - context "not empty" $ - it "should remove the character from the input buffer" $ let - s = newProgState - & inputBuffer .~ "foo" - & removeChar - in s ^. inputBuffer `shouldBe` "fo" - createPlayerSpec :: Spec createPlayerSpec = describe "createPlayer" $ it "should change the mode appropriately" $ let @@ -422,7 +399,7 @@ backHomeSpec = describe "backHome" $ do let input = newProgState & progMode.gameStateL .~ newGameState - & inputBuffer .~ "foo" + & editorW .~ mkEditor "foo" result = backHome input it "should set the program mode back to MainMenu" $ @@ -431,4 +408,4 @@ backHomeSpec = describe "backHome" $ do _ -> False it "should clear the input buffer" $ - result^.inputBuffer `shouldBe` "" + result^.editorW.to userText `shouldBe` "" diff --git a/test/SpecHelpers.hs b/test/SpecHelpers.hs new file mode 100644 index 0000000..6c5fcf9 --- /dev/null +++ b/test/SpecHelpers.hs @@ -0,0 +1,29 @@ +{- + +mtlstats +Copyright (C) Rhéal Lamothe + + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +-} + +module SpecHelpers where + +import Brick.Widgets.Edit (Editor, editContentsL, editor) +import Data.Text.Zipper (gotoEOL) +import Lens.Micro ((&), (%~)) + +mkEditor :: String -> Editor String () +mkEditor str = editor () (Just 1) str & editContentsL %~ gotoEOL diff --git a/test/UtilSpec.hs b/test/UtilSpec.hs index 3c73564..5da4999 100644 --- a/test/UtilSpec.hs +++ b/test/UtilSpec.hs @@ -26,6 +26,8 @@ import Test.Hspec (Spec, context, describe, it, shouldBe) import Mtlstats.Util +import SpecHelpers + spec :: Spec spec = describe "Mtlstats.Util" $ do nthSpec @@ -114,7 +116,7 @@ capitalizeNameSpec :: Spec capitalizeNameSpec = describe "capitalizeName" $ mapM_ (\(label, ch, str, expected) -> context label $ it ("should be " ++ expected) $ - capitalizeName ch str `shouldBe` expected) + userText (capitalizeName ch $ mkEditor str) `shouldBe` expected) -- label, character, string, expected [ ( "initial lower", 'a', "", "A" ) , ( "initial upper", 'A', "", "A" ) From 284a8c67255ce96f3689960844bf24ce7e326bb4 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 31 May 2023 22:19:18 -0400 Subject: [PATCH 15/21] various layout fixes --- src/Mtlstats/Control/EditStandings.hs | 8 ++++++-- src/Mtlstats/Control/NewGame.hs | 13 ++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Mtlstats/Control/EditStandings.hs b/src/Mtlstats/Control/EditStandings.hs index 1ad18ae..a6ffcf3 100644 --- a/src/Mtlstats/Control/EditStandings.hs +++ b/src/Mtlstats/Control/EditStandings.hs @@ -24,7 +24,7 @@ along with this program. If not, see . module Mtlstats.Control.EditStandings (editStandingsC) where import Brick.Types (Widget) -import Brick.Widgets.Core (str, vBox) +import Brick.Widgets.Core (vBox) import Lens.Micro ((^.)) import Mtlstats.Format @@ -34,6 +34,7 @@ import Mtlstats.Prompt import Mtlstats.Prompt.EditStandings import Mtlstats.Types import Mtlstats.Types.Menu +import Mtlstats.Util -- | Controller for the edit standings menu editStandingsC :: EditStandingsMode -> Controller @@ -75,7 +76,10 @@ header s w = let [ ( "HOME", valsFor home ) , ( "ROAD", valsFor away ) ] - in vBox $ map str (table ++ [""]) ++ [w] + in vBox + [ linesToWidget $ table ++ [""] + , w + ] valsFor :: GameStats -> [Int] valsFor gs = diff --git a/src/Mtlstats/Control/NewGame.hs b/src/Mtlstats/Control/NewGame.hs index 318a60d..21a6d13 100644 --- a/src/Mtlstats/Control/NewGame.hs +++ b/src/Mtlstats/Control/NewGame.hs @@ -103,7 +103,7 @@ verifyDataC :: Controller verifyDataC = Controller { drawController = \s -> let gs = s^.progMode.gameStateL - in header s $ vBox $ map str $ + in header s $ linesToWidget $ [""] ++ labelTable [ ( "Date", gameDate gs ) @@ -172,7 +172,7 @@ confirmGoalDataC = Controller [ "" , "Is the above information correct? (Y/N)" ] - in vBox $ map str msg + in linesToWidget msg , handleController = \e -> do case ynHandler e of Just True -> modify recordGoalAssists @@ -201,7 +201,7 @@ getPMinsC = Controller reportC :: Controller reportC = Controller - { drawController = viewport () Vertical . hCenter . vBox . map str . + { drawController = viewport () Vertical . hCenter . linesToWidget . displayReport reportCols , handleController = \e -> do scr <- use scroller @@ -239,8 +239,11 @@ monthHeader s w = let , "NOVEMBER" , "DECEMBER" ] - in header s $ vBox $ map (hCenter . str) - (["MONTH:", ""] ++ table ++ [""]) ++ [w] + in header s $ vBox + [ linesToWidgetC $ + ["MONTH:", ""] ++ table ++ [""] + , w + ] gameGoal :: ProgState -> (Int, Int) gameGoal s = From 134787e1be33d72056f236734171d70d715c733a Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 1 Jun 2023 17:15:29 -0400 Subject: [PATCH 16/21] removed Travis CI configuration file --- .travis.yml | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d6fa9bd..0000000 --- a/.travis.yml +++ /dev/null @@ -1,40 +0,0 @@ -# This is the simple Travis configuration, which is intended for use -# on applications which do not require cross-platform and -# multiple-GHC-version support. For more information and other -# options, see: -# -# https://docs.haskellstack.org/en/stable/travis_ci/ -# -# Copy these contents into the root directory of your Github project in a file -# named .travis.yml - -# Choose a build environment -dist: xenial - -# Do not choose a language; we provide our own build tools. -language: generic - -# Caching so the next build will be fast too. -cache: - directories: - - $HOME/.stack - -# Ensure necessary system libraries are present -addons: - apt: - packages: - - libgmp-dev - -before_install: -# Download and unpack the stack executable -- mkdir -p ~/.local/bin -- export PATH=$HOME/.local/bin:$PATH -- travis_retry curl -L https://get.haskellstack.org/stable/linux-x86_64.tar.gz | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' - -install: -# Build dependencies -- stack --no-terminal --install-ghc test --only-dependencies - -script: -# Build the package, its tests, and its docs and run the tests -- stack --no-terminal test --haddock --no-haddock-deps From 01457dbe6f55df4ea7c706291245c246718fcad9 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 1 Jun 2023 17:18:09 -0400 Subject: [PATCH 17/21] removed signature line --- src/Mtlstats.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Mtlstats.hs b/src/Mtlstats.hs index 3e7406a..f51c6b4 100644 --- a/src/Mtlstats.hs +++ b/src/Mtlstats.hs @@ -62,5 +62,3 @@ handler e = do myAttrMap :: AttrMap myAttrMap = forceAttrMap (white `on` blue) - ---jl From 17b3f9a03e3a64496b1c4925aed889c43818ead0 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 1 Jun 2023 18:39:46 -0400 Subject: [PATCH 18/21] minor stylistic edits --- src/Mtlstats/Control/CreatePlayer.hs | 3 ++- src/Mtlstats/Control/EditGoalie.hs | 5 +++-- src/Mtlstats/Control/EditPlayer.hs | 5 +++-- src/Mtlstats/Control/NewGame.hs | 4 ++-- src/Mtlstats/Control/TitleScreen.hs | 2 +- src/Mtlstats/Prompt.hs | 6 +++--- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Mtlstats/Control/CreatePlayer.hs b/src/Mtlstats/Control/CreatePlayer.hs index 3f55751..e4361e5 100644 --- a/src/Mtlstats/Control/CreatePlayer.hs +++ b/src/Mtlstats/Control/CreatePlayer.hs @@ -24,6 +24,7 @@ module Mtlstats.Control.CreatePlayer (createPlayerC) where import Brick.Widgets.Core (str) import Control.Monad.State.Class (gets, modify) import Lens.Micro ((^.), (.~), (?~), (%~), to) +import Lens.Micro.Mtl ((.=)) import Mtlstats.Actions import Mtlstats.Format @@ -66,7 +67,7 @@ getActiveFlagC :: Controller getActiveFlagC = Controller { drawController = const $ str "Is the player active? (Y/N)" , handleController = \e -> - modify $ progMode.createPlayerStateL.cpsActiveFlag .~ ynHandler e + progMode.createPlayerStateL.cpsActiveFlag .= ynHandler e } confirmCreatePlayerC :: Controller diff --git a/src/Mtlstats/Control/EditGoalie.hs b/src/Mtlstats/Control/EditGoalie.hs index 179db42..b854372 100644 --- a/src/Mtlstats/Control/EditGoalie.hs +++ b/src/Mtlstats/Control/EditGoalie.hs @@ -25,9 +25,10 @@ module Mtlstats.Control.EditGoalie (editGoalieC) where import Brick.Types (Widget) import Brick.Widgets.Core (str, vBox) -import Control.Monad.State.Class (gets, modify) +import Control.Monad.State.Class (modify) import Data.Maybe (fromMaybe) import Lens.Micro ((^.), (.~), (%~)) +import Lens.Micro.Mtl (use) import Mtlstats.Actions import Mtlstats.Handlers @@ -99,7 +100,7 @@ deleteC _ = Controller in str $ hdr ++ "Are you sure you want to delete this goalie? (Y/N)" , handleController = \e -> case ynHandler e of Just True -> do - gets (^.progMode.editGoalieStateL.egsSelectedGoalie) >>= mapM_ + use (progMode.editGoalieStateL.egsSelectedGoalie) >>= mapM_ (\gid -> modify $ database.dbGoalies %~ dropNth gid) modify edit Just False -> modify $ progMode.editGoalieStateL.egsMode .~ EGMenu diff --git a/src/Mtlstats/Control/EditPlayer.hs b/src/Mtlstats/Control/EditPlayer.hs index fb348b5..ed746b3 100644 --- a/src/Mtlstats/Control/EditPlayer.hs +++ b/src/Mtlstats/Control/EditPlayer.hs @@ -23,9 +23,10 @@ module Mtlstats.Control.EditPlayer (editPlayerC) where import Brick.Types (Widget) import Brick.Widgets.Core (emptyWidget, str, vBox) -import Control.Monad.State.Class (gets, modify) +import Control.Monad.State.Class (modify) import Data.Maybe (fromMaybe) import Lens.Micro ((^.), (.~), (%~)) +import Lens.Micro.Mtl (use) import Mtlstats.Actions import Mtlstats.Handlers @@ -90,7 +91,7 @@ deleteC _ = Controller in str $ hdr ++ "Are you sure you want to delete this player? (Y/N)" , handleController = \e -> case ynHandler e of Just True -> do - gets (^.progMode.editPlayerStateL.epsSelectedPlayer) >>= mapM_ + use (progMode.editPlayerStateL.epsSelectedPlayer) >>= mapM_ (\pid -> modify $ database.dbPlayers %~ dropNth pid) modify edit Just False -> modify $ progMode.editPlayerStateL.epsMode .~ EPMenu diff --git a/src/Mtlstats/Control/NewGame.hs b/src/Mtlstats/Control/NewGame.hs index 21a6d13..e96fcd0 100644 --- a/src/Mtlstats/Control/NewGame.hs +++ b/src/Mtlstats/Control/NewGame.hs @@ -37,7 +37,7 @@ import Graphics.Vty.Input.Events , Key (KDown, KHome, KEnter, KUp) ) import Lens.Micro ((^.), (.~)) -import Lens.Micro.Mtl (use) +import Lens.Micro.Mtl ((.=), use) import Mtlstats.Actions import Mtlstats.Actions.NewGame @@ -96,7 +96,7 @@ overtimeFlagC = Controller { drawController = \s -> header s $ str "Did the game go into overtime? (Y/N)" , handleController = \e -> - modify $ progMode.gameStateL.overtimeFlag .~ ynHandler e + progMode.gameStateL.overtimeFlag .= ynHandler e } verifyDataC :: Controller diff --git a/src/Mtlstats/Control/TitleScreen.hs b/src/Mtlstats/Control/TitleScreen.hs index f727add..dbe6167 100644 --- a/src/Mtlstats/Control/TitleScreen.hs +++ b/src/Mtlstats/Control/TitleScreen.hs @@ -47,7 +47,7 @@ titleScreenC = Controller ] , handleController = \case VtyEvent (EvKey _ _) -> modify backHome - _ -> return () + _ -> return () } titleText :: [String] diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 8b26164..6e654fb 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -77,9 +77,9 @@ promptHandler p (VtyEvent (EvKey KEnter [])) = do editorW %= clearEditor promptAction p val promptHandler p (VtyEvent (EvKey (KChar c) [])) = - modify $ editorW %~ promptProcessChar p c + editorW %= promptProcessChar p c promptHandler _ (VtyEvent (EvKey KBS [])) = - modify (editorW.editContentsL %~ deletePrevChar) + editorW.editContentsL %= deletePrevChar promptHandler p (VtyEvent (EvKey k m)) = promptSpecialKey p k m promptHandler _ _ = return () @@ -243,7 +243,7 @@ selectPrompt params = Prompt results = spSearch params sStr db when (n < maxFunKeys) $ whenJust (nth n results) $ \(sel, _) -> do - modify $ editorW %~ clearEditor + editorW %= clearEditor spCallback params $ Just sel _ -> return () } From 251dc90cea38785ff4e8caa843e23ee737d7b696 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 1 Jun 2023 19:06:46 -0400 Subject: [PATCH 19/21] more stylistic changes --- src/Mtlstats/Control/EditGoalie.hs | 8 ++++---- src/Mtlstats/Control/EditPlayer.hs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Mtlstats/Control/EditGoalie.hs b/src/Mtlstats/Control/EditGoalie.hs index b854372..ea05ea4 100644 --- a/src/Mtlstats/Control/EditGoalie.hs +++ b/src/Mtlstats/Control/EditGoalie.hs @@ -27,8 +27,8 @@ import Brick.Types (Widget) import Brick.Widgets.Core (str, vBox) import Control.Monad.State.Class (modify) import Data.Maybe (fromMaybe) -import Lens.Micro ((^.), (.~), (%~)) -import Lens.Micro.Mtl (use) +import Lens.Micro ((^.)) +import Lens.Micro.Mtl ((.=), (%=), use) import Mtlstats.Actions import Mtlstats.Handlers @@ -101,9 +101,9 @@ deleteC _ = Controller , handleController = \e -> case ynHandler e of Just True -> do use (progMode.editGoalieStateL.egsSelectedGoalie) >>= mapM_ - (\gid -> modify $ database.dbGoalies %~ dropNth gid) + (\gid -> database.dbGoalies %= dropNth gid) modify edit - Just False -> modify $ progMode.editGoalieStateL.egsMode .~ EGMenu + Just False -> progMode.editGoalieStateL.egsMode .= EGMenu Nothing -> return () } diff --git a/src/Mtlstats/Control/EditPlayer.hs b/src/Mtlstats/Control/EditPlayer.hs index ed746b3..b60a766 100644 --- a/src/Mtlstats/Control/EditPlayer.hs +++ b/src/Mtlstats/Control/EditPlayer.hs @@ -25,8 +25,8 @@ import Brick.Types (Widget) import Brick.Widgets.Core (emptyWidget, str, vBox) import Control.Monad.State.Class (modify) import Data.Maybe (fromMaybe) -import Lens.Micro ((^.), (.~), (%~)) -import Lens.Micro.Mtl (use) +import Lens.Micro ((^.)) +import Lens.Micro.Mtl ((.=), (%=), use) import Mtlstats.Actions import Mtlstats.Handlers @@ -92,9 +92,9 @@ deleteC _ = Controller , handleController = \e -> case ynHandler e of Just True -> do use (progMode.editPlayerStateL.epsSelectedPlayer) >>= mapM_ - (\pid -> modify $ database.dbPlayers %~ dropNth pid) + (\pid -> database.dbPlayers %= dropNth pid) modify edit - Just False -> modify $ progMode.editPlayerStateL.epsMode .~ EPMenu + Just False -> progMode.editPlayerStateL.epsMode .= EPMenu Nothing -> return () } From 886cf0b24382a8caa1376bccc24c4c5f985300c6 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 1 Jun 2023 19:51:04 -0400 Subject: [PATCH 20/21] even more stylistic changes I hope to God I'm done with these now. --- src/Mtlstats.hs | 5 ++--- src/Mtlstats/Control/CreateGoalie.hs | 3 ++- src/Mtlstats/Control/CreatePlayer.hs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Mtlstats.hs b/src/Mtlstats.hs index f51c6b4..ba904da 100644 --- a/src/Mtlstats.hs +++ b/src/Mtlstats.hs @@ -26,14 +26,13 @@ import Brick.Main (App (..), halt, showFirstCursor) import Brick.Types (BrickEvent (VtyEvent), Widget) import Brick.Util (on) import Brick.Widgets.Core (fill) +import Control.Monad.State.Class (gets) import Graphics.Vty.Attributes.Color (blue, white) import Graphics.Vty.Input.Events ( Event (EvKey) , Modifier (MCtrl) , Key (KChar) ) -import Lens.Micro (to) -import Lens.Micro.Mtl (use) import Mtlstats.Control import Mtlstats.Types @@ -57,7 +56,7 @@ draw s = handler :: Handler () handler (VtyEvent (EvKey (KChar 'c') [MCtrl])) = halt handler e = do - c <- use (to dispatch) + c <- gets dispatch handleController c e myAttrMap :: AttrMap diff --git a/src/Mtlstats/Control/CreateGoalie.hs b/src/Mtlstats/Control/CreateGoalie.hs index 9a5852e..0bfaebe 100644 --- a/src/Mtlstats/Control/CreateGoalie.hs +++ b/src/Mtlstats/Control/CreateGoalie.hs @@ -24,6 +24,7 @@ module Mtlstats.Control.CreateGoalie (createGoalieC) where import Brick.Widgets.Core (str) import Control.Monad.State.Class (gets, modify) import Lens.Micro ((^.), (.~), (?~), (%~), to) +import Lens.Micro.Mtl ((.=)) import Mtlstats.Actions import Mtlstats.Format @@ -63,7 +64,7 @@ getActiveFlagC :: Controller getActiveFlagC = Controller { drawController = const $ str "Is this goalie active? (Y/N)" , handleController = \e -> - modify $ progMode.createGoalieStateL.cgsActiveFlag .~ ynHandler e + progMode.createGoalieStateL.cgsActiveFlag .= ynHandler e } confirmCreateGoalieC :: Controller diff --git a/src/Mtlstats/Control/CreatePlayer.hs b/src/Mtlstats/Control/CreatePlayer.hs index e4361e5..7db55a2 100644 --- a/src/Mtlstats/Control/CreatePlayer.hs +++ b/src/Mtlstats/Control/CreatePlayer.hs @@ -24,7 +24,7 @@ module Mtlstats.Control.CreatePlayer (createPlayerC) where import Brick.Widgets.Core (str) import Control.Monad.State.Class (gets, modify) import Lens.Micro ((^.), (.~), (?~), (%~), to) -import Lens.Micro.Mtl ((.=)) +import Lens.Micro.Mtl ((.=), use) import Mtlstats.Actions import Mtlstats.Format @@ -85,7 +85,7 @@ confirmCreatePlayerC = Controller , "Create player: are you sure? (Y/N)" ] , handleController = \e -> do - cps <- gets (^.progMode.createPlayerStateL) + cps <- use $ progMode.createPlayerStateL let success = cps^.cpsSuccessCallback failure = cps^.cpsFailureCallback From e0efe2657f2ce9d9c01d7e9e2589a7d846474251 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Fri, 2 Jun 2023 15:26:22 -0400 Subject: [PATCH 21/21] ynHandler should ignore keypresses with modifier keys --- src/Mtlstats/Handlers.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mtlstats/Handlers.hs b/src/Mtlstats/Handlers.hs index 59177c9..846b90a 100644 --- a/src/Mtlstats/Handlers.hs +++ b/src/Mtlstats/Handlers.hs @@ -27,7 +27,7 @@ import Graphics.Vty.Input.Events (Event (EvKey), Key (KChar)) -- | Handler for a yes/no prompt ynHandler :: BrickEvent () () -> Maybe Bool -ynHandler (VtyEvent (EvKey (KChar c) _)) = case toUpper c of +ynHandler (VtyEvent (EvKey (KChar c) [])) = case toUpper c of 'Y' -> Just True 'N' -> Just False _ -> Nothing