Skip to content

Commit 3526e6f

Browse files
authored
Fix and extend DragDrop API (#209)
1 parent bc6406d commit 3526e6f

File tree

4 files changed

+180
-17
lines changed

4 files changed

+180
-17
lines changed

ChangeLog.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog for dear-imgui
22

3+
## [2.3.1]
4+
5+
- Extended DragDrop API.
6+
* Added `DearImGui.withDragDropSource` and `DearImGui.withDragDropTarget` wrappers.
7+
* Fixed `DearImGui.Raw.DragDrop.beginTarget` to return accept flag.
8+
* Added `DearImGui.Raw.DragDrop.getData` and `DearImGui.Raw.DragDrop.getDataSize`.
9+
* Added remaining `Payload` internals.
10+
311
## [2.3.0]
412

513
- `imgui` updated to [1.90.9].
@@ -138,6 +146,7 @@ Initial Hackage release based on [1.83].
138146
[2.2.0]: https://github.com/haskell-game/dear-imgui.hs/tree/v2.2.0
139147
[2.2.1]: https://github.com/haskell-game/dear-imgui.hs/tree/v2.2.1
140148
[2.3.0]: https://github.com/haskell-game/dear-imgui.hs/tree/v2.3.0
149+
[2.3.1]: https://github.com/haskell-game/dear-imgui.hs/tree/v2.3.1
141150

142151
[1.90.9]: https://github.com/ocornut/imgui/releases/tag/v1.90.9
143152
[1.89.9]: https://github.com/ocornut/imgui/releases/tag/v1.89.9

dear-imgui.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cabal-version: 3.0
22

33
name: dear-imgui
4-
version: 2.3.0
4+
version: 2.3.1
55
author: Oliver Charles
66
77
license: BSD-3-Clause

src/DearImGui.hs

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,14 @@ module DearImGui
421421
, Raw.setKeyboardFocusHere
422422
, Raw.setNextItemAllowOverlap
423423

424+
-- ** Drag and drop
425+
, withDragDropSource
426+
, withDragDropTarget
427+
, withDragDropSource_
428+
, withDragDropTarget_
429+
, withDragDropSourceData
430+
, withDragDropTargetData
431+
424432
-- ** ListClipper
425433
, withListClipper
426434
, ClipItems(..)
@@ -446,7 +454,7 @@ import Control.Monad
446454
( when )
447455
import Data.Bool
448456
import Data.Foldable
449-
( foldl' )
457+
( foldl', for_, traverse_ )
450458
import Foreign
451459
import Foreign.C
452460

@@ -456,6 +464,7 @@ import DearImGui.Internal.Text (Text)
456464
import DearImGui.Structs
457465
import qualified DearImGui.Internal.Text as Text
458466
import qualified DearImGui.Raw as Raw
467+
import qualified DearImGui.Raw.DragDrop as Raw.DragDrop
459468
import qualified DearImGui.Raw.Font as Raw.Font
460469
import qualified DearImGui.Raw.ListClipper as Raw.ListClipper
461470

@@ -471,7 +480,7 @@ import Control.Monad.IO.Class
471480
( MonadIO, liftIO )
472481

473482
-- unliftio
474-
import UnliftIO (MonadUnliftIO)
483+
import UnliftIO (MonadUnliftIO (..))
475484
import UnliftIO.Exception (bracket, bracket_)
476485

477486
-- vector
@@ -2609,6 +2618,86 @@ popStyleVar n = liftIO do
26092618
withFont :: MonadUnliftIO m => Raw.Font.Font -> m a -> m a
26102619
withFont font = bracket_ (Raw.Font.pushFont font) Raw.Font.popFont
26112620

2621+
-- | Attach drag-n-drop source with a payload to a preceding item.
2622+
--
2623+
-- A valid target should have a matching payload type.
2624+
--
2625+
-- Data is copied and retained by DearImGui.
2626+
-- Action is executed when the payload is accepted.
2627+
withDragDropSource :: (MonadUnliftIO m, Storable a) => ImGuiDragDropFlags -> Text -> a -> (Bool -> m ()) -> m ()
2628+
withDragDropSource flags payloadType payload action =
2629+
withRunInIO \run ->
2630+
with payload \payloadPtr ->
2631+
run $ withDragDropSourceData flags payloadType (castPtr payloadPtr, Foreign.sizeOf payload) action
2632+
2633+
-- | Attach drag-n-drop target to a preceding item.
2634+
--
2635+
-- A valid source should have a matching payload type.
2636+
--
2637+
-- Data is fetched from DearImGui copy and cleared on delivery.
2638+
-- Action is executed when the payload is accepted and not empty.
2639+
withDragDropTarget :: (MonadUnliftIO m, Storable a) => ImGuiDragDropFlags -> Text -> (a -> m ()) -> m ()
2640+
withDragDropTarget flags payloadType action =
2641+
withRunInIO \run ->
2642+
Raw.DragDrop.beginTarget >>= flip when do
2643+
Text.withCString payloadType \typePtr -> do
2644+
payload_ <- Raw.DragDrop.acceptPayload typePtr flags
2645+
for_ payload_ \payload -> do
2646+
dataPtr <- Raw.DragDrop.getData payload
2647+
Foreign.maybePeek peek (castPtr dataPtr) >>= traverse_ (run . action)
2648+
Raw.DragDrop.endTarget
2649+
2650+
-- | Like 'withDragDropSource', but only set payload type.
2651+
withDragDropSource_ :: (MonadUnliftIO m) => ImGuiDragDropFlags -> Text -> (Bool -> m ()) -> m ()
2652+
withDragDropSource_ flags payloadType action =
2653+
withRunInIO \run ->
2654+
Raw.DragDrop.beginSource flags >>= flip when do
2655+
accepted <-
2656+
Text.withCString payloadType \typePtr ->
2657+
Raw.DragDrop.setPayload typePtr nullPtr 0 ImGuiCond_Once
2658+
run $ action accepted
2659+
Raw.DragDrop.endSource
2660+
2661+
-- | Like 'withDragDropTarget', but only set payload type.
2662+
--
2663+
-- Payload data is ignored.
2664+
withDragDropTarget_ :: (MonadUnliftIO m) => ImGuiDragDropFlags -> Text -> m () -> m ()
2665+
withDragDropTarget_ flags payloadType action =
2666+
withRunInIO \run ->
2667+
Raw.DragDrop.beginTarget >>= flip when do
2668+
Text.withCString payloadType \typePtr -> do
2669+
payload_ <- Raw.DragDrop.acceptPayload typePtr flags
2670+
for_ payload_ (\_dataPtr -> run action)
2671+
Raw.DragDrop.endTarget
2672+
2673+
-- | Like 'withDragDropSource', explicitly setting data ptr and size.
2674+
--
2675+
-- Suitable for data with dynamic lengths via @withCStringLen@-like functions.
2676+
withDragDropSourceData :: (MonadUnliftIO m, Integral len) => ImGuiDragDropFlags -> Text -> (Ptr a, len) -> (Bool -> m ()) -> m ()
2677+
withDragDropSourceData flags payloadType (dataPtr, dataSize) action =
2678+
withRunInIO \run ->
2679+
Raw.DragDrop.beginSource flags >>= flip when do
2680+
accepted <-
2681+
Text.withCString payloadType \typePtr ->
2682+
Raw.DragDrop.setPayload typePtr dataPtr (fromIntegral dataSize) ImGuiCond_Once
2683+
run $ action accepted
2684+
Raw.DragDrop.endSource
2685+
2686+
-- | Like 'withDragDropTarget', getting raw data ptr and size.
2687+
--
2688+
-- Check the size, and pointer for NULLs etc.!
2689+
withDragDropTargetData :: (MonadUnliftIO m, Integral len) => ImGuiDragDropFlags -> Text -> ((Ptr a, len) -> m ()) -> m ()
2690+
withDragDropTargetData flags payloadType action =
2691+
withRunInIO \run ->
2692+
Raw.DragDrop.beginTarget >>= flip when do
2693+
Text.withCString payloadType \typePtr -> do
2694+
payload_ <- Raw.DragDrop.acceptPayload typePtr flags
2695+
for_ payload_ \payload -> do
2696+
dataPtr <- Raw.DragDrop.getData payload
2697+
dataSize <- Raw.DragDrop.getDataSize payload
2698+
run $ action (castPtr dataPtr, fromIntegral dataSize)
2699+
Raw.DragDrop.endTarget
2700+
26122701
-- | Clips a large list of items
26132702
--
26142703
-- The requirements on @a@ are that they are all of the same height.

src/DearImGui/Raw/DragDrop.hs

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,32 @@
1010
{-# LANGUAGE ViewPatterns #-}
1111

1212
module DearImGui.Raw.DragDrop
13-
( Payload(..)
14-
, beginSource
13+
( -- * Source
14+
beginSource
1515
, setPayload
1616
, endSource
17+
-- * Target
1718
, beginTarget
1819
, acceptPayload
1920
, endTarget
21+
-- * Payload object
22+
, Payload(..)
23+
, getData
24+
, getDataSize
25+
-- ** Direct access
2026
, getPayload
27+
, clear
28+
, isDataType
29+
, isPreview
30+
, isDelivery
2131
)
2232
where
2333

2434
-- base
2535
import Control.Monad.IO.Class
2636
( MonadIO, liftIO )
2737
import Foreign
28-
( Ptr, castPtr )
38+
( Ptr, castPtr, nullPtr )
2939
import Foreign.C
3040

3141
-- dear-imgui
@@ -44,38 +54,93 @@ C.context (Cpp.cppCtx <> C.bsCtx <> imguiContext)
4454
C.include "imgui.h"
4555
Cpp.using "namespace ImGui"
4656

47-
-- | Font configuration data handle
57+
-- | Call after submitting an item which may be dragged.
4858
--
49-
-- Wraps @ImGuiPayload*@.
50-
newtype Payload = Payload (Ptr ImGuiPayload)
51-
52-
59+
-- When this return True, you can call 'setPayload' + 'endDragDropSource'.
5360
beginSource :: MonadIO m => ImGuiDragDropFlags -> m Bool
5461
beginSource flags = liftIO do
5562
(0 /=) <$> [C.exp| bool { BeginDragDropSource( $(ImGuiDragDropFlags flags) ) } |]
5663

64+
-- | Type is a user defined string of maximum 32 characters.
65+
--
66+
-- Strings starting with '_' are reserved for dear imgui internal types.
67+
-- Data is copied and held by imgui.
68+
-- Returns True when payload has been accepted.
5769
setPayload :: MonadIO m => CString -> Ptr a -> CSize -> ImGuiCond -> m Bool
5870
setPayload typePtr dataPtr sz cond = liftIO do
5971
(0 /=) <$> [C.exp| bool { SetDragDropPayload( $(char* typePtr), $(void* dataPtr'), $(size_t sz), $(ImGuiCond cond) ) } |]
6072
where
6173
dataPtr' = castPtr dataPtr
6274

75+
-- | Only call if 'beginSource' returns True!
6376
endSource :: MonadIO m => m ()
6477
endSource = liftIO do
6578
[C.block| void { EndDragDropSource( ); } |]
6679

67-
beginTarget :: MonadIO m => m ()
80+
-- | Call after submitting an item that may receive a payload.
81+
--
82+
-- If this returns True, you can call 'acceptPayload' + 'endTarget'.
83+
beginTarget :: MonadIO m => m Bool
6884
beginTarget = liftIO do
69-
[C.block| void { BeginDragDropTarget(); } |]
85+
(0 /=) <$> [C.exp| bool { BeginDragDropTarget() } |]
7086

71-
acceptPayload :: MonadIO m => CString -> ImGuiDragDropFlags -> m Payload
87+
-- | Accept contents of a given type.
88+
--
89+
-- If "ImGuiDragDropFlags_AcceptBeforeDelivery" is set you can peek into the payload before the mouse button is released.
90+
acceptPayload :: MonadIO m => CString -> ImGuiDragDropFlags -> m (Maybe Payload)
7291
acceptPayload typePtr flags = liftIO do
73-
Payload <$> [C.exp| const ImGuiPayload* { AcceptDragDropPayload( $(char* typePtr), $(ImGuiDragDropFlags flags) ) } |]
92+
ptr <- [C.exp| const ImGuiPayload* { AcceptDragDropPayload( $(char* typePtr), $(ImGuiDragDropFlags flags) ) } |]
93+
if ptr == nullPtr then
94+
pure Nothing
95+
else
96+
pure $ Just (Payload ptr)
7497

98+
-- | Only call if 'beginTarget' returns true!
7599
endTarget :: MonadIO m => m ()
76100
endTarget = liftIO do
77101
[C.block| void { EndDragDropTarget(); } |]
78102

79-
getPayload :: MonadIO m => m Payload
103+
-- | Peek directly into the current payload from anywhere.
104+
--
105+
-- Returns NULL when drag and drop is finished or inactive.
106+
-- Use 'isDataType' to test for the payload type.
107+
getPayload :: MonadIO m => m (Maybe Payload)
80108
getPayload = liftIO do
81-
Payload <$> [C.exp| const ImGuiPayload* { GetDragDropPayload() } |]
109+
ptr <- [C.exp| const ImGuiPayload* { GetDragDropPayload() } |]
110+
if ptr == nullPtr then
111+
pure Nothing
112+
else
113+
pure $ Just (Payload ptr)
114+
115+
-- | DragDrop payload data handle
116+
--
117+
-- Wraps @ImGuiPayload*@.
118+
newtype Payload = Payload (Ptr ImGuiPayload)
119+
deriving (Eq, Show)
120+
121+
getData :: MonadIO m => Payload -> m (Ptr ())
122+
getData (Payload payloadPtr) = liftIO do
123+
[C.exp| void* { $(ImGuiPayload* payloadPtr)->Data } |]
124+
125+
getDataSize :: MonadIO m => Payload -> m CInt
126+
getDataSize (Payload payloadPtr) = liftIO do
127+
[C.exp| int { $(ImGuiPayload* payloadPtr)->DataSize } |]
128+
129+
-- | Clear the DearImGui copy of payload data.
130+
--
131+
-- Gets called on 'endTarget' right after delivery.
132+
clear :: MonadIO m => Payload -> m ()
133+
clear (Payload payloadPtr) = liftIO do
134+
[C.block| void { $(ImGuiPayload* payloadPtr)->Clear(); } |]
135+
136+
isDataType :: MonadIO m => Payload -> CString -> m Bool
137+
isDataType (Payload payloadPtr) typePtr = liftIO do
138+
(0 /=) <$> [C.exp| bool { $(ImGuiPayload* payloadPtr)->IsDataType($(char* typePtr)) } |]
139+
140+
isPreview :: MonadIO m => Payload -> m Bool
141+
isPreview (Payload payloadPtr) = liftIO do
142+
(0 /=) <$> [C.exp| bool { $(ImGuiPayload* payloadPtr)->IsPreview() } |]
143+
144+
isDelivery :: MonadIO m => Payload -> m Bool
145+
isDelivery (Payload payloadPtr) = liftIO do
146+
(0 /=) <$> [C.exp| bool { $(ImGuiPayload* payloadPtr)->IsDelivery() } |]

0 commit comments

Comments
 (0)