monomer-1.4.0.0: src/Monomer/Widgets/Containers/Confirm.hs
{-|
Module : Monomer.Widgets.Containers.Confirm
Copyright : (c) 2018 Francisco Vallarino
License : BSD-3-Clause (see the LICENSE file)
Maintainer : fjvallarino@gmail.com
Stability : experimental
Portability : non-portable
Simple confirm dialog, displaying accept and close buttons and optional title.
Usually embedded in a zstack component and displayed/hidden depending on
context.
Similar to "Monomer.Widgets.Containers.Alert", but takes two events to handle
the Accept and Cancel actions.
A simple text message can be displayed with 'confirmMsg', providing the message
text and the events to generate when the user interacts with the dialog:
@
confirmMsg "Save changes?" ConfirmAcceptEvent ConfirmCancelEvent
@
Alternatively, a custom widget can be provided to display as content:
@
customConfirm = confirm ConfirmAcceptEvent ConfirmCancelEvent where
content = hstack [
label "Save changes?",
filler,
label fileName
]
@
-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE Strict #-}
module Monomer.Widgets.Containers.Confirm (
-- * Configuration
ConfirmCfg,
InnerConfirmEvt,
-- * Constructors
confirm,
confirm_,
confirmMsg,
confirmMsg_
) where
import Control.Applicative ((<|>))
import Control.Lens ((&), (^.), (.~), (<>~))
import Data.Default
import Data.Maybe
import Data.Text (Text)
import Monomer.Core
import Monomer.Core.Combinators
import Monomer.Widgets.Composite
import Monomer.Widgets.Containers.Box
import Monomer.Widgets.Containers.Keystroke
import Monomer.Widgets.Containers.Stack
import Monomer.Widgets.Singles.Button
import Monomer.Widgets.Singles.Icon
import Monomer.Widgets.Singles.Label
import Monomer.Widgets.Singles.Spacer
import qualified Monomer.Lens as L
{-|
Configuration options for confirm:
- 'titleCaption': the title of the alert dialog.
- 'acceptCaption': the caption of the accept button.
- 'closeCaption': the caption of the close button.
-}
data ConfirmCfg = ConfirmCfg {
_cfcTitle :: Maybe Text,
_cfcAccept :: Maybe Text,
_cfcCancel :: Maybe Text
}
instance Default ConfirmCfg where
def = ConfirmCfg {
_cfcTitle = Nothing,
_cfcAccept = Nothing,
_cfcCancel = Nothing
}
instance Semigroup ConfirmCfg where
(<>) a1 a2 = ConfirmCfg {
_cfcTitle = _cfcTitle a2 <|> _cfcTitle a1,
_cfcAccept = _cfcAccept a2 <|> _cfcAccept a1,
_cfcCancel = _cfcCancel a2 <|> _cfcCancel a1
}
instance Monoid ConfirmCfg where
mempty = def
instance CmbTitleCaption ConfirmCfg where
titleCaption t = def {
_cfcTitle = Just t
}
instance CmbAcceptCaption ConfirmCfg where
acceptCaption t = def {
_cfcAccept = Just t
}
instance CmbCancelCaption ConfirmCfg where
cancelCaption t = def {
_cfcCancel = Just t
}
{-|
Wraps the user event to be able to implement custom events and still be able to
report to its parent.
Previously used, now kept for reference.
-}
newtype InnerConfirmEvt e
= ConfirmParentEvt e
deriving (Eq, Show)
-- | Creates a confirm dialog with the provided content.
confirm
:: (WidgetModel s, WidgetEvent e)
=> e -- ^ The accept button event.
-> e -- ^ The cancel button event.
-> WidgetNode () (InnerConfirmEvt e) -- ^ Content to display in the dialog.
-> WidgetNode s e -- ^ The created dialog.
confirm acceptEvt cancelEvt dialogBody = newNode where
newNode = confirm_ acceptEvt cancelEvt def dialogBody
-- | Creates an alert dialog with the provided content. Accepts config.
confirm_
:: (WidgetModel s, WidgetEvent e)
=> e -- ^ The accept button event.
-> e -- ^ The cancel button event.
-> [ConfirmCfg] -- ^ The config options for the dialog.
-> WidgetNode () (InnerConfirmEvt e) -- ^ Content to display in the dialog.
-> WidgetNode s e -- ^ The created dialog.
confirm_ acceptEvt cancelEvt configs dialogBody = newNode where
config = mconcat configs
createUI = buildUI (const dialogBody) acceptEvt cancelEvt config
compCfg = [compositeMergeReqs mergeReqs]
newNode = compositeD_ "confirm" (WidgetValue ()) createUI handleEvent compCfg
-- | Creates an alert dialog with a text message as content.
confirmMsg
:: (WidgetModel s, WidgetEvent e)
=> Text -- ^ The message to display in the dialog.
-> e -- ^ The accept button event.
-> e -- ^ The cancel button event.
-> WidgetNode s e -- ^ The created dialog.
confirmMsg msg acceptEvt cancelEvt = confirmMsg_ msg acceptEvt cancelEvt def
-- | Creates an alert dialog with a text message as content. Accepts config.
confirmMsg_
:: (WidgetModel s, WidgetEvent e)
=> Text -- ^ The message to display in the dialog.
-> e -- ^ The accept button event.
-> e -- ^ The cancel button event.
-> [ConfirmCfg] -- ^ The config options for the dialog.
-> WidgetNode s e -- ^ The created dialog.
confirmMsg_ message acceptEvt cancelEvt configs = newNode where
config = mconcat configs
dialogBody wenv = label_ message [multiline]
& L.info . L.style .~ collectTheme wenv L.dialogMsgBodyStyle
createUI = buildUI dialogBody acceptEvt cancelEvt config
compCfg = [compositeMergeReqs mergeReqs]
newNode = compositeD_ "confirm" (WidgetValue ()) createUI handleEvent compCfg
mergeReqs :: MergeReqsHandler s e sp
mergeReqs wenv newNode oldNode parentModel oldModel model = reqs where
acceptPath = SetFocus <$> widgetIdFromKey wenv "acceptBtn"
isVisible node = node ^. L.info . L.visible
reqs
| not (isVisible oldNode) && isVisible newNode = catMaybes [acceptPath]
| otherwise = []
buildUI
:: (WidgetModel s, WidgetEvent ep)
=> (WidgetEnv s (InnerConfirmEvt ep) -> WidgetNode s (InnerConfirmEvt ep))
-> ep
-> ep
-> ConfirmCfg
-> WidgetEnv s (InnerConfirmEvt ep)
-> s
-> WidgetNode s (InnerConfirmEvt ep)
buildUI dialogBody pAcceptEvt pCancelEvt config wenv model = mainTree where
acceptEvt = ConfirmParentEvt pAcceptEvt
cancelEvt = ConfirmParentEvt pCancelEvt
title = fromMaybe "" (_cfcTitle config)
accept = fromMaybe "Accept" (_cfcAccept config)
cancel = fromMaybe "Cancel" (_cfcCancel config)
emptyOverlay = collectTheme wenv L.emptyOverlayStyle
acceptBtn = mainButton accept acceptEvt `nodeKey` "acceptBtn"
cancelBtn = button cancel cancelEvt
buttons = hstack [ acceptBtn, spacer, cancelBtn ]
closeIcon = icon_ IconClose [width 2]
& L.info . L.style .~ collectTheme wenv L.dialogCloseIconStyle
confirmTree = vstack_ [sizeReqUpdater clearExtra] [
hstack [
label title & L.info . L.style .~ collectTheme wenv L.dialogTitleStyle,
filler,
box_ [alignTop, onClick cancelEvt] closeIcon
],
dialogBody wenv,
filler,
box_ [alignRight] buttons
& L.info . L.style <>~ collectTheme wenv L.dialogButtonsStyle
] & L.info . L.style .~ collectTheme wenv L.dialogFrameStyle
confirmBox = box_ [onClickEmpty cancelEvt] confirmTree
& L.info . L.style .~ emptyOverlay
mainTree = keystroke [("Esc", cancelEvt)] confirmBox
handleEvent
:: WidgetEnv s (InnerConfirmEvt ep)
-> WidgetNode s (InnerConfirmEvt ep)
-> s
-> InnerConfirmEvt ep
-> [EventResponse s (InnerConfirmEvt ep) sp ep]
handleEvent wenv node model evt = case evt of
ConfirmParentEvt pevt -> [Report pevt]