Изменения в других элементах, основанные на выборе списка в 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) 
Другие вопросы по тегам