mirror of
https://github.com/Yvee1/hascard.git
synced 2024-11-22 04:33:06 +03:00
Add scrolling functionality to cards & change F1 to Ctrl+F1
This commit is contained in:
parent
3132f7a44b
commit
0b877aa855
94
cards/long.txt
Normal file
94
cards/long.txt
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Very long definition of Haskell
|
||||||
|
Haskell (/ˈhæskəl/) is a general-purpose, statically-typed, purely functional programming language with type inference and lazy evaluation.
|
||||||
|
Designed for teaching, research, and industrial applications, Haskell has pioneered a number of programming language features such as type classes, which enable type-safe operator overloading, and monadic input/output (IO).
|
||||||
|
It is named after logician Haskell Curry. Haskell's main implementation is the Glasgow Haskell Compiler (GHC).
|
||||||
|
|
||||||
|
Haskell's semantics are historically based on those of the Miranda programming language, which served to focus the efforts of the initial Haskell working group.
|
||||||
|
The last formal specification of the language was made in July 2010, while the development of GHC continues to expand Haskell via language extensions.
|
||||||
|
|
||||||
|
Haskell is used in academia and industry.
|
||||||
|
As of May 2021, Haskell was the 28th most popular programming language by Google searches for tutorials, and made up less than 1% of active users on the GitHub source code repository.
|
||||||
|
|
||||||
|
After the release of Miranda by Research Software Ltd. in 1985, interest in lazy functional languages grew. By 1987, more than a dozen non-strict, purely functional programming languages existed.
|
||||||
|
Miranda was the most widely used, but it was proprietary software.
|
||||||
|
At the conference on Functional Programming Languages and Computer Architecture (FPCA '87) in Portland, Oregon, there was a strong consensus that a committee be formed to define an open standard for such languages.
|
||||||
|
The committee's purpose was to consolidate existing functional languages into a common one to serve as a basis for future research in functional-language design.
|
||||||
|
Haskell was developed by a committee, attempting to bring together off the shelf solutions where possible.
|
||||||
|
|
||||||
|
Type classes, which enable type-safe operator overloading, were first proposed by Philip Wadler and Stephen Blott to address the ad-hoc handling of equality types and arithmetic overloading in languages at the time.
|
||||||
|
|
||||||
|
In early versions of Haskell up until and including version 1.2, user interaction and IO (input and output) were handled by both streams based and continuation based mechanisms which were widely considered unsatisfactory.
|
||||||
|
In version 1.3, monadic IO was introduced, along with the generalisation of type classes to higher kinds (type constructors).
|
||||||
|
Along with "do notation", which provides syntactic sugar for the Monad type class, this gave Haskell an effect system that maintained referential transparency and was convenient.
|
||||||
|
|
||||||
|
Other notable changes in early versions were the approach to the 'seq' function, which creates a data dependency between values, and is used in lazy languages to avoid excessive memory consumption; with it moving from a type class to a standard function to make refactoring more practical.
|
||||||
|
|
||||||
|
The first version of Haskell ("Haskell 1.0") was defined in 1990.
|
||||||
|
The committee's efforts resulted in a series of language definitions (1.0, 1.1, 1.2, 1.3, 1.4).
|
||||||
|
|
||||||
|
---
|
||||||
|
# Very many options
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
- 4
|
||||||
|
- 5
|
||||||
|
- 6
|
||||||
|
- 7
|
||||||
|
- 8
|
||||||
|
- 9
|
||||||
|
* Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten
|
||||||
|
---
|
||||||
|
|
||||||
|
# Very many options
|
||||||
|
[ ] 1
|
||||||
|
[ ] 2
|
||||||
|
[ ] 3
|
||||||
|
[ ] 4
|
||||||
|
[*] Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five Five
|
||||||
|
[ ] 6
|
||||||
|
[ ] 7
|
||||||
|
[ ] 8
|
||||||
|
[ ] 9
|
||||||
|
[*] Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten Ten
|
||||||
|
|
||||||
|
---
|
||||||
|
# Very long open question of Haskell
|
||||||
|
Haskell (/ˈhæskəl/) is a general-purpose, statically-typed, purely functional programming language with type inference and lazy evaluation.
|
||||||
|
Designed for teaching, research, and industrial applications, Haskell has pioneered a number of programming language features such as type classes, which enable type-safe operator overloading, and monadic input/output (IO).
|
||||||
|
It is named after logician _Haskell Curry_. Haskell's main implementation is the Glasgow Haskell Compiler (GHC).
|
||||||
|
|
||||||
|
Haskell's semantics are historically based on those of the Miranda programming language, which served to focus the efforts of the initial Haskell working group.
|
||||||
|
The last formal specification of the language was made in July 2010, while the development of GHC continues to expand Haskell via language extensions.
|
||||||
|
|
||||||
|
Haskell is used in academia and industry.
|
||||||
|
As of May 2021, Haskell was the _28_th most popular programming language by Google searches for tutorials, and made up less than 1% of active users on the GitHub source code repository.
|
||||||
|
|
||||||
|
After the release of Miranda by Research Software Ltd. in 1985, interest in lazy functional languages grew. By 1987, more than a dozen non-strict, purely functional programming languages existed.
|
||||||
|
Miranda was the most widely used, but it was proprietary software. At the conference on Functional Programming Languages and Computer Architecture (FPCA '87) in Portland, Oregon, there was a strong consensus that a committee be formed to define an open standard for such languages.
|
||||||
|
The committee's purpose was to consolidate existing functional languages into a common one to serve as a basis for future research in functional-language design.
|
||||||
|
Haskell was developed by a committee, attempting to bring together off the shelf solutions where possible.
|
||||||
|
|
||||||
|
Type classes, which enable type-safe operator overloading, were first proposed by Philip Wadler and Stephen Blott to address the ad-hoc handling of equality types and arithmetic overloading in languages at the time.
|
||||||
|
|
||||||
|
In early versions of Haskell up until and including version 1.2, user interaction and IO (input and output) were handled by both streams based and continuation based mechanisms which were widely considered unsatisfactory.
|
||||||
|
In version 1.3, monadic IO was introduced, along with the generalisation of type classes to higher kinds (type constructors).
|
||||||
|
Along with "do notation", which provides syntactic sugar for the Monad type class, this gave Haskell an effect system that maintained referential transparency and was convenient.
|
||||||
|
|
||||||
|
Other notable changes in early versions were the approach to the '_seq_' function, which creates a data dependency between values, and is used in lazy languages to avoid excessive memory consumption; with it moving from a type class to a standard function to make refactoring more practical.
|
||||||
|
|
||||||
|
The first version of Haskell ("Haskell 1.0") was defined in 1990.
|
||||||
|
The committee's efforts resulted in a series of language definitions (1.0, 1.1, 1.2, 1.3, 1.4).
|
||||||
|
|
||||||
|
---
|
||||||
|
# Very long reorder
|
||||||
|
1. a
|
||||||
|
2. b
|
||||||
|
3. c
|
||||||
|
4. d
|
||||||
|
5. e
|
||||||
|
7. g
|
||||||
|
6. f
|
||||||
|
8. h
|
||||||
|
9. i
|
||||||
|
10. j
|
@ -37,6 +37,7 @@ data Name =
|
|||||||
| MainMenuList
|
| MainMenuList
|
||||||
| InfoViewport
|
| InfoViewport
|
||||||
| SettingsViewport
|
| SettingsViewport
|
||||||
|
| CardViewport Int
|
||||||
| RecentsList
|
| RecentsList
|
||||||
| FileBrowserList
|
| FileBrowserList
|
||||||
| SBClick T.ClickableScrollbarElement Name
|
| SBClick T.ClickableScrollbarElement Name
|
||||||
|
212
src/UI/Cards.hs
212
src/UI/Cards.hs
@ -23,6 +23,7 @@ import UI.Attributes
|
|||||||
import UI.BrickHelpers
|
import UI.BrickHelpers
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
import Data.List (intercalate)
|
import Data.List (intercalate)
|
||||||
|
import qualified Brick.Types as BT
|
||||||
import qualified Data.List.NonEmpty as NE
|
import qualified Data.List.NonEmpty as NE
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
@ -44,7 +45,7 @@ drawInfo s = if not (s ^. showControls) then emptyWidget else
|
|||||||
DefinitionState {} -> ", ENTER: flip card / continue"
|
DefinitionState {} -> ", ENTER: flip card / continue"
|
||||||
MultipleChoiceState {} -> ", ENTER: submit answer / continue"
|
MultipleChoiceState {} -> ", ENTER: submit answer / continue"
|
||||||
MultipleAnswerState {} -> ", ENTER: select / continue, c: submit selection"
|
MultipleAnswerState {} -> ", ENTER: select / continue, c: submit selection"
|
||||||
OpenQuestionState {} -> ", LEFT/RIGHT/TAB: navigate gaps, ENTER: submit answer / continue, F1: show answer"
|
OpenQuestionState {} -> ", LEFT/RIGHT/TAB: navigate gaps, ENTER: submit answer / continue, Ctrl+F1: show answer"
|
||||||
ReorderState {} -> ", ENTER: grab, c: submit answer"
|
ReorderState {} -> ", ENTER: grab, c: submit answer"
|
||||||
|
|
||||||
drawCardBox :: Widget Name -> Widget Name
|
drawCardBox :: Widget Name -> Widget Name
|
||||||
@ -67,31 +68,38 @@ drawFooter s = if s^.reviewMode
|
|||||||
endCard = maybe False (isFinalPopup . view popupState) (s^.popup)
|
endCard = maybe False (isFinalPopup . view popupState) (s^.popup)
|
||||||
|
|
||||||
drawCardUI :: CS -> Widget Name
|
drawCardUI :: CS -> Widget Name
|
||||||
drawCardUI s = let p = 1 in
|
drawCardUI s =
|
||||||
joinBorders $ drawCardBox $ (<=> drawFooter s) $
|
let card = (s ^. shownCards) !! (s ^. index)
|
||||||
case (s ^. shownCards) !! (s ^. index) of
|
in
|
||||||
Definition title _ descr -> drawHeader title
|
joinBorders $
|
||||||
<=> B.hBorder
|
drawCardBox $
|
||||||
<=> padLeftRight p (drawDef s descr <=> str " ")
|
drawHeader card
|
||||||
|
<=>
|
||||||
|
B.hBorder
|
||||||
|
<=>
|
||||||
|
scrollableViewportPercent 60 (CardViewport (s ^. index))
|
||||||
|
(drawContent s card)
|
||||||
|
<=>
|
||||||
|
str " "
|
||||||
|
<=>
|
||||||
|
drawFooter s
|
||||||
|
|
||||||
MultipleChoice question _ correct others -> drawHeader question
|
drawHeader :: Card -> Widget Name
|
||||||
<=> B.hBorder
|
drawHeader (Definition title _ _) = drawTitle title
|
||||||
<=> padLeftRight p (drawChoices s (listMultipleChoice correct others) <=> str " ")
|
drawHeader (MultipleChoice question _ _ _) = drawTitle question
|
||||||
|
drawHeader (OpenQuestion question _ _) = drawTitle question
|
||||||
|
drawHeader (MultipleAnswer question _ _) = drawTitle question
|
||||||
|
drawHeader (Reorder question _ _) = drawTitle question
|
||||||
|
|
||||||
OpenQuestion title _ perforated -> drawHeader title
|
drawContent :: CS -> Card -> Widget Name
|
||||||
<=> B.hBorder
|
drawContent s (Definition _ _ descr) = padLeftRight 1 $ drawDef s descr
|
||||||
<=> padLeftRight p (atLeastV 1 (drawPerforated s perforated) <=> str " ")
|
drawContent s (MultipleChoice _ _ correct others) = padLeftRight 1 $ drawChoices s (listMultipleChoice correct others)
|
||||||
|
drawContent s (OpenQuestion _ _ perforated) = padLeftRight 1 $ drawPerforated s perforated
|
||||||
|
drawContent s (MultipleAnswer _ _ options) = padRight (Pad 1) $ drawOptions s options
|
||||||
|
drawContent s (Reorder{}) = padLeftRight 1 $ drawReorder s
|
||||||
|
|
||||||
MultipleAnswer question _ options -> drawHeader question
|
drawTitle :: String -> Widget n
|
||||||
<=> B.hBorder
|
drawTitle title = withAttr titleAttr $
|
||||||
<=> padRight (Pad p) (drawOptions s options <=> str " ")
|
|
||||||
|
|
||||||
Reorder question _ _ -> drawHeader question
|
|
||||||
<=> B.hBorder
|
|
||||||
<=> padLeftRight p (drawReorder s <=> str " ")
|
|
||||||
|
|
||||||
drawHeader :: String -> Widget Name
|
|
||||||
drawHeader title = withAttr titleAttr $
|
|
||||||
padLeftRight 1 $
|
padLeftRight 1 $
|
||||||
hCenteredStrWrap title
|
hCenteredStrWrap title
|
||||||
|
|
||||||
@ -128,10 +136,11 @@ drawChoices s options = case (s ^. cardState, s ^. currentCard) of
|
|||||||
(MultipleChoiceState {_highlighted=i, _tried=kvs}, MultipleChoice _ _ (CorrectOption k _) _) -> vBox formattedOptions
|
(MultipleChoiceState {_highlighted=i, _tried=kvs}, MultipleChoice _ _ (CorrectOption k _) _) -> vBox formattedOptions
|
||||||
|
|
||||||
where formattedOptions :: [Widget Name]
|
where formattedOptions :: [Widget Name]
|
||||||
formattedOptions = [ prefix <+> coloring (drawDescr opt) |
|
formattedOptions = [ visibility $ prefix <+> coloring (drawDescr opt) |
|
||||||
(j, opt) <- zip [0..] options,
|
(j, opt) <- zip [0..] options,
|
||||||
let prefix = if i == j then withAttr highlightedChoiceAttr (str "* ") else str " "
|
let prefix = if i == j then withAttr highlightedChoiceAttr (str "* ") else str " "
|
||||||
chosen = M.findWithDefault False j kvs
|
chosen = M.findWithDefault False j kvs
|
||||||
|
visibility = if i == j && not chosen then visible else id
|
||||||
coloring = case (chosen, j==k) of
|
coloring = case (chosen, j==k) of
|
||||||
(False, _) -> id
|
(False, _) -> id
|
||||||
(True, False) -> withAttr incorrectChoiceAttr
|
(True, False) -> withAttr incorrectChoiceAttr
|
||||||
@ -143,10 +152,11 @@ drawOptions :: CS -> NonEmpty Option -> Widget Name
|
|||||||
drawOptions s = case (s ^. cardState, s ^. currentCard) of
|
drawOptions s = case (s ^. cardState, s ^. currentCard) of
|
||||||
(MultipleAnswerState {_highlighted=j, _selected=kvs, _entered=submitted}, _) ->
|
(MultipleAnswerState {_highlighted=j, _selected=kvs, _entered=submitted}, _) ->
|
||||||
vBox . NE.toList . NE.map drawOption . (`NE.zip` NE.fromList [0..])
|
vBox . NE.toList . NE.map drawOption . (`NE.zip` NE.fromList [0..])
|
||||||
where drawOption (Option kind text, i) = coloring (str "[") <+> coloring (highlighting (str symbol)) <+> coloring (str "] ") <+> drawDescr text
|
where drawOption (Option kind text, i) = visibility $ coloring (str "[") <+> coloring (highlighting (str symbol)) <+> coloring (str "] ") <+> drawDescr text
|
||||||
where symbol = if (i == j && not submitted) || enabled then "*" else " "
|
where symbol = if (i == j && not submitted) || enabled then "*" else " "
|
||||||
enabled = M.findWithDefault False i kvs
|
enabled = M.findWithDefault False i kvs
|
||||||
highlighting = if i == j && not submitted then withAttr highlightedOptAttr else id
|
highlighting = if i == j && not submitted then withAttr highlightedOptAttr else id
|
||||||
|
visibility = if i == j && not submitted then visible else id
|
||||||
coloring = case (submitted, enabled, kind) of
|
coloring = case (submitted, enabled, kind) of
|
||||||
(True, True, Correct) -> withAttr correctOptAttr
|
(True, True, Correct) -> withAttr correctOptAttr
|
||||||
(True, False, Incorrect) -> withAttr correctOptAttr
|
(True, False, Incorrect) -> withAttr correctOptAttr
|
||||||
@ -174,20 +184,22 @@ makeSentenceWidget w state = vBox . fst . makeSentenceWidget' 0 0
|
|||||||
makeSentenceWidget' padding i (Perforated pre _ post) = case state ^. cardState of
|
makeSentenceWidget' padding i (Perforated pre _ post) = case state ^. cardState of
|
||||||
OpenQuestionState {_gapInput = kvs, _highlighted=j, _entered=submitted, _correctGaps=cgs} ->
|
OpenQuestionState {_gapInput = kvs, _highlighted=j, _entered=submitted, _correctGaps=cgs} ->
|
||||||
let (ws, n, fit') = wrapStringWithPadding padding w pre
|
let (ws, n, fit') = wrapStringWithPadding padding w pre
|
||||||
gap = M.findWithDefault "" i kvs
|
stored = M.findWithDefault "" i kvs
|
||||||
|
gap = if stored == "" then "░" else stored
|
||||||
n' = w - n - textWidth gap
|
n' = w - n - textWidth gap
|
||||||
|
|
||||||
cursor :: Widget Name -> Widget Name
|
cursor :: Widget Name -> Widget Name
|
||||||
-- i is the index of the gap that we are drawing; j is the gap that is currently selected
|
-- i is the index of the gap that we are drawing; j is the gap that is currently selected
|
||||||
cursor = if i == j then showCursor Ordinary (Location (textWidth gap, 0)) else id
|
cursor = if i == j then showCursor Ordinary (Location (textWidth gap, 0)) else id
|
||||||
|
|
||||||
|
visibility = if i == j && not submitted then visible else id
|
||||||
correct = M.findWithDefault False i cgs
|
correct = M.findWithDefault False i cgs
|
||||||
coloring = case (submitted, correct) of
|
coloring = case (submitted, correct) of
|
||||||
(False, _) -> withAttr gapAttr
|
(False, _) -> withAttr gapAttr
|
||||||
(True, False) -> withAttr incorrectGapAttr
|
(True, False) -> withAttr incorrectGapAttr
|
||||||
(True, True) -> withAttr correctGapAttr
|
(True, True) -> withAttr correctGapAttr
|
||||||
|
|
||||||
gapWidget = cursor $ coloring (str gap) in
|
gapWidget = visibility . cursor $ coloring (str gap) in
|
||||||
|
|
||||||
if n' >= 0
|
if n' >= 0
|
||||||
then let (ws1@(w':ws'), fit) = makeSentenceWidget' (w-n') (i+1) post in
|
then let (ws1@(w':ws'), fit) = makeSentenceWidget' (w-n') (i+1) post in
|
||||||
@ -230,13 +242,14 @@ drawReorder s = case (s ^. cardState, s ^. currentCard) of
|
|||||||
(True, False) -> withAttr highlightedElementAttr
|
(True, False) -> withAttr highlightedElementAttr
|
||||||
_ -> id
|
_ -> id
|
||||||
|
|
||||||
|
visibility = if i == j && not submitted then visible else id
|
||||||
|
|
||||||
number =
|
number =
|
||||||
case (submitted, i+1 == k) of
|
case (submitted, i+1 == k) of
|
||||||
(False, _) -> str (show (i+1) <> ". ")
|
(False, _) -> str (show (i+1) <> ". ")
|
||||||
(True, False) -> withAttr incorrectElementAttr (str (show k <> ". "))
|
(True, False) -> withAttr incorrectElementAttr (str (show k <> ". "))
|
||||||
(True, True ) -> withAttr correctElementAttr (str (show k <> ". "))
|
(True, True ) -> withAttr correctElementAttr (str (show k <> ". "))
|
||||||
in
|
in visibility $ number <+> color (drawDescr text)
|
||||||
number <+> color (drawDescr text)
|
|
||||||
|
|
||||||
_ -> error "cardstate mismatch"
|
_ -> error "cardstate mismatch"
|
||||||
|
|
||||||
@ -247,6 +260,12 @@ halt' :: EventM n GlobalState ()
|
|||||||
halt' = removeToModeOrQuit' beforeMoving CardSelector
|
halt' = removeToModeOrQuit' beforeMoving CardSelector
|
||||||
where beforeMoving = zoom css refreshRecents
|
where beforeMoving = zoom css refreshRecents
|
||||||
|
|
||||||
|
scroll :: CS -> Int -> EventM Name s ()
|
||||||
|
scroll s = scroll' $ s ^. index
|
||||||
|
|
||||||
|
scroll' :: Int -> Int -> EventM Name s ()
|
||||||
|
scroll' i = vScrollBy $ viewportScroll $ CardViewport i
|
||||||
|
|
||||||
handleEvent :: BrickEvent Name Event -> EventM Name GlobalState ()
|
handleEvent :: BrickEvent Name Event -> EventM Name GlobalState ()
|
||||||
handleEvent (VtyEvent e) =
|
handleEvent (VtyEvent e) =
|
||||||
-- let update = updateCS gs
|
-- let update = updateCS gs
|
||||||
@ -268,8 +287,15 @@ handleEvent (VtyEvent e) =
|
|||||||
then if not (s^.reviewMode) then next
|
then if not (s^.reviewMode) then next
|
||||||
else cs.popup ?= correctPopup
|
else cs.popup ?= correctPopup
|
||||||
else cs.cardState.flipped %= not
|
else cs.cardState.flipped %= not
|
||||||
|
V.EvKey V.KUp [] -> up
|
||||||
|
V.EvKey (V.KChar 'k') [] -> up
|
||||||
|
V.EvKey V.KDown [] -> down
|
||||||
|
V.EvKey (V.KChar 'j') [] -> down
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
|
|
||||||
|
where up = when f $ scroll s (-1)
|
||||||
|
down = when f $ scroll s 1
|
||||||
|
|
||||||
(MultipleChoiceState {_highlighted = i, _number = n, _tried = kvs}, MultipleChoice _ _ (CorrectOption j _) _) ->
|
(MultipleChoiceState {_highlighted = i, _number = n, _tried = kvs}, MultipleChoice _ _ (CorrectOption j _) _) ->
|
||||||
case ev of
|
case ev of
|
||||||
V.EvKey V.KUp [] -> up
|
V.EvKey V.KUp [] -> up
|
||||||
@ -286,11 +312,13 @@ handleEvent (VtyEvent e) =
|
|||||||
|
|
||||||
where frozen = M.findWithDefault False j kvs
|
where frozen = M.findWithDefault False j kvs
|
||||||
|
|
||||||
down = when (i < n-1 && not frozen) $
|
down = if not frozen
|
||||||
cs.cardState.highlighted += 1
|
then when (i < n-1) $ cs.cardState.highlighted += 1
|
||||||
|
else scroll s 1
|
||||||
|
|
||||||
up = when (i > 0 && not frozen) $
|
up = if not frozen
|
||||||
cs.cardState.highlighted -= 1
|
then when (i > 0) $ cs.cardState.highlighted -= 1
|
||||||
|
else scroll s (-1)
|
||||||
|
|
||||||
correctlyAnswered = i == j && M.size (M.filter id kvs) == 1
|
correctlyAnswered = i == j && M.size (M.filter id kvs) == 1
|
||||||
|
|
||||||
@ -320,72 +348,85 @@ handleEvent (VtyEvent e) =
|
|||||||
|
|
||||||
where frozen = submitted
|
where frozen = submitted
|
||||||
|
|
||||||
down = when (i < n-1 && not frozen) $
|
down = if not frozen
|
||||||
cs.cardState.highlighted += 1
|
then when (i < n-1) $ cs.cardState.highlighted += 1
|
||||||
|
else scroll s 1
|
||||||
|
|
||||||
up = when (i > 0 && not frozen) $
|
up = if not frozen
|
||||||
cs.cardState.highlighted -= 1
|
then when (i > 0) $ cs.cardState.highlighted -= 1
|
||||||
|
else scroll s (-1)
|
||||||
|
|
||||||
correctlyAnswered = NE.toList (NE.map isOptionCorrect opts) == map snd (M.toAscList kvs)
|
correctlyAnswered = NE.toList (NE.map isOptionCorrect opts) == map snd (M.toAscList kvs)
|
||||||
|
|
||||||
(OpenQuestionState {_highlighted = i, _number = n, _gapInput = kvs, _correctGaps = cGaps, _failed=fail}, OpenQuestion _ _ perforated) ->
|
(OpenQuestionState {_highlighted = i, _number = n, _gapInput = kvs, _correctGaps = cGaps, _failed=fail}, OpenQuestion _ _ perforated) ->
|
||||||
let frozen = M.foldr (&&) True cGaps in
|
case ev of
|
||||||
case ev of
|
V.EvKey (V.KFun 1) [V.MCtrl] -> zoom (cs.cardState) $ do
|
||||||
V.EvKey (V.KFun 1) [] -> zoom (cs.cardState) $ do
|
gapInput .= correctAnswers
|
||||||
gapInput .= correctAnswers
|
entered .= True
|
||||||
entered .= True
|
failed .= True
|
||||||
failed .= True
|
correctGaps .= M.fromAscList [(i, True) | i <- [0..n-1]]
|
||||||
correctGaps .= M.fromAscList [(i, True) | i <- [0..n-1]]
|
where correctAnswers = M.fromAscList $ zip [0..] $ map NE.head (sentenceToGaps (perforatedToSentence perforated))
|
||||||
where correctAnswers = M.fromAscList $ zip [0..] $ map NE.head (sentenceToGaps (perforatedToSentence perforated))
|
|
||||||
|
|
||||||
V.EvKey (V.KChar '\t') [] -> zoom (cs.cardState) $ do
|
V.EvKey (V.KChar '\t') [] -> zoom (cs.cardState) $ do
|
||||||
if i < n - 1 && not frozen
|
if i < n - 1 && not frozen
|
||||||
then highlighted += 1
|
then highlighted += 1
|
||||||
else highlighted .= 0
|
else highlighted .= 0
|
||||||
|
|
||||||
V.EvKey V.KRight [] ->
|
V.EvKey V.KRight [] ->
|
||||||
when (i < n - 1 && not frozen) $
|
when (i < n - 1 && not frozen) $
|
||||||
cs.cardState.highlighted += 1
|
cs.cardState.highlighted += 1
|
||||||
|
|
||||||
V.EvKey V.KLeft [] ->
|
V.EvKey V.KLeft [] ->
|
||||||
when (i > 0 && not frozen) $
|
when (i > 0 && not frozen) $
|
||||||
cs.cardState.highlighted -= 1
|
cs.cardState.highlighted -= 1
|
||||||
|
|
||||||
-- C-w deletes a word back (eg. "test test" -> "test")
|
-- C-w deletes a word back (eg. "test test" -> "test")
|
||||||
V.EvKey (V.KChar 'w') [V.MCtrl] -> zoom (cs.cardState) $ do
|
V.EvKey (V.KChar 'w') [V.MCtrl] -> zoom (cs.cardState) $ do
|
||||||
unless frozen $ gapInput.ix i %= backword
|
unless frozen $ gapInput.ix i %= backword
|
||||||
where backword "" = ""
|
where backword "" = ""
|
||||||
backword xs = unwords . init . words $ xs
|
backword xs = unwords . init . words $ xs
|
||||||
|
|
||||||
V.EvKey (V.KChar c) [] -> zoom (cs.cardState) $ do
|
V.EvKey V.KUp [] -> up
|
||||||
unless frozen $ gapInput.at i.non "" %= (++[c])
|
V.EvKey V.KDown [] -> down
|
||||||
|
|
||||||
V.EvKey V.KEnter [] -> case (frozen, fail) of
|
V.EvKey (V.KChar c) [] -> zoom (cs.cardState) $ do
|
||||||
(False, _) -> zoom cs $ do
|
unless frozen $ gapInput.at i.non "" %= (++[c])
|
||||||
let sentence = perforatedToSentence perforated
|
case c of
|
||||||
gaps = sentenceToGaps sentence
|
'k' -> up
|
||||||
|
'j' -> down
|
||||||
|
_ -> return ()
|
||||||
|
|
||||||
wordIsCorrect :: String -> NonEmpty String -> Bool
|
V.EvKey V.KEnter [] -> case (frozen, fail) of
|
||||||
wordIsCorrect = if s^.isCaseSensitive
|
(False, _) -> zoom cs $ do
|
||||||
then elem
|
let sentence = perforatedToSentence perforated
|
||||||
else (\word possibilites -> map toLower word `elem` NE.map (map toLower) possibilites)
|
gaps = sentenceToGaps sentence
|
||||||
|
|
||||||
cardState.correctGaps %= M.mapWithKey (\j _ -> M.findWithDefault "" j kvs `wordIsCorrect` (gaps !! j))
|
wordIsCorrect :: String -> NonEmpty String -> Bool
|
||||||
cardState.entered .= True
|
wordIsCorrect = if s^.isCaseSensitive
|
||||||
|
then elem
|
||||||
|
else (\word possibilites -> map toLower word `elem` NE.map (map toLower) possibilites)
|
||||||
|
|
||||||
unlessM (M.foldr (&&) True <$> use (cardState.correctGaps)) $
|
cardState.correctGaps %= M.mapWithKey (\j _ -> M.findWithDefault "" j kvs `wordIsCorrect` (gaps !! j))
|
||||||
cardState.failed .= True
|
cardState.entered .= True
|
||||||
|
|
||||||
(_, True) -> next
|
unlessM (M.foldr (&&) True <$> use (cardState.correctGaps)) $
|
||||||
(_, False) -> do
|
cardState.failed .= True
|
||||||
cs.correctCards %= (s^.index:)
|
|
||||||
next
|
|
||||||
|
|
||||||
V.EvKey V.KBS [] -> unless frozen $
|
(_, True) -> next
|
||||||
cs.cardState.gapInput.ix i %= backspace
|
(_, False) -> do
|
||||||
where backspace "" = ""
|
cs.correctCards %= (s^.index:)
|
||||||
backspace xs = init xs
|
next
|
||||||
_ -> return ()
|
|
||||||
|
V.EvKey V.KBS [] -> unless frozen $
|
||||||
|
cs.cardState.gapInput.ix i %= backspace
|
||||||
|
where backspace "" = ""
|
||||||
|
backspace xs = init xs
|
||||||
|
|
||||||
|
_ -> return ()
|
||||||
|
|
||||||
|
where frozen = M.foldr (&&) True cGaps
|
||||||
|
down = when frozen $ scroll s 1
|
||||||
|
up = when frozen $ scroll s (-1)
|
||||||
|
|
||||||
(ReorderState {_highlighted = i, _entered = submitted, _grabbed=dragging, _number = n, _order = kvs }, Reorder _ _ elts) ->
|
(ReorderState {_highlighted = i, _entered = submitted, _grabbed=dragging, _number = n, _order = kvs }, Reorder _ _ elts) ->
|
||||||
case ev of
|
case ev of
|
||||||
@ -407,7 +448,7 @@ handleEvent (VtyEvent e) =
|
|||||||
|
|
||||||
down = zoom (cs.cardState) $
|
down = zoom (cs.cardState) $
|
||||||
case (frozen, i < n - 1, dragging) of
|
case (frozen, i < n - 1, dragging) of
|
||||||
(True, _, _) -> return ()
|
(True, _, _) -> scroll s 1
|
||||||
(_, False, _) -> return ()
|
(_, False, _) -> return ()
|
||||||
(_, _, False) -> highlighted += 1
|
(_, _, False) -> highlighted += 1
|
||||||
(_, _, True) -> do highlighted += 1
|
(_, _, True) -> do highlighted += 1
|
||||||
@ -415,7 +456,7 @@ handleEvent (VtyEvent e) =
|
|||||||
|
|
||||||
up = zoom (cs.cardState) $
|
up = zoom (cs.cardState) $
|
||||||
case (frozen, i > 0, dragging) of
|
case (frozen, i > 0, dragging) of
|
||||||
(True, _, _) -> return ()
|
(True, _, _) -> scroll s (-1)
|
||||||
(_, False, _) -> return ()
|
(_, False, _) -> return ()
|
||||||
(_, _, False) -> highlighted -= 1
|
(_, _, False) -> highlighted -= 1
|
||||||
(_, _, True) -> do highlighted -= 1
|
(_, _, True) -> do highlighted -= 1
|
||||||
@ -424,6 +465,7 @@ handleEvent (VtyEvent e) =
|
|||||||
correct = all (uncurry (==) . (\i -> (i+1, fst (kvs M.! i)))) [0..n-1]
|
correct = all (uncurry (==) . (\i -> (i+1, fst (kvs M.! i)))) [0..n-1]
|
||||||
|
|
||||||
_ -> error "impossible"
|
_ -> error "impossible"
|
||||||
|
handleEvent (BT.MouseDown (SBClick el (CardViewport i)) _ _ _) = handleClickScroll (scroll' i) el
|
||||||
handleEvent _ = return ()
|
handleEvent _ = return ()
|
||||||
|
|
||||||
next :: EventM Name GlobalState ()
|
next :: EventM Name GlobalState ()
|
||||||
|
@ -61,14 +61,12 @@ info = unlines
|
|||||||
, "Controls:"
|
, "Controls:"
|
||||||
, " * Use arrows or the j and k keys for menu navigation"
|
, " * Use arrows or the j and k keys for menu navigation"
|
||||||
, ""
|
, ""
|
||||||
, " * Press the s key to toggle shuffling inside the deck selector menu"
|
|
||||||
, ""
|
|
||||||
, " * Enter confirms a selection, flips a card or continues to the next card"
|
, " * Enter confirms a selection, flips a card or continues to the next card"
|
||||||
, ""
|
, ""
|
||||||
, " * Use TAB or the arrow keys for navigating gaps in open questions"
|
, " * Use TAB or the arrow keys for navigating gaps in open questions"
|
||||||
, ""
|
, ""
|
||||||
, " * Use the c key for confirming reorder questions or multiple choice questions with more than 1 possible answer"
|
, " * Use the c key for confirming reorder questions or multiple choice questions with more than 1 possible answer"
|
||||||
, ""
|
, ""
|
||||||
, " * Use F1 to show the answers of a open question"
|
, " * Use Ctrl+F1 to show the answers of a open question"
|
||||||
, ""
|
, ""
|
||||||
, " * Use CTRL+Left and CTRL+Right to move to previous and next cards without having to answer them; this is disabled in review mode"]
|
, " * Use Ctrl+Left and Ctrl+Right to move to previous and next cards without having to answer them; this is disabled in review mode"]
|
||||||
|
Loading…
Reference in New Issue
Block a user