Format widgets

This commit is contained in:
Francisco Vallarino 2020-07-27 01:42:51 -03:00
parent ad47f7f387
commit 7054a1d2a2
13 changed files with 737 additions and 545 deletions

View File

@ -6,6 +6,7 @@ module Monomer.Event.Keyboard (
isKeyBackspace,
isKeyEsc,
isKeyReturn,
isKeySpace,
isKeyTab,
isKeyLeft,
isKeyRight,
@ -55,27 +56,68 @@ convertKeyModifier keyMod = KeyMod {
keyModAltGr = SDL.keyModifierAltGr keyMod
}
keyBackspace :: KeyCode
keyBackspace = getKeycode SDL.KeycodeBackspace
keyEsc :: KeyCode
keyEsc = getKeycode SDL.KeycodeEscape
keyReturn :: KeyCode
keyReturn = getKeycode SDL.KeycodeReturn
keySpace :: KeyCode
keySpace = getKeycode SDL.KeycodeSpace
keyTab :: KeyCode
keyTab = getKeycode SDL.KeycodeTab
keyLeft :: KeyCode
keyLeft = getKeycode SDL.KeycodeLeft
keyRight :: KeyCode
keyRight = getKeycode SDL.KeycodeRight
keyUp :: KeyCode
keyUp = getKeycode SDL.KeycodeUp
keyDown :: KeyCode
keyDown = getKeycode SDL.KeycodeDown
keyC :: KeyCode
keyC = getKeycode SDL.KeycodeC
keyV :: KeyCode
keyV = getKeycode SDL.KeycodeV
isKeyBackspace :: KeyCode -> Bool
isKeyBackspace = (== keyBackspace)
isKeyEsc :: KeyCode -> Bool
isKeyEsc = (== keyEsc)
isKeyReturn :: KeyCode -> Bool
isKeyReturn = (== keyReturn)
isKeySpace :: KeyCode -> Bool
isKeySpace = (== keySpace)
isKeyTab :: KeyCode -> Bool
isKeyTab = (== keyTab)
isKeyLeft :: KeyCode -> Bool
isKeyLeft = (== keyLeft)
isKeyRight :: KeyCode -> Bool
isKeyRight = (== keyRight)
isKeyUp :: KeyCode -> Bool
isKeyUp = (== keyUp)
isKeyDown :: KeyCode -> Bool
isKeyDown = (== keyDown)
isKeyC :: KeyCode -> Bool
isKeyC = (== keyC)
isKeyV :: KeyCode -> Bool
isKeyV = (== keyV)

View File

@ -7,6 +7,7 @@ import qualified Data.Map.Strict as M
import Monomer.Event.Types
defKeyMod :: KeyMod
defKeyMod = KeyMod {
keyModLeftShift = False,
keyModRightShift = False,
@ -21,6 +22,7 @@ defKeyMod = KeyMod {
keyModAltGr = False
}
defInputStatus :: InputStatus
defInputStatus = InputStatus {
statusMousePos = def,
statusKeyMod = defKeyMod,

View File

@ -37,24 +37,29 @@ button_ :: ButtonConfig s e -> WidgetInstance s e
button_ config = defaultWidgetInstance "button" (makeButton config)
makeButton :: ButtonConfig s e -> Widget s e
makeButton config = createWidget {
makeButton config = widget where
widget = createWidget {
_widgetHandleEvent = handleEvent,
_widgetPreferredSize = preferredSize,
_widgetRender = render
}
where
handleEvent wenv ctx evt widgetInst = case evt of
Click (Point x y) _ -> Just $ resultReqsEvents requests events widgetInst where
requests = _btnOnChangeReq config
events = _btnOnChange config
_ -> Nothing
preferredSize wenv widgetInst = singleNode sizeReq where
Style{..} = _instanceStyle widgetInst
size = getTextBounds wenv _styleText (_btnLabel config)
sizeReq = SizeReq size FlexibleSize FlexibleSize
handleEvent wenv ctx evt widgetInst = case evt of
Click (Point x y) _ -> Just result where
requests = _btnOnChangeReq config
events = _btnOnChange config
result = resultReqsEvents requests events widgetInst
_ -> Nothing
render renderer wenv WidgetInstance{..} =
do
drawStyledBackground renderer _instanceRenderArea _instanceStyle
drawStyledText_ renderer _instanceRenderArea _instanceStyle (_btnLabel config)
preferredSize wenv widgetInst = singleNode sizeReq where
Style{..} = _instanceStyle widgetInst
size = getTextBounds wenv _styleText (_btnLabel config)
sizeReq = SizeReq size FlexibleSize FlexibleSize
render renderer wenv WidgetInstance{..} = do
drawStyledBackground renderer renderArea style
drawStyledText_ renderer renderArea style (_btnLabel config)
where
renderArea = _instanceRenderArea
style = _instanceStyle

View File

@ -31,7 +31,7 @@ containerConfig :: ContainerConfig s e
containerConfig = ContainerConfig [] []
container :: ContainerConfig s e -> WidgetInstance s e -> WidgetInstance s e
container config managedWidget = makeInstance (makeContainer config) managedWidget
container config managed = makeInstance (makeContainer config) managed
makeInstance :: Widget s e -> WidgetInstance s e -> WidgetInstance s e
makeInstance widget managedWidget = (defaultWidgetInstance "container" widget) {
@ -40,34 +40,38 @@ makeInstance widget managedWidget = (defaultWidgetInstance "container" widget) {
}
makeContainer :: ContainerConfig s e -> Widget s e
makeContainer config = createContainer {
makeContainer config = widget where
widget = createContainer {
_widgetHandleEvent = containerHandleEvent handleEvent,
_widgetPreferredSize = containerPreferredSize preferredSize,
_widgetResize = containerResize resize,
_widgetRender = containerRender render
}
where
handleEvent wenv ctx evt widgetInst = case evt of
Click point btn -> result where
events = _ctOnClick config
requests = _ctOnClickReq config
result = if btn == LeftBtn && not (null events && null requests)
then Just $ resultReqsEvents requests events widgetInst
else Nothing
_ -> Nothing
preferredSize wenv widgetInst children reqs = Node sizeReq reqs where
sizeReq = nodeValue $ Seq.index reqs 0
handleEvent wenv ctx evt widgetInst = case evt of
Click point btn -> result where
events = _ctOnClick config
requests = _ctOnClickReq config
needsUpdate = btn == LeftBtn && not (null events && null requests)
result
| needsUpdate = Just $ resultReqsEvents requests events widgetInst
| otherwise = Nothing
_ -> Nothing
resize wenv viewport renderArea widgetInst children reqs = (widgetInst, assignedArea) where
assignedArea = Seq.singleton (viewport, renderArea)
preferredSize wenv widgetInst children reqs = Node sizeReq reqs where
sizeReq = nodeValue $ Seq.index reqs 0
render renderer wenv widgetInst = do
let point = statusMousePos (_weInputStatus wenv)
let viewport = _instanceViewport widgetInst
let Style{..} = _instanceStyle widgetInst
resize wenv viewport renderArea widgetInst children reqs = resized where
assignedArea = Seq.singleton (viewport, renderArea)
resized = (widgetInst, assignedArea)
drawRect renderer viewport _styleColor Nothing
render renderer wenv widgetInst = do
drawRect renderer viewport _styleColor Nothing
when (pointInRect point viewport) $
drawRect renderer viewport _styleHover Nothing
when (pointInRect point viewport) $
drawRect renderer viewport _styleHover Nothing
where
point = statusMousePos (_weInputStatus wenv)
viewport = _instanceViewport widgetInst
Style{..} = _instanceStyle widgetInst

View File

@ -55,7 +55,8 @@ newtype DropdownState = DropdownState {
newtype DropdownMessage = OnChangeMessage Int deriving Typeable
dropdownConfig :: WidgetValue s a -> Seq a -> (a -> Text) -> DropdownConfig s e a
dropdownConfig
:: WidgetValue s a -> Seq a -> (a -> Text) -> DropdownConfig s e a
dropdownConfig value items itemToText = DropdownConfig {
_ddValue = value,
_ddItems = items,
@ -67,7 +68,9 @@ dropdownConfig value items itemToText = DropdownConfig {
_ddHoverColor = lightGray
}
dropdown :: (Traversable t, Eq a) => ALens' s a -> t a -> (a -> Text) -> WidgetInstance s e
dropdown
:: (Traversable t, Eq a)
=> ALens' s a -> t a -> (a -> Text) -> WidgetInstance s e
dropdown field items itemToText = dropdown_ config where
config = dropdownConfig (WidgetLens field) newItems itemToText
newItems = foldl' (|>) Empty items
@ -82,7 +85,8 @@ makeInstance widget = (defaultWidgetInstance "dropdown" widget) {
}
makeDropdown :: (Eq a) => DropdownConfig s e a -> DropdownState -> Widget s e
makeDropdown config state = createContainer {
makeDropdown config state = widget where
widget = createContainer {
_widgetInit = containerInit init,
_widgetGetState = makeState state,
_widgetMerge = containerMergeTrees merge,
@ -92,102 +96,112 @@ makeDropdown config state = createContainer {
_widgetResize = containerResize resize,
_widgetRender = render
}
where
isOpen = _isOpen state
currentValue wenv = widgetValueGet (_weModel wenv) (_ddValue config)
createDropdown wenv newState widgetInst = newInstance where
selected = currentValue wenv
path = _instancePath widgetInst
newInstance = widgetInst {
_instanceWidget = makeDropdown config newState,
_instanceChildren = Seq.singleton $ makeListView config path selected
}
isOpen = _isOpen state
currentValue wenv = widgetValueGet (_weModel wenv) (_ddValue config)
init wenv widgetInst = resultWidget $ createDropdown wenv state widgetInst
createDropdown wenv newState widgetInst = newInstance where
selected = currentValue wenv
path = _instancePath widgetInst
newInstance = widgetInst {
_instanceWidget = makeDropdown config newState,
_instanceChildren = Seq.singleton $ makeListView config path selected
}
merge wenv oldState newInstance = resultWidget $ createDropdown wenv newState newInstance where
newState = fromMaybe state (useState oldState)
init wenv widgetInst = resultWidget $ createDropdown wenv state widgetInst
handleEvent wenv target evt widgetInst = case evt of
Click point _
| openRequired point widgetInst -> Just $ handleOpenDropdown wenv widgetInst
| closeRequired point widgetInst -> Just $ handleCloseDropdown wenv widgetInst
KeyAction mode code status
| isKeyDown code && not isOpen -> Just $ handleOpenDropdown wenv widgetInst
| isKeyEsc code && isOpen -> Just $ handleCloseDropdown wenv widgetInst
_
| not isOpen -> Just $ resultReqs [IgnoreChildrenEvents] widgetInst
| otherwise -> Nothing
merge wenv oldState newInst = result where
newState = fromMaybe state (useState oldState)
result = resultWidget $ createDropdown wenv newState newInst
openRequired point widgetInst = not isOpen && inViewport where
inViewport = pointInRect point (_instanceViewport widgetInst)
handleEvent wenv target evt widgetInst = case evt of
Click point _
| openRequired point widgetInst -> Just $ openDropdown wenv widgetInst
| closeRequired point widgetInst -> Just $ closeDropdown wenv widgetInst
KeyAction mode code status
| isKeyDown code && not isOpen -> Just $ openDropdown wenv widgetInst
| isKeyEsc code && isOpen -> Just $ closeDropdown wenv widgetInst
_
| not isOpen -> Just $ resultReqs [IgnoreChildrenEvents] widgetInst
| otherwise -> Nothing
closeRequired point widgetInst = isOpen && not inOverlay where
inOverlay = case Seq.lookup 0 (_instanceChildren widgetInst) of
Just inst -> pointInRect point (_instanceViewport inst)
Nothing -> False
openRequired point widgetInst = not isOpen && inViewport where
inViewport = pointInRect point (_instanceViewport widgetInst)
handleOpenDropdown wenv widgetInst = resultReqs requests newInstance where
selected = currentValue wenv
selectedIdx = fromMaybe 0 (Seq.elemIndexL selected (_ddItems config))
newState = DropdownState True
newInstance = widgetInst {
_instanceWidget = makeDropdown config newState
}
path = _instancePath widgetInst
lvPath = firstChildPath widgetInst
requests = [SetOverlay path, SetFocus lvPath]
closeRequired point widgetInst = isOpen && not inOverlay where
inOverlay = case Seq.lookup 0 (_instanceChildren widgetInst) of
Just inst -> pointInRect point (_instanceViewport inst)
Nothing -> False
handleCloseDropdown wenv widgetInst = resultReqs requests newInstance where
path = _instancePath widgetInst
newState = DropdownState False
newInstance = widgetInst {
_instanceWidget = makeDropdown config newState
}
requests = [ResetOverlay, SetFocus path]
openDropdown wenv widgetInst = resultReqs requests newInstance where
selected = currentValue wenv
selectedIdx = fromMaybe 0 (Seq.elemIndexL selected (_ddItems config))
newState = DropdownState True
newInstance = widgetInst {
_instanceWidget = makeDropdown config newState
}
path = _instancePath widgetInst
lvPath = firstChildPath widgetInst
requests = [SetOverlay path, SetFocus lvPath]
handleMessage wenv target message widgetInst = cast message
>>= \(OnChangeMessage idx) -> Seq.lookup idx (_ddItems config)
>>= \value -> Just $ handleOnChange wenv idx value widgetInst
closeDropdown wenv widgetInst = resultReqs requests newInstance where
path = _instancePath widgetInst
newState = DropdownState False
newInstance = widgetInst {
_instanceWidget = makeDropdown config newState
}
requests = [ResetOverlay, SetFocus path]
handleOnChange wenv idx item widgetInst = WidgetResult (reqs <> newReqs) (events <> newEvents) newInstance where
WidgetResult reqs events newInstance = handleCloseDropdown wenv widgetInst
newReqs = Seq.fromList $ widgetValueSet (_ddValue config) item
newEvents = Seq.fromList $ fmap ($ item) (_ddOnChange config)
handleMessage wenv target message widgetInst = cast message
>>= \(OnChangeMessage idx) -> Seq.lookup idx (_ddItems config)
>>= \value -> Just $ onChange wenv idx value widgetInst
preferredSize wenv widgetInst children reqs = Node sizeReq reqs where
Style{..} = _instanceStyle widgetInst
size = getTextBounds wenv _styleText (dropdownLabel wenv)
sizeReq = SizeReq size FlexibleSize StrictSize
onChange wenv idx item widgetInst = result where
WidgetResult reqs events newInstance = closeDropdown wenv widgetInst
newReqs = Seq.fromList $ widgetValueSet (_ddValue config) item
newEvents = Seq.fromList $ fmap ($ item) (_ddOnChange config)
result = WidgetResult (reqs <> newReqs) (events <> newEvents) newInstance
resize wenv viewport renderArea widgetInst children reqs = (widgetInst, assignedArea) where
childrenReqs = Seq.zip children reqs
area = case Seq.lookup 0 childrenReqs of
Just (child, reqChild) -> (oViewport, oRenderArea) where
reqHeight = _h . _sizeRequested . nodeValue $ reqChild
maxHeight = min reqHeight 150
oViewport = viewport { _ry = _ry viewport + _rh viewport, _rh = maxHeight }
oRenderArea = renderArea { _ry = _ry renderArea + _rh viewport }
Nothing -> (viewport, renderArea)
assignedArea = Seq.singleton area
preferredSize wenv widgetInst children reqs = Node sizeReq reqs where
Style{..} = _instanceStyle widgetInst
size = getTextBounds wenv _styleText (dropdownLabel wenv)
sizeReq = SizeReq size FlexibleSize StrictSize
render renderer wenv WidgetInstance{..} =
do
drawStyledBackground renderer _instanceRenderArea _instanceStyle
drawStyledText_ renderer _instanceRenderArea _instanceStyle (dropdownLabel wenv)
resize wenv viewport renderArea widgetInst children reqs = resized where
childrenReqs = Seq.zip children reqs
area = case Seq.lookup 0 childrenReqs of
Just (child, reqChild) -> (oViewport, oRenderArea) where
reqHeight = _h . _sizeRequested . nodeValue $ reqChild
maxHeight = min reqHeight 150
oViewport = viewport {
_ry = _ry viewport + _rh viewport,
_rh = maxHeight
}
oRenderArea = renderArea { _ry = _ry renderArea + _rh viewport }
Nothing -> (viewport, renderArea)
assignedArea = Seq.singleton area
resized = (widgetInst, assignedArea)
when (isOpen && isJust listViewOverlay) $
createOverlay renderer $ renderOverlay renderer wenv (fromJust listViewOverlay)
where
listViewOverlay = Seq.lookup 0 _instanceChildren
render renderer wenv WidgetInstance{..} = do
drawStyledBackground renderer renderArea style
drawStyledText_ renderer renderArea style (dropdownLabel wenv)
renderOverlay renderer wenv overlayInstance = renderAction where
renderAction = _widgetRender (_instanceWidget overlayInstance) renderer wenv overlayInstance
when (isOpen && isJust listViewOverlay) $
createOverlay renderer $
renderOverlay renderer wenv (fromJust listViewOverlay)
where
listViewOverlay = Seq.lookup 0 _instanceChildren
renderArea = _instanceRenderArea
style = _instanceStyle
dropdownLabel wenv = _ddItemToText config $ currentValue wenv
renderOverlay renderer wenv overlayInstance = renderAction where
widget = _instanceWidget overlayInstance
renderAction = _widgetRender widget renderer wenv overlayInstance
makeListView :: (Eq a) => DropdownConfig s e a -> Path -> a -> WidgetInstance s e
dropdownLabel wenv = _ddItemToText config $ currentValue wenv
makeListView
:: (Eq a) => DropdownConfig s e a -> Path -> a -> WidgetInstance s e
makeListView DropdownConfig{..} dropdownPath selected = listView_ lvConfig where
lvConfig = ListViewConfig {
_lvValue = WidgetValue selected,

View File

@ -27,32 +27,46 @@ vgrid children = (defaultWidgetInstance "vgrid" (makeFixedGrid False)) {
}
makeFixedGrid :: Bool -> Widget s e
makeFixedGrid isHorizontal = createContainer {
makeFixedGrid isHorizontal = widget where
widget = createContainer {
_widgetPreferredSize = containerPreferredSize preferredSize,
_widgetResize = containerResize resize
}
where
preferredSize wenv widgetInst children reqs = Node reqSize reqs where
(vchildren, vreqs) = visibleChildrenReq children reqs
reqSize = SizeReq (Size width height) FlexibleSize FlexibleSize
width = if Seq.null vchildren then 0 else fromIntegral wMul * (maximum . fmap (_w . _sizeRequested)) vreqs
height = if Seq.null vchildren then 0 else fromIntegral hMul * (maximum . fmap (_h . _sizeRequested)) vreqs
wMul = if isHorizontal then length vchildren else 1
hMul = if isHorizontal then 1 else length vchildren
resize wenv viewport renderArea widgetInst children reqs = (widgetInst, assignedAreas) where
vchildren = Seq.filter _instanceVisible children
Rect l t w h = renderArea
cols = if isHorizontal then length vchildren else 1
rows = if isHorizontal then 1 else length vchildren
foldHelper (newAreas, index) child = (newAreas |> newArea, newIndex) where
visible = _instanceVisible child
newIndex = index + if _instanceVisible child then 1 else 0
newViewport = if visible then calcViewport index else def
newArea = (newViewport, newViewport)
assignedAreas = fst $ foldl' foldHelper (Seq.empty, 0) vchildren
calcViewport i = Rect (cx i) (cy i) cw ch
cw = if cols > 0 then w / fromIntegral cols else 0
ch = if rows > 0 then h / fromIntegral rows else 0
cx i = if rows > 0 then l + fromIntegral (i `div` rows) * cw else 0
cy i = if cols > 0 then t + fromIntegral (i `div` cols) * ch else 0
preferredSize wenv widgetInst children reqs = Node reqSize reqs where
(vchildren, vreqs) = visibleChildrenReq children reqs
reqSize = SizeReq (Size width height) FlexibleSize FlexibleSize
width
| Seq.null vchildren = 0
| otherwise = wMul * (maximum . fmap (_w . _sizeRequested)) vreqs
height
| Seq.null vchildren = 0
| otherwise = hMul * (maximum . fmap (_h . _sizeRequested)) vreqs
wMul
| isHorizontal = fromIntegral (length vchildren)
| otherwise = 1
hMul
| isHorizontal = 1
| otherwise = fromIntegral (length vchildren)
resize wenv viewport renderArea widgetInst children reqs = resized where
Rect l t w h = renderArea
vchildren = Seq.filter _instanceVisible children
cols = if isHorizontal then length vchildren else 1
rows = if isHorizontal then 1 else length vchildren
cw = if cols > 0 then w / fromIntegral cols else 0
ch = if rows > 0 then h / fromIntegral rows else 0
cx i
| rows > 0 = l + fromIntegral (i `div` rows) * cw
| otherwise = 0
cy i
| cols > 0 = t + fromIntegral (i `div` cols) * ch
| otherwise = 0
foldHelper (newAreas, index) child = (newAreas |> newArea, newIndex) where
visible = _instanceVisible child
newIndex = index + if _instanceVisible child then 1 else 0
newViewport = if visible then calcViewport index else def
newArea = (newViewport, newViewport)
calcViewport i = Rect (cx i) (cy i) cw ch
assignedAreas = fst $ foldl' foldHelper (Seq.empty, 0) vchildren
resized = (widgetInst, assignedAreas)

View File

@ -17,17 +17,17 @@ label :: Text -> WidgetInstance s e
label caption = defaultWidgetInstance "label" (makeLabel caption)
makeLabel :: Text -> Widget s e
makeLabel caption = createWidget {
makeLabel caption = widget where
widget = createWidget {
_widgetPreferredSize = preferredSize,
_widgetRender = render
}
where
preferredSize wenv widgetInst = singleNode sizeReq where
Style{..} = _instanceStyle widgetInst
size = getTextBounds wenv _styleText caption
sizeReq = SizeReq size FlexibleSize StrictSize
render renderer wenv WidgetInstance{..} =
do
drawStyledBackground renderer _instanceRenderArea _instanceStyle
drawStyledText_ renderer _instanceRenderArea _instanceStyle caption
preferredSize wenv widgetInst = singleNode sizeReq where
Style{..} = _instanceStyle widgetInst
size = getTextBounds wenv _styleText caption
sizeReq = SizeReq size FlexibleSize StrictSize
render renderer wenv WidgetInstance{..} = do
drawStyledBackground renderer _instanceRenderArea _instanceStyle
drawStyledText_ renderer _instanceRenderArea _instanceStyle caption

View File

@ -60,7 +60,8 @@ newtype ListViewState = ListViewState {
newtype ListViewMessage = OnClickMessage Int deriving Typeable
listViewConfig :: WidgetValue s a -> Seq a -> (a -> Text) -> ListViewConfig s e a
listViewConfig
:: WidgetValue s a -> Seq a -> (a -> Text) -> ListViewConfig s e a
listViewConfig value items itemToText = ListViewConfig {
_lvValue = value,
_lvItems = items,
@ -72,7 +73,9 @@ listViewConfig value items itemToText = ListViewConfig {
_lvHoverColor = lightGray
}
listView :: (Traversable t, Eq a) => ALens' s a -> t a -> (a -> Text) -> WidgetInstance s e
listView
:: (Traversable t, Eq a)
=> ALens' s a -> t a -> (a -> Text) -> WidgetInstance s e
listView field items itemToText = listView_ config where
config = listViewConfig (WidgetLens field) newItems itemToText
newItems = foldl' (|>) Empty items
@ -87,7 +90,8 @@ makeInstance widget = (defaultWidgetInstance "listView" widget) {
}
makeListView :: (Eq a) => ListViewConfig s e a -> ListViewState -> Widget s e
makeListView config state = createContainer {
makeListView config state = widget where
widget = createContainer {
_widgetInit = containerInit init,
_widgetGetState = makeState state,
_widgetMerge = containerMergeTrees merge,
@ -96,89 +100,106 @@ makeListView config state = createContainer {
_widgetPreferredSize = containerPreferredSize preferredSize,
_widgetResize = containerResize resize
}
where
currentValue wenv = widgetValueGet (_weModel wenv) (_lvValue config)
createListView wenv newState widgetInst = newInstance where
selected = currentValue wenv
path = _instancePath widgetInst
itemsList = makeItemsList config path selected (_highlighted newState)
newInstance = widgetInst {
_instanceWidget = makeListView config newState,
_instanceChildren = Seq.singleton (scroll itemsList)
}
currentValue wenv = widgetValueGet (_weModel wenv) (_lvValue config)
init wenv widgetInst = resultWidget $ createListView wenv state widgetInst
createListView wenv newState widgetInst = newInstance where
selected = currentValue wenv
path = _instancePath widgetInst
itemsList = makeItemsList config path selected (_highlighted newState)
newInstance = widgetInst {
_instanceWidget = makeListView config newState,
_instanceChildren = Seq.singleton (scroll itemsList)
}
merge wenv oldState newInstance = resultWidget $ createListView wenv newState newInstance where
newState = fromMaybe state (useState oldState)
init wenv widgetInst = resultWidget $ createListView wenv state widgetInst
handleEvent wenv target evt widgetInst = case evt of
KeyAction mode code status
| isKeyDown code && status == KeyPressed -> handleHighlightNext wenv widgetInst
| isKeyUp code && status == KeyPressed -> handleHighlightPrev wenv widgetInst
| isKeyReturn code && status == KeyPressed -> Just $ selectItem wenv widgetInst (_highlighted state)
_ -> Nothing
merge wenv oldState newInstance = result where
newState = fromMaybe state (useState oldState)
result = resultWidget $ createListView wenv newState newInstance
handleHighlightNext wenv widgetInst = highlightItem wenv widgetInst nextIdx where
tempIdx = _highlighted state
nextIdx = if tempIdx < length (_lvItems config) - 1 then tempIdx + 1 else tempIdx
handleEvent wenv target evt widgetInst = case evt of
KeyAction mode code status
| isKeyDown code && status == KeyPressed -> highlightNext wenv widgetInst
| isKeyUp code && status == KeyPressed -> highlightPrev wenv widgetInst
| isSelectKey code && status == KeyPressed -> resultSelected
where
resultSelected = Just $ selectItem wenv widgetInst (_highlighted state)
isSelectKey code = isKeyReturn code || isKeySpace code
_ -> Nothing
handleHighlightPrev wenv widgetInst = highlightItem wenv widgetInst nextIdx where
tempIdx = _highlighted state
nextIdx = if tempIdx > 0 then tempIdx - 1 else tempIdx
highlightNext wenv widgetInst = highlightItem wenv widgetInst nextIdx where
tempIdx = _highlighted state
nextIdx
| tempIdx < length (_lvItems config) - 1 = tempIdx + 1
| otherwise = tempIdx
handleMessage wenv target message widgetInst = fmap handleSelect (cast message) where
handleSelect (OnClickMessage idx) = selectItem wenv widgetInst idx
highlightPrev wenv widgetInst = highlightItem wenv widgetInst nextIdx where
tempIdx = _highlighted state
nextIdx
| tempIdx > 0 = tempIdx - 1
| otherwise = tempIdx
highlightItem wenv widgetInst nextIdx = Just $ widgetResult { _resultRequests = requests } where
newState = ListViewState nextIdx
newWidget = makeListView config newState
-- ListView's merge uses the old widget's state. Since we want the newly created state, the old widget is replaced here
oldInstance = widgetInst {
_instanceWidget = newWidget
}
-- ListView's tree will be rebuilt in merge, before merging its children, so it does not matter what we currently have
newInstance = oldInstance
widgetResult = _widgetMerge newWidget wenv oldInstance newInstance
scrollToReq = itemScrollTo widgetInst nextIdx
requests = Seq.fromList scrollToReq
handleMessage wenv target message widgetInst = result where
handleSelect (OnClickMessage idx) = selectItem wenv widgetInst idx
result = fmap handleSelect (cast message)
selectItem wenv widgetInst idx = resultReqs requests newInstance where
selected = currentValue wenv
value = fromMaybe selected (Seq.lookup idx (_lvItems config))
valueSetReq = widgetValueSet (_lvValue config) value
scrollToReq = itemScrollTo widgetInst idx
changeReqs = fmap ($ idx) (_lvOnChangeReq config)
focusReq = [SetFocus $ _instancePath widgetInst]
requests = valueSetReq ++ scrollToReq ++ changeReqs ++ focusReq
newState = ListViewState idx
newInstance = widgetInst {
_instanceWidget = makeListView config newState
}
highlightItem wenv widgetInst nextIdx = result where
newState = ListViewState nextIdx
newWidget = makeListView config newState
-- ListView's merge uses the old widget's state. Since we want the newly
-- created state, the old widget is replaced here
oldInstance = widgetInst {
_instanceWidget = newWidget
}
-- ListView's tree will be rebuilt in merge, before merging its children,
-- so it does not matter what we currently have
newInstance = oldInstance
widgetResult = _widgetMerge newWidget wenv oldInstance newInstance
scrollToReq = itemScrollTo widgetInst nextIdx
requests = Seq.fromList scrollToReq
result = Just $ widgetResult { _resultRequests = requests }
itemScrollTo widgetInst idx = maybeToList (fmap makeScrollReq renderArea) where
lookup idx inst = Seq.lookup idx (_instanceChildren inst)
renderArea = fmap _instanceRenderArea $ pure widgetInst
>>= lookup 0 -- scroll
>>= lookup 0 -- vstack
>>= lookup idx -- item
scrollPath = firstChildPath widgetInst
makeScrollReq rect = SendMessage scrollPath (ScrollTo rect)
selectItem wenv widgetInst idx = resultReqs requests newInstance where
selected = currentValue wenv
value = fromMaybe selected (Seq.lookup idx (_lvItems config))
valueSetReq = widgetValueSet (_lvValue config) value
scrollToReq = itemScrollTo widgetInst idx
changeReqs = fmap ($ idx) (_lvOnChangeReq config)
focusReq = [SetFocus $ _instancePath widgetInst]
requests = valueSetReq ++ scrollToReq ++ changeReqs ++ focusReq
newState = ListViewState idx
newInstance = widgetInst {
_instanceWidget = makeListView config newState
}
preferredSize wenv widgetInst children reqs = Node sizeReq reqs where
sizeReq = nodeValue $ Seq.index reqs 0
itemScrollTo widgetInst idx = maybeToList (fmap scrollReq renderArea) where
lookup idx inst = Seq.lookup idx (_instanceChildren inst)
renderArea = fmap _instanceRenderArea $ pure widgetInst
>>= lookup 0 -- scroll
>>= lookup 0 -- vstack
>>= lookup idx -- item
scrollPath = firstChildPath widgetInst
scrollReq rect = SendMessage scrollPath (ScrollTo rect)
resize wenv viewport renderArea widgetInst children reqs = (widgetInst, assignedArea) where
assignedArea = Seq.singleton (viewport, renderArea)
preferredSize wenv widgetInst children reqs = Node sizeReq reqs where
sizeReq = nodeValue $ Seq.index reqs 0
makeItemsList :: (Eq a) => ListViewConfig s e a -> Path -> a -> Int -> WidgetInstance s e
makeItemsList lvConfig lvPath selected highlightedIdx = makeItemsList where
resize wenv viewport renderArea widgetInst children reqs = resized where
assignedArea = Seq.singleton (viewport, renderArea)
resized = (widgetInst, assignedArea)
makeItemsList
:: (Eq a) => ListViewConfig s e a -> Path -> a -> Int -> WidgetInstance s e
makeItemsList lvConfig lvPath selected highlightedIdx = itemsList where
ListViewConfig{..} = lvConfig
isSelected item = item == selected
selectedColor item = if isSelected item then Just _lvSelectedColor else Nothing
highlightedColor idx = if idx == highlightedIdx then Just _lvHighlightedColor else Nothing
pairs = Seq.zip (Seq.fromList [0..length _lvItems]) _lvItems
selectedColor item
| isSelected item = Just _lvSelectedColor
| otherwise = Nothing
highlightedColor idx
| idx == highlightedIdx = Just _lvHighlightedColor
| otherwise = Nothing
itemStyle idx item = def {
_styleColor = selectedColor item <|> highlightedColor idx,
_styleHover = Just _lvHoverColor
@ -189,4 +210,5 @@ makeItemsList lvConfig lvPath selected highlightedIdx = makeItemsList where
makeItem idx item = container config content `style` itemStyle idx item where
config = itemConfig idx
content = label (_lvItemToText item)
makeItemsList = vstack $ fmap (uncurry makeItem) pairs
pairs = Seq.zip (Seq.fromList [0..length _lvItems]) _lvItems
itemsList = vstack $ fmap (uncurry makeItem) pairs

View File

@ -18,7 +18,11 @@ import Monomer.Widget.BaseWidget
import Monomer.Widget.Types
import Monomer.Widget.Util
data SandboxData = SandboxData | SandboxData2 deriving (Eq, Show, Typeable)
data SandboxData
= SandboxData
| SandboxData2
deriving (Eq, Show, Typeable)
newtype SandboxState = SandboxState {
_clickCount :: Int
} deriving (Eq, Show, Typeable)
@ -30,7 +34,8 @@ makeInstance :: Widget s e -> WidgetInstance s e
makeInstance widget = defaultWidgetInstance "sandbox" widget
makeSandbox :: e -> SandboxState -> Widget s e
makeSandbox onClick state = createWidget {
makeSandbox onClick state = widget where
widget = createWidget {
_widgetGetState = makeState state,
_widgetMerge = widgetMerge merge,
_widgetHandleEvent = handleEvent,
@ -38,38 +43,40 @@ makeSandbox onClick state = createWidget {
_widgetPreferredSize = preferredSize,
_widgetRender = render
}
where
label = "Sandbox: " ++ show (_clickCount state)
merge wenv oldState widgetInst = resultWidget newInstance where
newState = fromMaybe state (useState oldState)
newInstance = widgetInst {
_instanceWidget = makeSandbox onClick newState
}
label = "Sandbox: " ++ show (_clickCount state)
handleEvent wenv target evt widgetInst = case evt of
Click (Point x y) _ -> Just $ resultReqsEvents requests events newInstance where
events = [onClick]
requests = [RunTask (_instancePath widgetInst) runTask]
newState = SandboxState (_clickCount state + 1)
newInstance = makeInstance $ makeSandbox onClick newState
Enter p -> Nothing --trace ("Enter: " ++ show p) Nothing
Move p -> Nothing --trace ("Move: " ++ show p) Nothing
Leave _ p -> Nothing --trace ("Leave: " ++ show p) Nothing
_ -> Nothing
merge wenv oldState widgetInst = resultWidget newInstance where
newState = fromMaybe state (useState oldState)
newInstance = widgetInst {
_instanceWidget = makeSandbox onClick newState
}
runTask = return SandboxData2
handleEvent wenv target evt widgetInst = case evt of
Click (Point x y) _ -> result where
events = [onClick]
requests = [RunTask (_instancePath widgetInst) runTask]
newState = SandboxState (_clickCount state + 1)
newInstance = makeInstance $ makeSandbox onClick newState
result = Just $ resultReqsEvents requests events newInstance
Enter p -> Nothing --trace ("Enter: " ++ show p) Nothing
Move p -> Nothing --trace ("Move: " ++ show p) Nothing
Leave _ p -> Nothing --trace ("Leave: " ++ show p) Nothing
_ -> Nothing
handleMessage wenv target bd widgetInst = case cast bd of
Just val -> if val == SandboxData2 then trace "Sandbox handleMessage called" Nothing else Nothing
Nothing -> Nothing
runTask = return SandboxData2
preferredSize wenv widgetInst = singleNode sizeReq where
Style{..} = _instanceStyle widgetInst
size = getTextBounds wenv _styleText (T.pack label)
sizeReq = SizeReq size FlexibleSize FlexibleSize
handleMessage wenv target bd widgetInst = case cast bd of
Just val
| val == SandboxData2 -> trace "Sandbox handleMessage called" Nothing
| otherwise -> Nothing
Nothing -> Nothing
render renderer wenv WidgetInstance{..} =
do
drawStyledBackground renderer _instanceRenderArea _instanceStyle
drawStyledText_ renderer _instanceRenderArea _instanceStyle (T.pack label)
preferredSize wenv widgetInst = singleNode sizeReq where
Style{..} = _instanceStyle widgetInst
size = getTextBounds wenv _styleText (T.pack label)
sizeReq = SizeReq size FlexibleSize FlexibleSize
render renderer wenv WidgetInstance{..} = do
drawStyledBackground renderer _instanceRenderArea _instanceStyle
drawStyledText_ renderer _instanceRenderArea _instanceStyle (T.pack label)

View File

@ -25,7 +25,10 @@ import Monomer.Widget.BaseContainer
import Monomer.Widget.Types
import Monomer.Widget.Util
data ActiveBar = HBar | VBar deriving (Eq)
data ActiveBar
= HBar
| VBar
deriving (Eq)
data ScrollConfig = ScrollConfig {
_scActiveBarColor :: Maybe Color,
@ -44,7 +47,9 @@ data ScrollState = ScrollState {
_sstReqSize :: Tree SizeReq
} deriving (Typeable)
newtype ScrollMessage = ScrollTo Rect deriving Typeable
newtype ScrollMessage
= ScrollTo Rect
deriving Typeable
data ScrollContext = ScrollContext {
hScrollRatio :: Double,
@ -61,6 +66,7 @@ data ScrollContext = ScrollContext {
vThumbRect :: Rect
}
scrollConfig :: ScrollConfig
scrollConfig = ScrollConfig {
_scActiveBarColor = Just $ darkGray { _alpha = 0.4 },
_scIdleBarColor = Nothing,
@ -70,6 +76,7 @@ scrollConfig = ScrollConfig {
_scWheelRate = 10
}
defaultState :: ScrollState
defaultState = ScrollState {
_sstDragging = Nothing,
_sstDeltaX = 0,
@ -82,7 +89,7 @@ scroll :: WidgetInstance s e -> WidgetInstance s e
scroll managedWidget = scroll_ scrollConfig managedWidget
scroll_ :: ScrollConfig -> WidgetInstance s e -> WidgetInstance s e
scroll_ config managedWidget = makeInstance (makeScroll config defaultState) managedWidget
scroll_ config managed = makeInstance (makeScroll config defaultState) managed
makeInstance :: Widget s e -> WidgetInstance s e -> WidgetInstance s e
makeInstance widget managedWidget = (defaultWidgetInstance "scroll" widget) {
@ -91,7 +98,8 @@ makeInstance widget managedWidget = (defaultWidgetInstance "scroll" widget) {
}
makeScroll :: ScrollConfig -> ScrollState -> Widget s e
makeScroll config state@(ScrollState dragging dx dy cs prevReqs) = createContainer {
makeScroll config state = widget where
widget = createContainer {
_widgetGetState = makeState state,
_widgetMerge = containerMergeTrees merge,
_widgetHandleEvent = containerHandleEvent handleEvent,
@ -100,162 +108,205 @@ makeScroll config state@(ScrollState dragging dx dy cs prevReqs) = createContain
_widgetResize = scrollResize Nothing,
_widgetRender = render
}
where
Size childWidth childHeight = cs
merge wenv oldState widgetInst = resultWidget newInstance where
newState = fromMaybe state (useState oldState)
ScrollState dragging dx dy cs prevReqs = state
Size childWidth childHeight = cs
merge wenv oldState widgetInst = resultWidget newInstance where
newState = fromMaybe state (useState oldState)
newInstance = widgetInst {
_instanceWidget = makeScroll config newState
}
handleEvent wenv target evt widgetInst = case evt of
ButtonAction point btn status -> result where
leftPressed = status == PressedBtn && btn == LeftBtn
btnReleased = status == ReleasedBtn
isDragging = isJust $ _sstDragging state
startDrag = leftPressed && not isDragging
jumpScroll = btnReleased && not isDragging
newState
| startDrag && hMouseInThumb = state { _sstDragging = Just HBar }
| startDrag && vMouseInThumb = state { _sstDragging = Just VBar }
| jumpScroll && hMouseInScroll =
updateScrollThumb state HBar point viewport sctx
| jumpScroll && vMouseInScroll =
updateScrollThumb state VBar point viewport sctx
| btnReleased = state { _sstDragging = Nothing }
| otherwise = state
newInstance = widgetInst {
_instanceWidget = makeScroll config newState
}
handleEvent wenv target evt widgetInst = case evt of
ButtonAction point btn status -> result where
isLeftPressed = status == PressedBtn && btn == LeftBtn
isButtonReleased = status == ReleasedBtn
isDragging = isJust $ _sstDragging state
newState = if | isLeftPressed && hMouseInThumb && not isDragging -> state { _sstDragging = Just HBar }
| isLeftPressed && vMouseInThumb && not isDragging -> state { _sstDragging = Just VBar }
| isButtonReleased && hMouseInScroll && not isDragging -> updateScrollThumb state HBar point viewport sctx
| isButtonReleased && vMouseInScroll && not isDragging -> updateScrollThumb state VBar point viewport sctx
| isButtonReleased -> state { _sstDragging = Nothing }
| otherwise -> state
newInstance = widgetInst {
_instanceWidget = makeScroll config newState
}
handledResult = Just $ resultReqs [IgnoreChildrenEvents] newInstance
result = if | isLeftPressed && (hMouseInThumb || vMouseInThumb) -> handledResult
| isButtonReleased && (hMouseInScroll || vMouseInScroll) -> handledResult
| isButtonReleased && isDragging -> handledResult
| otherwise -> Nothing
Click point btn -> result where
isDragging = isJust $ _sstDragging state
handledResult = Just $ resultReqs [IgnoreChildrenEvents] widgetInst
result = if | hMouseInScroll || vMouseInScroll || isDragging -> handledResult
| otherwise -> Nothing
Move point -> result where
updatedState = fmap (\dg -> updateScrollThumb state dg point viewport sctx) dragging
makeResult newState = resultReqs [IgnoreChildrenEvents] (rebuildWidget wenv newState widgetInst prevReqs)
result = fmap makeResult updatedState
WheelScroll _ (Point wx wy) wheelDirection -> result where
needsUpdate = (wx /= 0 && childWidth > vw) || (wy /= 0 && childHeight > vh)
result = if | needsUpdate -> Just $ resultReqs [IgnoreChildrenEvents] (rebuildWidget wenv newState widgetInst prevReqs)
| otherwise -> Nothing
wheelRate = _scWheelRate config
stepX = wx * if wheelDirection == WheelNormal then -wheelRate else wheelRate
stepY = wy * if wheelDirection == WheelNormal then wheelRate else -wheelRate
newState = state {
_sstDeltaX = scrollAxis stepX dx childWidth vw,
_sstDeltaY = scrollAxis stepY dy childHeight vh
}
_ -> Nothing
where
viewport = _instanceViewport widgetInst
Rect vx vy vw vh = _instanceViewport widgetInst
sctx@ScrollContext{..} = scrollStatus config wenv state viewport
scrollAxis reqDelta currScroll childPos viewportLimit
| reqDelta >= 0 = if currScroll + reqDelta < 0
then currScroll + reqDelta
else 0
| otherwise = if childPos - viewportLimit + currScroll + reqDelta > 0
then currScroll + reqDelta
else viewportLimit - childPos
handleMessage wenv ctx message widgetInst = cast message >>= handleScrollMessage where
handleScrollMessage (ScrollTo rect) = scrollTo wenv widgetInst rect
scrollTo wenv widgetInst rect
| rectInRect rect viewport = Nothing
| otherwise = Just $ resultWidget newInstance
where
viewport = _instanceViewport widgetInst
Rect rx ry rw rh = rect
Rect vx vy vw vh = viewport
diffL = vx - rx
diffR = vx + vw - (rx + rw)
diffT = vy - ry
diffB = vy + vh - (ry + rh)
stepX = if | rectInRectH rect viewport -> dx
| abs diffL <= abs diffR -> diffL + dx
| otherwise -> diffR + dx
stepY = if | rectInRectV rect viewport -> dy
| abs diffT <= abs diffB -> diffT + dy
| otherwise -> diffB + dy
newState = state {
_sstDeltaX = scrollAxis stepX 0 childWidth vw,
_sstDeltaY = scrollAxis stepY 0 childHeight vh
}
newInstance = rebuildWidget wenv newState widgetInst prevReqs
updateScrollThumb state activeBar point viewport sctx = newState where
Point px py = point
ScrollContext{..} = sctx
Rect rx ry rw rh = viewport
hMid = _rw hThumbRect / 2
vMid = _rh vThumbRect / 2
hDelta = (rx - px + hMid) / hScrollRatio
vDelta = (ry - py + vMid) / vScrollRatio
newDeltaX = if activeBar == HBar then scrollAxis hDelta 0 childWidth rw else dx
newDeltaY = if activeBar == VBar then scrollAxis vDelta 0 childHeight rh else dy
newState = state { _sstDeltaX = newDeltaX, _sstDeltaY = newDeltaY }
rebuildWidget wenv newState widgetInst reqs = newInstance where
newWidget = makeScroll config newState
tempInstance = widgetInst { _instanceWidget = newWidget }
newInstance = scrollResize (Just newWidget) wenv (_instanceViewport tempInstance) (_instanceRenderArea tempInstance) tempInstance reqs
preferredSize wenv widgetInst children reqs = Node sizeReq reqs where
sizeReq = SizeReq (_sizeRequested . nodeValue $ Seq.index reqs 0) FlexibleSize FlexibleSize
scrollResize updatedWidget wenv viewport renderArea widgetInst reqs = newInstance where
Rect l t w h = renderArea
child = Seq.index (_instanceChildren widgetInst) 0
childReq = fromMaybe (singleNode def) (Seq.lookup 0 (nodeChildren reqs))
Size childWidth2 childHeight2 = _sizeRequested $ nodeValue childReq
areaW = max w childWidth2
areaH = max h childHeight2
childRenderArea = Rect (l + dx) (t + dy) areaW areaH
newWidget = fromMaybe (makeScroll config $ state { _sstChildSize = Size areaW areaH, _sstReqSize = reqs }) updatedWidget
newChildWidget = _widgetResize (_instanceWidget child) wenv viewport childRenderArea child childReq
newInstance = widgetInst {
_instanceViewport = viewport,
_instanceRenderArea = renderArea,
_instanceWidget = newWidget,
_instanceChildren = Seq.singleton newChildWidget
handledResult = Just $ resultReqs [IgnoreChildrenEvents] newInstance
result
| leftPressed && (hMouseInThumb || vMouseInThumb) = handledResult
| btnReleased && (hMouseInScroll || vMouseInScroll) = handledResult
| btnReleased && isDragging = handledResult
| otherwise = Nothing
Click point btn -> result where
isDragging = isJust $ _sstDragging state
handledResult = Just $ resultReqs [IgnoreChildrenEvents] widgetInst
result
| hMouseInScroll || vMouseInScroll || isDragging = handledResult
| otherwise = Nothing
Move point -> result where
drag bar = updateScrollThumb state bar point viewport sctx
makeWidget state = rebuildWidget wenv state widgetInst prevReqs
makeResult state = resultReqs [IgnoreChildrenEvents] (makeWidget state)
result = fmap (makeResult . drag) dragging
WheelScroll _ (Point wx wy) wheelDirection -> result where
changedX = wx /= 0 && childWidth > vw
changedY = wy /= 0 && childHeight > vh
needsUpdate = changedX || changedY
makeWidget state = rebuildWidget wenv state widgetInst prevReqs
makeResult state = resultReqs [IgnoreChildrenEvents] (makeWidget state)
wheelRate = _scWheelRate config
result
| needsUpdate = Just $ makeResult newState
| otherwise = Nothing
stepX
| wheelDirection == WheelNormal = -wheelRate * wx
| otherwise = wheelRate * wx
stepY
| wheelDirection == WheelNormal = wheelRate * wy
| otherwise = -wheelRate * wy
newState = state {
_sstDeltaX = scrollAxis stepX dx childWidth vw,
_sstDeltaY = scrollAxis stepY dy childHeight vh
}
_ -> Nothing
where
viewport = _instanceViewport widgetInst
Rect vx vy vw vh = _instanceViewport widgetInst
sctx@ScrollContext{..} = scrollStatus config wenv state viewport
render renderer wenv widgetInst =
do
setScissor renderer viewport
containerRender defaultContainerRender renderer wenv widgetInst
resetScissor renderer
scrollAxis reqDelta currScroll childPos vpLimit
| reqDelta >= 0 && currScroll + reqDelta < 0 = currScroll + reqDelta
| reqDelta >= 0 = 0
| childPos - vpLimit + currScroll + reqDelta > 0 = currScroll + reqDelta
| otherwise = vpLimit - childPos
when hScrollRequired $
drawRect renderer hScrollRect barColorH Nothing
handleMessage wenv ctx message widgetInst = result where
handleScrollMessage (ScrollTo rect) = scrollTo wenv widgetInst rect
result = cast message >>= handleScrollMessage
when vScrollRequired $
drawRect renderer vScrollRect barColorV Nothing
scrollTo wenv widgetInst rect
| rectInRect rect viewport = Nothing
| otherwise = Just $ resultWidget newInstance
where
viewport = _instanceViewport widgetInst
Rect rx ry rw rh = rect
Rect vx vy vw vh = viewport
diffL = vx - rx
diffR = vx + vw - (rx + rw)
diffT = vy - ry
diffB = vy + vh - (ry + rh)
stepX
| rectInRectH rect viewport = dx
| abs diffL <= abs diffR = diffL + dx
| otherwise = diffR + dx
stepY
| rectInRectV rect viewport = dy
| abs diffT <= abs diffB = diffT + dy
| otherwise = diffB + dy
newState = state {
_sstDeltaX = scrollAxis stepX 0 childWidth vw,
_sstDeltaY = scrollAxis stepY 0 childHeight vh
}
newInstance = rebuildWidget wenv newState widgetInst prevReqs
when hScrollRequired $
drawRect renderer hThumbRect (Just thumbColorH) Nothing
updateScrollThumb state activeBar point viewport sctx = newState where
Point px py = point
ScrollContext{..} = sctx
Rect rx ry rw rh = viewport
hMid = _rw hThumbRect / 2
vMid = _rh vThumbRect / 2
hDelta = (rx - px + hMid) / hScrollRatio
vDelta = (ry - py + vMid) / vScrollRatio
newDeltaX
| activeBar == HBar = scrollAxis hDelta 0 childWidth rw
| otherwise = dx
newDeltaY
| activeBar == VBar = scrollAxis vDelta 0 childHeight rh
| otherwise = dy
newState = state {
_sstDeltaX = newDeltaX,
_sstDeltaY = newDeltaY
}
when vScrollRequired $
drawRect renderer vThumbRect (Just thumbColorV) Nothing
where
viewport = _instanceViewport widgetInst
ScrollContext{..} = scrollStatus config wenv state viewport
draggingH = _sstDragging state == Just HBar
draggingV = _sstDragging state == Just VBar
barColorH = if hMouseInScroll then _scActiveBarColor config else _scIdleBarColor config
barColorV = if vMouseInScroll then _scActiveBarColor config else _scIdleBarColor config
thumbColorH = if hMouseInThumb || draggingH then _scActiveThumbColor config else _scIdleThumbColor config
thumbColorV = if vMouseInThumb || draggingV then _scActiveThumbColor config else _scIdleThumbColor config
rebuildWidget wenv newState widgetInst reqs = newInst where
newWidget = makeScroll config newState
tempInst = widgetInst { _instanceWidget = newWidget }
widget = _instanceViewport tempInst
renderArea = _instanceRenderArea tempInst
newInst = scrollResize (Just newWidget) wenv widget renderArea tempInst reqs
scrollStatus :: ScrollConfig -> WidgetEnv s e -> ScrollState -> Rect -> ScrollContext
preferredSize wenv widgetInst children reqs = Node sizeReq reqs where
size = _sizeRequested . nodeValue $ Seq.index reqs 0
sizeReq = SizeReq size FlexibleSize FlexibleSize
scrollResize uWidget wenv viewport renderArea widgetInst reqs = newInst where
Rect l t w h = renderArea
child = Seq.index (_instanceChildren widgetInst) 0
childReq = fromMaybe (singleNode def) (Seq.lookup 0 (nodeChildren reqs))
Size childWidth2 childHeight2 = _sizeRequested $ nodeValue childReq
areaW = max w childWidth2
areaH = max h childHeight2
cRenderArea = Rect (l + dx) (t + dy) areaW areaH
defWidget = makeScroll config $ state {
_sstChildSize = Size areaW areaH,
_sstReqSize = reqs
}
newWidget = fromMaybe defWidget uWidget
cWidget = _instanceWidget child
newChild = _widgetResize cWidget wenv viewport cRenderArea child childReq
newInst = widgetInst {
_instanceViewport = viewport,
_instanceRenderArea = renderArea,
_instanceWidget = newWidget,
_instanceChildren = Seq.singleton newChild
}
render renderer wenv widgetInst = do
setScissor renderer viewport
containerRender defaultContainerRender renderer wenv widgetInst
resetScissor renderer
when hScrollRequired $
drawRect renderer hScrollRect barColorH Nothing
when vScrollRequired $
drawRect renderer vScrollRect barColorV Nothing
when hScrollRequired $
drawRect renderer hThumbRect (Just thumbColorH) Nothing
when vScrollRequired $
drawRect renderer vThumbRect (Just thumbColorV) Nothing
where
viewport = _instanceViewport widgetInst
ScrollContext{..} = scrollStatus config wenv state viewport
draggingH = _sstDragging state == Just HBar
draggingV = _sstDragging state == Just VBar
barColorH
| hMouseInScroll = _scActiveBarColor config
| otherwise = _scIdleBarColor config
barColorV
| vMouseInScroll = _scActiveBarColor config
| otherwise = _scIdleBarColor config
thumbColorH
| hMouseInThumb || draggingH = _scActiveThumbColor config
| otherwise = _scIdleThumbColor config
thumbColorV
| vMouseInThumb || draggingV = _scActiveThumbColor config
| otherwise = _scIdleThumbColor config
scrollStatus
:: ScrollConfig -> WidgetEnv s e -> ScrollState -> Rect -> ScrollContext
scrollStatus config wenv scrollState viewport = ScrollContext{..} where
ScrollState _ dx dy (Size childWidth childHeight) _ = scrollState
barThickness = _scBarThickness config
@ -270,10 +321,20 @@ scrollStatus config wenv scrollState viewport = ScrollContext{..} where
vScrollRatio = min (vpHeight / childHeight) 1
hScrollRequired = hScrollRatio < 1
vScrollRequired = vScrollRatio < 1
hScrollRect = Rect vpLeft (vpTop + hScrollTop) (vpLeft + vpWidth) (vpTop + vpHeight)
vScrollRect = Rect (vpLeft + vScrollLeft) vpTop (vpLeft + vpWidth) (vpTop + vpHeight)
hThumbRect = Rect (vpLeft - hScrollRatio * dx) (vpTop + hScrollTop) (hScrollRatio * vpWidth) barThickness
vThumbRect = Rect (vpLeft + vScrollLeft) (vpTop - vScrollRatio * dy) barThickness (vScrollRatio * vpHeight)
hScrollRect =
Rect vpLeft (vpTop + hScrollTop) (vpLeft + vpWidth) (vpTop + vpHeight)
vScrollRect =
Rect (vpLeft + vScrollLeft) vpTop (vpLeft + vpWidth) (vpTop + vpHeight)
hThumbRect = Rect
(vpLeft - hScrollRatio * dx)
(vpTop + hScrollTop)
(hScrollRatio * vpWidth)
barThickness
vThumbRect = Rect
(vpLeft + vScrollLeft)
(vpTop - vScrollRatio * dy)
barThickness
(vScrollRatio * vpHeight)
hMouseInScroll = pointInRect mousePos hScrollRect
vMouseInScroll = pointInRect mousePos vScrollRect
hMouseInThumb = pointInRect mousePos hThumbRect

View File

@ -16,9 +16,11 @@ defaultSpace :: Double
defaultSpace = 10
makeSpacer :: Widget s e
makeSpacer = createWidget {
makeSpacer = widget where
widget = createWidget {
_widgetPreferredSize = preferredSize
}
where
preferredSize wenv widgetInst = singleNode sizeReq where
sizeReq = SizeReq (Size defaultSpace defaultSpace) RemainderSize RemainderSize
preferredSize wenv widgetInst = singleNode sizeReq where
size = Size defaultSpace defaultSpace
sizeReq = SizeReq size RemainderSize RemainderSize

View File

@ -28,73 +28,85 @@ vstack children = (defaultWidgetInstance "vstack" (makeStack False)) {
}
makeStack :: Bool -> Widget s e
makeStack isHorizontal = createContainer {
makeStack isHorizontal = widget where
widget = createContainer {
_widgetPreferredSize = containerPreferredSize preferredSize,
_widgetResize = containerResize resize
}
where
preferredSize wenv widgetInst children reqs = Node reqSize reqs where
(_, vreqs) = visibleChildrenReq children reqs
reqSize = SizeReq (calcPreferredSize vreqs) FlexibleSize FlexibleSize
resize wenv viewport renderArea widgetInst children reqs = (widgetInst, assignedArea) where
Rect l t w h = renderArea
childrenPairs = Seq.zip children reqs
(vchildren, vreqs) = visibleChildrenReq children reqs
mainSize = if isHorizontal then w else h
mainStart = if isHorizontal then l else t
policyFilter policy req = policySelector req == policy
sChildren = Seq.filter (policyFilter StrictSize) vreqs
fChildren = Seq.filter (policyFilter FlexibleSize) vreqs
rChildren = Seq.filter (policyFilter RemainderSize) vreqs
fExists = not $ null fChildren
rExists = not $ null rChildren
sSize = sizeSelector $ calcPreferredSize sChildren
fSize = sizeSelector $ calcPreferredSize fChildren
rSize = max 0 (mainSize - sSize)
fCount = fromIntegral $ length fChildren
rCount = fromIntegral $ length rChildren
fAvg = if fExists then fSize / fCount else 0
fBigFilter c = sizeSelector (_sizeRequested c) >= fAvg
fBigSize = sizeSelector $ calcPreferredSize (Seq.filter fBigFilter fChildren)
fExtra = if fExists then (rSize - fSize) / fBigSize else 0
rUnit = if rExists && not fExists then rSize / rCount else 0
assignedArea = Seq.zip newViewports newViewports
(newViewports, _) = foldl' foldHelper (Seq.empty, mainStart) childrenPairs
foldHelper (accum, offset) childPair = (newAccum, newOffset) where
newSize = resizeChild renderArea fAvg fExtra rUnit offset childPair
newAccum = accum |> newSize
newOffset = offset + rectSelector newSize
preferredSize wenv widgetInst children reqs = Node reqSize reqs where
(_, vreqs) = visibleChildrenReq children reqs
reqSize = SizeReq (calcPreferredSize vreqs) FlexibleSize FlexibleSize
resizeChild renderArea fAvg fExtra rUnit offset childPair = result where
Rect l t w h = renderArea
result = if | not $ _instanceVisible childInstance -> emptyRect
| isHorizontal -> hRect
| otherwise -> vRect
childInstance = fst childPair
req = nodeValue $ snd childPair
srSize = _sizeRequested req
emptyRect = Rect l t 0 0
hRect = Rect offset t calcNewSize h
vRect = Rect l offset w calcNewSize
calcNewSize = case policySelector req of
StrictSize -> sizeSelector srSize
FlexibleSize
| sizeSelector srSize >= fAvg -> (1 + fExtra) * sizeSelector srSize
| otherwise -> sizeSelector srSize
RemainderSize -> rUnit
resize wenv viewport renderArea widgetInst children reqs = resized where
Rect l t w h = renderArea
childrenPairs = Seq.zip children reqs
(vchildren, vreqs) = visibleChildrenReq children reqs
mainSize = if isHorizontal then w else h
mainStart = if isHorizontal then l else t
policyFilter policy req = policySelector req == policy
sChildren = Seq.filter (policyFilter StrictSize) vreqs
fChildren = Seq.filter (policyFilter FlexibleSize) vreqs
rChildren = Seq.filter (policyFilter RemainderSize) vreqs
fExists = not $ null fChildren
rExists = not $ null rChildren
sSize = sizeSelector $ calcPreferredSize sChildren
fSize = sizeSelector $ calcPreferredSize fChildren
rSize = max 0 (mainSize - sSize)
fCount = fromIntegral $ length fChildren
rCount = fromIntegral $ length rChildren
fAvg = if fExists then fSize / fCount else 0
fLargeFilter c = sizeSelector (_sizeRequested c) >= fAvg
fLargeFiltered = Seq.filter fLargeFilter fChildren
fLargeSize = sizeSelector $ calcPreferredSize fLargeFiltered
fExtra
| fExists = (rSize - fSize) / fLargeSize
| otherwise = 0
rUnit
| rExists && not fExists = rSize / rCount
| otherwise = 0
assignedArea = Seq.zip newViewports newViewports
(newViewports, _) = foldl' foldHelper (Seq.empty, mainStart) childrenPairs
foldHelper (accum, offset) childPair = (newAccum, newOffset) where
newSize = resizeChild renderArea fAvg fExtra rUnit offset childPair
newAccum = accum |> newSize
newOffset = offset + rectSelector newSize
resized = (widgetInst, assignedArea)
calcPreferredSize vreqs = Size width height where
(maxWidth, sumWidth, maxHeight, sumHeight) = calcDimensions vreqs
width = if isHorizontal then sumWidth else maxWidth
height = if isHorizontal then maxHeight else sumHeight
resizeChild renderArea fAvg fExtra rUnit offset childPair = result where
Rect l t w h = renderArea
childInstance = fst childPair
req = nodeValue $ snd childPair
srSize = _sizeRequested req
emptyRect = Rect l t 0 0
hRect = Rect offset t calcNewSize h
vRect = Rect l offset w calcNewSize
calcNewSize = case policySelector req of
StrictSize -> sizeSelector srSize
FlexibleSize
| sizeSelector srSize >= fAvg -> (1 + fExtra) * sizeSelector srSize
| otherwise -> sizeSelector srSize
RemainderSize -> rUnit
result
| not $ _instanceVisible childInstance = emptyRect
| isHorizontal = hRect
| otherwise = vRect
calcDimensions vreqs = (maxWidth, sumWidth, maxHeight, sumHeight) where
maxWidth = if Seq.null vreqs then 0 else (maximum . fmap (_w . _sizeRequested)) vreqs
sumWidth = (sum . fmap (_w . _sizeRequested)) vreqs
maxHeight = if null vreqs then 0 else (maximum . fmap (_h . _sizeRequested)) vreqs
sumHeight = (sum . fmap (_h . _sizeRequested)) vreqs
calcPreferredSize vreqs = Size width height where
(maxWidth, sumWidth, maxHeight, sumHeight) = calcDimensions vreqs
width = if isHorizontal then sumWidth else maxWidth
height = if isHorizontal then maxHeight else sumHeight
sizeSelector = if isHorizontal then _w else _h
rectSelector = if isHorizontal then _rw else _rh
policySelector = if isHorizontal then _sizePolicyWidth else _sizePolicyHeight
calcDimensions vreqs = (maxWidth, sumWidth, maxHeight, sumHeight) where
sumWidth = (sum . fmap (_w . _sizeRequested)) vreqs
sumHeight = (sum . fmap (_h . _sizeRequested)) vreqs
maxWidth
| Seq.null vreqs = 0
| otherwise = (maximum . fmap (_w . _sizeRequested)) vreqs
maxHeight
| Seq.null vreqs = 0
| otherwise = (maximum . fmap (_h . _sizeRequested)) vreqs
sizeSelector = if isHorizontal then _w else _h
rectSelector = if isHorizontal then _rw else _rh
policySelector = if isHorizontal then _sizePolicyWidth else _sizePolicyHeight

View File

@ -68,7 +68,8 @@ makeInstance widget = (defaultWidgetInstance "textField" widget) {
}
makeTextField :: TextFieldConfig s e -> TextFieldState -> Widget s e
makeTextField config state = createWidget {
makeTextField config state = widget where
widget = createWidget {
_widgetInit = init,
_widgetGetState = makeState state,
_widgetMerge = widgetMerge merge,
@ -76,83 +77,89 @@ makeTextField config state = createWidget {
_widgetPreferredSize = preferredSize,
_widgetRender = render
}
where
TextFieldState currText currPos = state
(part1, part2) = T.splitAt currPos currText
currentValue wenv = widgetValueGet (_weModel wenv) (_tfcValue config)
init wenv widgetInst = resultWidget newInstance where
currText = currentValue wenv
newState = TextFieldState currText 0
newInstance = widgetInst {
_instanceWidget = makeTextField config newState
}
TextFieldState currText currPos = state
(part1, part2) = T.splitAt currPos currText
currentValue wenv = widgetValueGet (_weModel wenv) (_tfcValue config)
merge wenv oldState widgetInst = resultWidget newInstance where
TextFieldState _ oldPos = fromMaybe textFieldState (useState oldState)
currText = currentValue wenv
newPos = if | T.length currText < oldPos -> T.length currText
| otherwise -> oldPos
newState = TextFieldState currText newPos
newInstance = widgetInst {
_instanceWidget = makeTextField config newState
}
init wenv widgetInst = resultWidget newInstance where
currText = currentValue wenv
newState = TextFieldState currText 0
newInstance = widgetInst {
_instanceWidget = makeTextField config newState
}
handleKeyPress txt tp code
| isKeyBackspace code && tp > 0 = (T.append (T.init part1) part2, tp - 1)
| isKeyLeft code && tp > 0 = (txt, tp - 1)
| isKeyRight code && tp < T.length txt = (txt, tp + 1)
| isKeyBackspace code || isKeyLeft code || isKeyRight code = (txt, tp)
| otherwise = (txt, tp)
merge wenv oldState widgetInst = resultWidget newInstance where
TextFieldState _ oldPos = fromMaybe textFieldState (useState oldState)
currText = currentValue wenv
newPos = if | T.length currText < oldPos -> T.length currText
| otherwise -> oldPos
newState = TextFieldState currText newPos
newInstance = widgetInst {
_instanceWidget = makeTextField config newState
}
handleEvent wenv target evt widgetInst = case evt of
Click (Point x y) _ -> Just $ resultReqs reqs widgetInst where
reqs = [SetFocus $ _instancePath widgetInst]
handleKeyPress txt tp code
| isKeyBackspace code && tp > 0 = (T.append (T.init part1) part2, tp - 1)
| isKeyLeft code && tp > 0 = (txt, tp - 1)
| isKeyRight code && tp < T.length txt = (txt, tp + 1)
| isKeyBackspace code || isKeyLeft code || isKeyRight code = (txt, tp)
| otherwise = (txt, tp)
KeyAction mod code KeyPressed -> Just $ resultReqs reqs newInstance where
(newText, newPos) = handleKeyPress currText currPos code
reqGetClipboard = [GetClipboard (_instancePath widgetInst) | isClipboardPaste wenv evt]
reqSetClipboard = [SetClipboard (ClipboardText currText) | isClipboardCopy wenv evt]
reqUpdateModel = if | currText /= newText -> widgetValueSet (_tfcValue config) newText
| otherwise -> []
reqs = reqGetClipboard ++ reqSetClipboard ++ reqUpdateModel
newState = TextFieldState newText newPos
newInstance = widgetInst {
_instanceWidget = makeTextField config newState
}
handleEvent wenv target evt widgetInst = case evt of
Click (Point x y) _ -> Just $ resultReqs reqs widgetInst where
reqs = [SetFocus $ _instancePath widgetInst]
TextInput newText -> insertText wenv widgetInst newText
Clipboard (ClipboardText newText) -> insertText wenv widgetInst newText
_ -> Nothing
insertText wenv widgetInst addedText = Just $ resultReqs reqs newInstance where
newText = T.concat [part1, addedText, part2]
newPos = currPos + T.length addedText
KeyAction mod code KeyPressed -> Just $ resultReqs reqs newInstance where
(newText, newPos) = handleKeyPress currText currPos code
isPaste = isClipboardPaste wenv evt
isCopy = isClipboardCopy wenv evt
reqGetClipboard = [GetClipboard (_instancePath widgetInst) | isPaste]
reqSetClipboard = [SetClipboard (ClipboardText currText) | isCopy]
reqUpdateModel
| currText /= newText = widgetValueSet (_tfcValue config) newText
| otherwise = []
reqs = reqGetClipboard ++ reqSetClipboard ++ reqUpdateModel
newState = TextFieldState newText newPos
reqs = widgetValueSet (_tfcValue config) newText
newInstance = widgetInst {
_instanceWidget = makeTextField config newState
}
preferredSize wenv widgetInst = singleNode sizeReq where
Style{..} = _instanceStyle widgetInst
size = getTextBounds wenv _styleText currText
sizeReq = SizeReq size FlexibleSize StrictSize
render renderer wenv widgetInst =
let WidgetInstance{..} = widgetInst
ts = _weTimestamp wenv
textStyle = _styleText _instanceStyle
cursorAlpha = if isFocused wenv widgetInst then fromIntegral (ts `mod` 1000) / 1000.0 else 0
textColor = (tsTextColor textStyle) { _alpha = cursorAlpha }
renderArea@(Rect rl rt rw rh) = _instanceRenderArea
in do
drawStyledBackground renderer renderArea _instanceStyle
Rect tl tt _ _ <- drawText renderer renderArea textStyle currText
TextInput newText -> insertText wenv widgetInst newText
when (isFocused wenv widgetInst) $ do
let Size sw sh = getTextBounds wenv textStyle part1
drawRect renderer (Rect (tl + sw) tt (_tfcCaretWidth config) sh) (Just textColor) Nothing
return ()
Clipboard (ClipboardText newText) -> insertText wenv widgetInst newText
_ -> Nothing
insertText wenv widgetInst addedText = Just $ resultReqs reqs newInst where
newText = T.concat [part1, addedText, part2]
newPos = currPos + T.length addedText
newState = TextFieldState newText newPos
reqs = widgetValueSet (_tfcValue config) newText
newInst = widgetInst {
_instanceWidget = makeTextField config newState
}
preferredSize wenv widgetInst = singleNode sizeReq where
Style{..} = _instanceStyle widgetInst
size = getTextBounds wenv _styleText currText
sizeReq = SizeReq size FlexibleSize StrictSize
render renderer wenv widgetInst = do
drawStyledBackground renderer renderArea _instanceStyle
Rect tl tt _ _ <- drawText renderer renderArea textStyle currText
when (isFocused wenv widgetInst) $ do
let Size sw sh = getTextBounds wenv textStyle part1
drawRect renderer (Rect (tl + sw) tt caretWidth sh) caretColor Nothing
where
WidgetInstance{..} = widgetInst
ts = _weTimestamp wenv
renderArea@(Rect rl rt rw rh) = _instanceRenderArea
textStyle = _styleText _instanceStyle
caretAlpha
| isFocused wenv widgetInst = fromIntegral (ts `mod` 1000) / 1000.0
| otherwise = 0
caretColor = Just $ (tsTextColor textStyle) { _alpha = caretAlpha }
caretWidth = _tfcCaretWidth config