keid-ui-dearimgui-0.1.3.2: src/Render/ImGui/Window.hs
module Render.ImGui.Window
( Window(..)
, pack
, toView
, setNextWindowBox
, run
) where
import RIO
import Data.Bits ((.|.))
import Data.ByteString.Unsafe (unsafeInit, unsafeUseAsCString)
import DearImGui.Raw qualified as Raw
import Foreign (castPtr, free, new, nullPtr, peek, with)
import Foreign.C.Types (CFloat(..))
import Geomancy (Vec2, vec2)
import Geomancy.Layout.Box (Box(..))
import Geomancy.Layout.View as LV
import GHC.OverloadedLabels (IsLabel(..))
import GHC.Records (HasField(..))
import GHC.TypeLits (KnownSymbol, symbolVal)
data Window = Window
{ name0 :: ByteString -- ^ NUL-terminated UTF-8 encoded name for less copying.
, contentSize :: Maybe Vec2 -- ^ Set content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding.
, sizeConstraints :: Maybe (Vec2, Vec2) -- ^ Set size limits. use -1,-1 on either X/Y axis to preserve the current size. Sizes will be rounded down.
, collapsed :: Maybe Bool -- ^ Set collapsed state.
, focus :: Bool -- ^ Set focus state.
, scroll :: Maybe Vec2 -- ^ Set scrolling value (use < 0.0f to not affect a given axis).
, bgAlpha :: Maybe Float -- ^ Set background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg, ChildBg, PopupBg. you may also use ImGuiWindowFlags_NoBackground.
, flags :: Raw.ImGuiWindowFlags -- ^ Set window flags.
, onClose :: Maybe (IO ()) -- ^ Allow window to be closed and set a callback.
}
instance HasField "name" Window Text where
getField Window{name0} = decodeUtf8Lenient (unsafeInit name0)
instance IsString Window where
fromString = pack . fromString
instance KnownSymbol label => IsLabel label Window where
fromLabel = base{flags = base.flags .|. Raw.ImGuiWindowFlags_NoTitleBar}
where
base = pack . fromString . ("##" ++) . symbolVal $ Proxy @label
pack :: Text -> Window
pack name = Window
{ name0 = encodeUtf8 $ name <> "\0"
, contentSize = Nothing
, sizeConstraints = Nothing
, collapsed = Nothing
, scroll = Nothing
, focus = False
, bgAlpha = Nothing
, flags = Raw.ImGuiWindowFlags_NoSavedSettings
, onClose = Nothing
}
{- | ImGui windows as Views
@
hstack_ -- Split-screen with equally-sized windows
[ Window.toView "Hi" $ ImGui.text "hi" -- title and default options
, Window.toView #AnonWithID $ ImGui.text "hi" -- empty title with a hidden ID (and default options)
, Window.toView example $ ImGui.text "hi" -- reusable window value
]
@
-}
toView :: Window -> m () -> View () (Windowed m)
toView config action = LV.view_ Windowed{..}
data Windowed m = Windowed
{ config :: Window
, action :: m ()
}
instance MonadUnliftIO m => LayoutView (Windowed m) where
type Placed (Windowed m) = m ()
placeView box Windowed{..} = do
setNextWindowBox box
let
flags =
config.flags .|.
Raw.ImGuiWindowFlags_NoMove .|.
Raw.ImGuiWindowFlags_NoResize .|.
Raw.ImGuiWindowFlags_NoCollapse
run (config{flags}) action
run :: MonadUnliftIO m => Window -> m () -> m ()
run window action = do
liftIO do
for_ window.contentSize \size ->
with size $ Raw.setNextWindowContentSize . castPtr
for_ (window.sizeConstraints <|> (Just (-1, -1))) \(minSize, maxSize) ->
with minSize \minPtr ->
with maxSize \maxPtr ->
Raw.setNextWindowSizeConstraints (castPtr minPtr) (castPtr maxPtr)
for_ window.collapsed $ \collapsed ->
Raw.setNextWindowCollapsed (bool 0 1 collapsed) Raw.ImGuiCond_Always
when window.focus Raw.setNextWindowFocus
for_ window.scroll \scroll ->
with scroll $ Raw.setNextWindowScroll . castPtr
for_ window.bgAlpha $
Raw.setNextWindowBgAlpha . CFloat
bracket
( liftIO do
open_ <- for window.onClose \callback -> (callback,) <$> new 1
unsafeUseAsCString window.name0 \namePtr ->
(open_,) <$> Raw.begin namePtr (Just $ maybe nullPtr snd open_) (Just window.flags)
)
( \(open_, _) -> liftIO do
case open_ of
Just (callback, openPtr) | openPtr /= nullPtr -> do
stillOpen <- peek openPtr
Raw.end
free openPtr
when (stillOpen == 0) callback
_ -> Raw.end
)
(\(_openPtr, visible) -> when visible action)
{-# INLINE setNextWindowBox #-}
setNextWindowBox :: MonadIO m => Box -> m ()
setNextWindowBox box = liftIO do
with box.position \pos ->
with (vec2 0.5 0.5) \pivot ->
Raw.setNextWindowPos (castPtr pos) Raw.ImGuiCond_Always (Just $ castPtr pivot)
with box.size \size ->
Raw.setNextWindowSize (castPtr size) Raw.ImGuiCond_Always