Basic scrollbar display, minor general improvements and tests that will not stand the test of time

This commit is contained in:
Francisco Vallarino 2019-11-06 01:25:04 -03:00
parent 79faed3625
commit b316848600
9 changed files with 107 additions and 41 deletions

2
.ghci
View File

@ -1 +1,3 @@
:set -fno-ghci-sandbox
:set prompt "\x03BB: "
:set prompt-cont " | "

2
.gitignore vendored
View File

@ -1,7 +1,5 @@
.stack-work/
.vscode/
app copy/
src copy/
hs-GUI.cabal
*~
*.prof

View File

@ -124,8 +124,8 @@ buildUI model = styledTree where
textField `style` textStyle
],
hgrid [
scroll $ label "This is a really really really long label, you know?" `style` labelStyle,
label "Short"
label "Short",
scroll $ label "This is a really really really long label, you know?" `style` labelStyle
],
hgrid [
button (Action1 1) `style` buttonStyle,
@ -186,13 +186,13 @@ handleSystemEvents :: Renderer WidgetM -> [W.SystemEvent] -> TR.Path -> WidgetTr
handleSystemEvents renderer systemEvents currentFocus widgets =
foldM (\newWidgets event -> handleSystemEvent renderer event currentFocus newWidgets) widgets systemEvents
handleEvent :: Renderer WidgetM -> W.SystemEvent -> TR.Path -> WidgetTree -> (Bool, SQ.Seq AppEvent, Maybe WidgetTree)
handleEvent :: Renderer WidgetM -> W.SystemEvent -> TR.Path -> WidgetTree -> (Bool, [W.EventRequest], SQ.Seq AppEvent, Maybe WidgetTree)
handleEvent renderer systemEvent@(W.Click point _ _) currentFocus widgets = W.handleEventFromPoint point widgets systemEvent
handleEvent renderer systemEvent@(W.KeyAction _ _) currentFocus widgets = W.handleEventFromPath currentFocus widgets systemEvent
handleSystemEvent :: Renderer WidgetM -> W.SystemEvent -> TR.Path -> WidgetTree -> AppM WidgetTree
handleSystemEvent renderer systemEvent currentFocus widgets = do
let (stop, appEvents, newWidgets) = handleEvent renderer systemEvent currentFocus widgets
let (stop, eventRequests, appEvents, newWidgets) = handleEvent renderer systemEvent currentFocus widgets
let newRoot = fromMaybe widgets newWidgets
updatedRoot <- if (not stop && isKeyPressed systemEvent keycodeTab) then do
@ -203,11 +203,16 @@ handleSystemEvent renderer systemEvent currentFocus widgets = do
else
return newRoot
if length appEvents == 0 then
updatedRoot2 <- if length appEvents == 0 then
return updatedRoot
else
handleAppEvents renderer appEvents updatedRoot
if elem W.ResizeChildren eventRequests then
updateUI renderer updatedRoot2
else
return updatedRoot2
keycodeTab = fromIntegral $ Keyboard.unwrapKeycode SDL.KeycodeTab
isKeyboardEvent :: W.SystemEvent -> Bool

View File

@ -51,11 +51,14 @@ instance Semigroup Color where
instance Default Color where
def = RGB 0 0 0
white = RGB 255 255 255
black = RGB 0 0 0
red = RGB 255 0 0
green = RGB 0 255 0
blue = RGB 0 0 255
white = RGB 255 255 255
black = RGB 0 0 0
red = RGB 255 0 0
green = RGB 0 255 0
blue = RGB 0 0 255
lightGray = RGB 191 191 191
gray = RGB 127 127 127
darkGray = RGB 63 63 63
makeLenses ''Point
makeLenses ''Size
@ -112,3 +115,6 @@ midPoint :: Point -> Point -> Point
midPoint (Point x1 y1) (Point x2 y2) = Point x3 y3 where
x3 = (x2 + x1) / 2
y3 = (y2 + y1) / 2
moveRect :: Rect -> Double -> Double -> Rect
moveRect (Rect x y w h) dx dy = Rect (x + dx) (y + dy) w h

View File

@ -24,7 +24,7 @@ makeButton state onClick = Widget widgetType focusable handleEvent preferredSize
widgetType = "button"
focusable = False
handleEvent view evt = case evt of
Click (Point x y) _ status -> widgetEventResult False events (makeButton newState onClick) where
Click (Point x y) _ status -> eventResult events (makeButton newState onClick) where
isPressed = status == PressedBtn && inRect view (Point x y)
newState = if isPressed then state + 1 else state
events = if isPressed then [onClick] else []

View File

@ -38,8 +38,10 @@ data KeyMotion = KeyPressed | KeyReleased deriving (Show, Eq)
data SystemEvent = Click Point Button ButtonState |
KeyAction KeyCode KeyMotion deriving (Show, Eq)
data EventRequest = StopPropagation | ResizeChildren | ResizeAll deriving (Show, Eq)
data WidgetEventResult s e m = WidgetEventResult {
_eventResultStop :: Bool,
_eventResultRequest :: [EventRequest],
_eventResultUserEvents :: [e],
_eventResultNewWidget :: Maybe (Widget s e m)
}
@ -50,8 +52,11 @@ data WidgetResizeResult s e m = WidgetResizeResult {
_resizeResultWidget :: Maybe (Widget s e m)
}
widgetEventResult :: Bool -> [e] -> (Widget s e m) -> Maybe (WidgetEventResult s e m)
widgetEventResult stop userEvents newWidget = Just $ WidgetEventResult stop userEvents (Just newWidget)
eventResult :: [e] -> (Widget s e m) -> Maybe (WidgetEventResult s e m)
eventResult userEvents newWidget = Just $ WidgetEventResult [] userEvents (Just newWidget)
eventResultRequest :: [EventRequest] -> [e] -> (Widget s e m) -> Maybe (WidgetEventResult s e m)
eventResultRequest requests userEvents newWidget = Just $ WidgetEventResult requests userEvents (Just newWidget)
newtype WidgetType = WidgetType String deriving Eq
newtype WidgetKey = WidgetKey String deriving Eq
@ -143,6 +148,7 @@ data WidgetInstance s e m =
_widgetInstanceRenderArea :: Rect,
-- | Style attributes of the widget instance
_widgetInstanceStyle :: Style
--_widgetInstanceElementStyle :: Style
}
key :: (MonadState s m) => WidgetKey -> WidgetInstance s e m -> WidgetInstance s e m
@ -193,18 +199,20 @@ mergeTrees node1@(Node widget1 seq1) (Node widget2 seq2) = newNode where
handleWidgetEvents :: (MonadState s m) => Widget s e m -> Rect -> SystemEvent -> Maybe (WidgetEventResult s e m)
handleWidgetEvents (Widget {..}) viewport systemEvent = _widgetHandleEvent viewport systemEvent
handleChildEvent :: (MonadState s m) => (a -> SQ.Seq (WidgetNode s e m) -> (a, Maybe Int)) -> a -> WidgetNode s e m -> SystemEvent -> (Bool, SQ.Seq e, Maybe (WidgetNode s e m))
handleChildEvent selectorFn selector treeNode@(Node wn@WidgetInstance{..} children) systemEvent = (stopPropagation, userEvents, newTreeNode) where
(stopPropagation, userEvents, newTreeNode) = case spChild of
True -> (spChild, ueChild, newNode1)
False -> (sp, ueChild SQ.>< ue, newNode2)
(spChild, ueChild, tnChild, tnChildIdx) = case selectorFn selector children of
(_, Nothing) -> (False, SQ.empty, Nothing, 0)
(newSelector, Just idx) -> (sp2, ue2, tn2, idx) where
(sp2, ue2, tn2) = handleChildEvent selectorFn newSelector (SQ.index children idx) systemEvent
(sp, ue, tn) = case handleWidgetEvents _widgetInstanceWidget _widgetInstanceViewport systemEvent of
Nothing -> (False, SQ.empty, Nothing)
Just (WidgetEventResult sp2 ue2 widget) -> (sp2, SQ.fromList ue2, if isNothing widget then Nothing else Just (Node (wn { _widgetInstanceWidget = fromJust widget }) children))
handleChildEvent :: (MonadState s m) => (a -> SQ.Seq (WidgetNode s e m) -> (a, Maybe Int)) -> a -> WidgetNode s e m -> SystemEvent -> (Bool, [EventRequest], SQ.Seq e, Maybe (WidgetNode s e m))
handleChildEvent selectorFn selector treeNode@(Node wn@WidgetInstance{..} children) systemEvent = (stopPropagation, eventRequests, userEvents, newTreeNode) where
(stopPropagation, eventRequests, userEvents, newTreeNode) = case spChild of
True -> (spChild, erChild, ueChild, newNode1)
False -> (sp, erChild ++ er, ueChild SQ.>< ue, newNode2)
-- Children widgets
(spChild, erChild, ueChild, tnChild, tnChildIdx) = case selectorFn selector children of
(_, Nothing) -> (False, [], SQ.empty, Nothing, 0)
(newSelector, Just idx) -> (sp2, er2, ue2, tn2, idx) where
(sp2, er2, ue2, tn2) = handleChildEvent selectorFn newSelector (SQ.index children idx) systemEvent
-- Current widget
(sp, er, ue, tn) = case handleWidgetEvents _widgetInstanceWidget _widgetInstanceRenderArea systemEvent of
Nothing -> (False, [], SQ.empty, Nothing)
Just (WidgetEventResult er2 ue2 widget) -> (elem StopPropagation er2, er2, SQ.fromList ue2, if isNothing widget then Nothing else Just (Node (wn { _widgetInstanceWidget = fromJust widget }) children))
newNode1 = case tnChild of
Nothing -> Nothing
Just wnChild -> Just $ Node wn (SQ.update tnChildIdx wnChild children)
@ -214,14 +222,14 @@ handleChildEvent selectorFn selector treeNode@(Node wn@WidgetInstance{..} childr
(Just pn, Nothing) -> tn
(Just (Node wn _), Just tnChild) -> Just $ Node wn (SQ.update tnChildIdx tnChild children)
handleEventFromPath :: (MonadState s m) => Path -> WidgetNode s e m -> SystemEvent -> (Bool, SQ.Seq e, Maybe (WidgetNode s e m))
handleEventFromPath :: (MonadState s m) => Path -> WidgetNode s e m -> SystemEvent -> (Bool, [EventRequest], SQ.Seq e, Maybe (WidgetNode s e m))
handleEventFromPath path widgetInstance systemEvent = handleChildEvent pathSelector path widgetInstance systemEvent where
pathSelector [] _ = ([], Nothing)
pathSelector (p:ps) children
| length children > p = (ps, Just p)
| otherwise = ([], Nothing)
handleEventFromPoint :: (MonadState s m) => Point -> WidgetNode s e m -> SystemEvent -> (Bool, SQ.Seq e, Maybe (WidgetNode s e m))
handleEventFromPoint :: (MonadState s m) => Point -> WidgetNode s e m -> SystemEvent -> (Bool, [EventRequest], SQ.Seq e, Maybe (WidgetNode s e m))
handleEventFromPoint cursorPos widgetInstance systemEvent = handleChildEvent rectSelector cursorPos widgetInstance systemEvent where
rectSelector point children = (point, SQ.lookup 0 inRectList) where
inRectList = fmap snd $ SQ.filter inNodeRect childrenPair
@ -275,4 +283,3 @@ resizeNode renderer viewport renderArea (Node _ childrenSizes) (Node widgetInsta
}
childrenPair = SQ.zip4 childrenSizes childrenWns (SQ.fromList viewports) (SQ.fromList renderAreas)
childResize = \(size, node, viewport, renderArea) -> resizeNode renderer viewport renderArea size node

View File

@ -15,6 +15,15 @@ import GUI.Widget.Core
import qualified Data.Text as T
{--
***********************************
Implement auto scalable label! Selects correct size to fit the given text
***********************************
--}
label :: (MonadState s m) => T.Text -> WidgetNode s e m
label caption = singleWidget (makeLabel caption)

View File

@ -4,6 +4,8 @@
module GUI.Widget.Scroll (scroll) where
import Data.Default
import Debug.Trace
import Control.Monad
import Control.Monad.State
@ -16,28 +18,65 @@ import GUI.Data.Tree
import GUI.Widget.Core
data ScrollState = ScrollState {
_scPosition :: Int
_scDeltaX :: !Double,
_scDeltaY :: !Double,
_scChildSize :: Size
} deriving (Eq, Show)
scroll :: (MonadState s m) => WidgetNode s e m -> WidgetNode s e m
scroll managedWidget = parentWidget (makeScroll (ScrollState 0)) [managedWidget]
scroll managedWidget = parentWidget (makeScroll (ScrollState 0 0 def)) [managedWidget]
makeScroll :: (MonadState s m) => ScrollState -> Widget s e m
makeScroll state = Widget widgetType focusable handleEvent preferredSize resizeChildren render
makeScroll state@(ScrollState dx dy cs@(Size cw ch)) = Widget {
_widgetType = "scroll",
_widgetFocusable = False,
_widgetHandleEvent = handleEvent,
_widgetPreferredSize = preferredSize,
_widgetResizeChildren = resizeChildren,
_widgetRender = render
}
where
stepSize = 50
widgetType = "scroll"
focusable = False
handleEvent view evt = Nothing
handleEvent view@(Rect rx ry rw rh) evt = case evt of
Click (Point px py) btn status -> eventResultRequest [ResizeChildren] [] (makeScroll newState) where
isPressed = status == PressedBtn && inRect view (Point px py)
isLeftClick = traceShow state $ isPressed && btn == LeftBtn
isRigthClick = isPressed && btn == RightBtn
newDx = if | isLeftClick -> if dx + stepSize < 0 then dx + stepSize else 0
| isRigthClick -> if cw - rw + dx - stepSize > 0 then dx - stepSize else rw - cw
| otherwise -> dx
newState = ScrollState newDx dy cs
_ -> Nothing
preferredSize _ _ children = return (head children)
resizeChildren (Rect l t _ _) _ children = Just $ WidgetResizeResult viewport renderArea newWidget where
Size w h = (head children)
newWidget = Just $ makeScroll state
resizeChildren (Rect l t w h) _ children = Just $ WidgetResizeResult viewport renderArea newWidget where
Size cw2 ch2 = (head children)
newWidget = Just $ makeScroll (ScrollState dx dy (Size cw2 ch2))
viewport = [Rect l t w h]
renderArea = [Rect l t w h]
renderArea = [Rect (l + dx) (t + dy) cw2 ch2]
render renderer WidgetInstance{..} children ts =
do
scissor renderer _widgetInstanceViewport
handleRenderChildren renderer children ts
resetScissor renderer
drawText renderer _widgetInstanceRenderArea (_textStyle _widgetInstanceStyle) (T.pack (show state))
drawText renderer _widgetInstanceRenderArea (_textStyle _widgetInstanceStyle) (T.pack (show dx))
when (barRatioH < 1) $ do
drawRect renderer scrollRectH (Just darkGray) Nothing
when (barRatioV < 1) $ do
drawRect renderer scrollRectV (Just darkGray) Nothing
where
barThickness = 10
vpLeft = (_rx _widgetInstanceViewport)
vpTop = (_ry _widgetInstanceViewport)
vpWidth = (_rw _widgetInstanceViewport)
vpHeight = (_rh _widgetInstanceViewport)
barTop = vpHeight - barThickness
barLeft = vpWidth - barThickness
barRatioH = vpWidth / cw
barRatioV = vpHeight / ch
scrollRectH = Rect (vpLeft - barRatioH * dx) (vpTop + barTop) (barRatioH * vpWidth) barThickness
scrollRectV = Rect (vpLeft + barLeft) (vpTop - barRatioV * dy) barThickness (barRatioV * vpHeight)

View File

@ -48,7 +48,7 @@ makeTextField (TextFieldState txt tp) = Widget widgetType focusable handleEvent
newText = if isKeyPrintable code then [chr code] else ""
(part1, part2) = splitAt currTp currText
handleEvent _ evt = case evt of
KeyAction code KeyPressed -> widgetEventResult False [] (makeTextField newState) where
KeyAction code KeyPressed -> eventResult [] (makeTextField newState) where
(txt2, tp2) = handleKeyPress txt tp code
newState = TextFieldState txt2 tp2
_ -> Nothing