Изменения в других элементах, основанные на выборе списка в Threepenny-GUI
Итак, у меня есть простой пример макета со списком, кнопкой и текстовой областью, где нажатие на кнопку изменяет текст в текстовой области:
import Control.Applicative
import Control.Monad
import Data.Maybe
import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core
main :: IO ()
main = startGUI defaultConfig setup
setup :: Window -> UI ()
setup w = do
return w # set UI.title "Simple example"
listBox <- UI.listBox (pure ["First", "Second"]) (pure Nothing) ((UI.string .) <$> (pure id))
button <- UI.button # set UI.text "Button"
display <- UI.textarea # set UI.text "Initial value"
element listBox # set (attr "size") "10"
getBody w #+ [element listBox, element button, element display]
on UI.click button $ const $ do
element display # set UI.text "new text"
То, что я хотел сделать, это иметь изменение быть зависимым от выбора списка (например, иметь "new text"
быть "First"
или же "Second"
на основании выбора).
Я могу довольно легко получить выбор, комбинируя userSelection
а также facts
как
facts . userSelection :: ListBox a -> Behavior (Maybe a)
а потому что установка значения для текстовой области выполняется с
set text :: String -> UI Element -> UI Element
Я не знаю, как обойти тот факт, что выбор является Behavior
,
Все это кажется мне несколько неубедительным, и мне было интересно, что будет правильным способом сделать это. Может быть, я должен что-то делать уже тогда, когда выбор списка сделан или изменен, а не только при нажатии кнопки.
3 ответа
Прежде всего, произошла регрессия, которая повлияла на код здесь. Эта проблема теперь решена. Threepenny 0.6.0.3 имеет временное исправление, и окончательное исправление будет включено в релиз после этого.
Код в предоставленной вами пастбине почти верный. Единственное необходимое изменение - вам не нужно использовать sink
в обратном вызове нажатия кнопки - в вашем случае, sink
следует установить постоянную связь между поведением и содержимым текстовой области, причем значение поведения изменяется в ответ на события нажатия кнопки.
Ради полноты, вот полное решение:
{-# LANGUAGE RecursiveDo #-}
module Main where
import Control.Applicative
import Control.Monad
import Data.Maybe
import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core
main :: IO ()
main = startGUI defaultConfig setup
setup :: Window -> UI ()
setup w = void $ mdo
return w # set UI.title "Simple example"
listBox <- UI.listBox
(pure ["First", "Second"]) bSelected (pure $ UI.string)
button <- UI.button # set UI.text "Button"
display <- UI.textarea
element listBox # set (attr "size") "10"
getBody w #+ [element listBox, element button, element display]
bSelected <- stepper Nothing $ rumors (UI.userSelection listBox)
let eClick = UI.click button
eValue = fromMaybe "No selection" <$> bSelected <@ eClick
bValue <- stepper "Initial value" eValue
element display # sink UI.text bValue
Две ключевые вещи, которые нужно забрать:
-
Behavior (Maybe a)
аргументlistBox
не устанавливает только начальное выбранное значение, но определяет эволюцию значения в течение всего жизненного цикла приложения. В этом примереfacts $ UI.userSelection listBox
простоbSelected
, что можно проверить с помощью исходного кодаWidgets
модуль. - Типичный способ проверить поведение при возникновении событий - через
(<@)
(или же<@>
если событие содержит данные, которые вы хотите использовать).
Внимание: я не знаком с ThreePenny, я просто читаю документацию.
Я думаю тебе нужно sink
ваш список в вашей текстовой области:
element display # sink UI.text ((maybe "new text" id) <$> (facts $ userSelection listBox))
Попробуйте создать степпер для списка, а затем просто погрузитесь, чтобы отобразить
listB <- stepper Nothing (userSelection listBox)
element display # sink UI.text ((maybe "new text" id) <$> (listB)
Затем, если вы хотите попробовать поведение с помощью кнопки
listB <- stepper Nothing (userSelection listBox)
button <- UI.button # set UI.text "Button"
cutedListB <- stepper Nothing (listB <@ UI.click button)
element display # sink UI.text ((maybe "new text" id) <$> (listB)