Commit 97e0c414 authored by Roman 3237465's avatar Roman 3237465

changed the events machinery

parent 7efd0ee1
......@@ -28,47 +28,75 @@ class EventListener : public QObject {
Q_OBJECT
public:
EventListener(CallbackPtrQObjectPtrQEventBool callback, int* deletedPtr) :
callback_(callback), deleted_(deletedPtr) {}
EventListener(
QObject *parent,
CallbackPtrQObjectPtrQEventBool eventCallback,
CallbackVoid deletedCallback) :
QObject(parent),
eventCallback_(eventCallback),
deletedCallback_(deletedCallback),
notifyDeleted_(true) {
parent->installEventFilter(this);
}
~EventListener() {
if (deleted_) {
*deleted_ = 1;
parent()->removeEventFilter(this);
if (notifyDeleted_) {
deletedCallback_();
}
}
virtual bool eventFilter(QObject* receiver, QEvent* event) {
return callback_(receiver, event);
return eventCallback_(receiver, event);
}
void doNotNotifyOnDelete() {
notifyDeleted_ = false;
}
private:
CallbackPtrQObjectPtrQEventBool callback_;
int* deleted_;
CallbackPtrQObjectPtrQEventBool eventCallback_;
CallbackVoid deletedCallback_;
bool notifyDeleted_;
};
class SceneEventListener : public QGraphicsItem {
public:
SceneEventListener(CallbackPtrQGraphicsItemPtrQEventBool callback, int* deletedPtr) :
callback_(callback), deleted_(deletedPtr) {}
SceneEventListener(
QGraphicsItem *parent,
CallbackPtrQGraphicsItemPtrQEventBool eventCallback,
CallbackVoid deletedCallback) :
QGraphicsItem(parent),
eventCallback_(eventCallback),
deletedCallback_(deletedCallback),
notifyDeleted_(true) {
parent->installSceneEventFilter(this);
}
~SceneEventListener() {
if (deleted_) {
*deleted_ = 1;
parentItem()->removeSceneEventFilter(this);
if (notifyDeleted_) {
deletedCallback_();
}
}
virtual bool eventFilter(QGraphicsItem* receiver, QEvent* event) {
return eventCallback_(receiver, event);
}
void doNotNotifyOnDelete() {
notifyDeleted_ = false;
}
virtual QRectF boundingRect() const { return QRectF(); }
virtual void paint(QPainter * painter, const QStyleOptionGraphicsItem * option,
QWidget * widget = 0) { return; }
virtual bool sceneEventFilter(QGraphicsItem* receiver, QEvent* event) {
return callback_(receiver, event);
}
private:
CallbackPtrQGraphicsItemPtrQEventBool callback_;
int* deleted_;
CallbackPtrQGraphicsItemPtrQEventBool eventCallback_;
CallbackVoid deletedCallback_;
bool notifyDeleted_;
};
} // namespace event
......
......@@ -37,7 +37,6 @@ import Foreign.Hoppy.Generator.Spec.ClassFeature (
)
import Foreign.Hoppy.Generator.Types (voidT)
import Foreign.Hoppy.Generator.Version (collect, just)
-- import Graphics.UI.Qtah.Generator.Flags (qtVersion)
import Graphics.UI.Qtah.Generator.Interface.Core.Types (qreal)
import Graphics.UI.Qtah.Generator.Module (AModule (AQtModule), makeQtModule)
import Graphics.UI.Qtah.Generator.Types
......
......@@ -27,10 +27,11 @@ import Foreign.Hoppy.Generator.Spec (
includeLocal,
makeClass,
mkCtor,
mkMethod,
)
import Foreign.Hoppy.Generator.Types (callbackT, intT, ptrT)
import Foreign.Hoppy.Generator.Types (callbackT, objT, ptrT, voidT)
import Graphics.UI.Qtah.Generator.Interface.Core.QObject (c_QObject)
import Graphics.UI.Qtah.Generator.Interface.Internal.Callback (cb_PtrQObjectPtrQEventBool)
import Graphics.UI.Qtah.Generator.Interface.Internal.Callback (cb_PtrQObjectPtrQEventBool, cb_Void)
import Graphics.UI.Qtah.Generator.Module (AModule (AQtModule), makeQtModule)
import Graphics.UI.Qtah.Generator.Types
......@@ -45,5 +46,6 @@ c_EventListener =
addReqIncludes [includeLocal "event.hpp"] $
classSetEntityPrefix "" $
makeClass (ident2 "qtah" "event" "EventListener") Nothing [c_QObject]
[ mkCtor "new" [callbackT cb_PtrQObjectPtrQEventBool, ptrT intT]
[ mkCtor "new" [ptrT $ objT c_QObject, callbackT cb_PtrQObjectPtrQEventBool, callbackT cb_Void]
, mkMethod "doNotNotifyOnDelete" [] voidT
]
......@@ -27,10 +27,12 @@ import Foreign.Hoppy.Generator.Spec (
includeLocal,
makeClass,
mkCtor,
mkMethod,
)
import Foreign.Hoppy.Generator.Types (callbackT, intT, ptrT)
import Foreign.Hoppy.Generator.Types (callbackT, objT, ptrT, voidT)
import Graphics.UI.Qtah.Generator.Interface.Internal.Callback
(cb_PtrQGraphicsItemPtrQEventBool, cb_Void)
import Graphics.UI.Qtah.Generator.Interface.Widgets.QGraphicsItem (c_QGraphicsItem)
import Graphics.UI.Qtah.Generator.Interface.Internal.Callback (cb_PtrQGraphicsItemPtrQEventBool)
import Graphics.UI.Qtah.Generator.Module (AModule (AQtModule), makeQtModule)
import Graphics.UI.Qtah.Generator.Types
......@@ -45,5 +47,7 @@ c_SceneEventListener =
addReqIncludes [includeLocal "event.hpp"] $
classSetEntityPrefix "" $
makeClass (ident2 "qtah" "event" "SceneEventListener") Nothing [c_QGraphicsItem]
[ mkCtor "new" [callbackT cb_PtrQGraphicsItemPtrQEventBool, ptrT intT]
[ mkCtor "new"
[ptrT $ objT c_QGraphicsItem, callbackT cb_PtrQGraphicsItemPtrQEventBool, callbackT cb_Void]
, mkMethod "doNotNotifyOnDelete" [] voidT
]
......@@ -17,6 +17,26 @@
{-# LANGUAGE ExistentialQuantification #-}
-- | General routines for managing 'QEvent's.
-- This file is part of Qtah.
--
-- Copyright 2016-2017 Bryan Gardiner <bog@khumba.net>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
{-# LANGUAGE ExistentialQuantification #-}
-- | General routines for managing 'QEvent's.
module Graphics.UI.Qtah.Event (
-- * High-level interface.
......@@ -27,24 +47,21 @@ module Graphics.UI.Qtah.Event (
EventFilter,
onAnyEvent,
-- * Internal
internalOnEvent,
internalRegistrationIsLive,
) where
import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar)
import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar, readMVar)
import Control.Monad (when)
import Foreign.C.Types (CInt)
import Foreign.Hoppy.Runtime (delete)
import Foreign.Ptr (Ptr, nullPtr)
import Graphics.UI.Qtah.SceneEvent (SceneEvent)
import Graphics.UI.Qtah.Core.QObject (QObject, QObjectPtr)
-- Importing QObject in both ways is a silly, but it prevents unused import
-- warnings from having a doc link to installEventFilter.
import Graphics.UI.Qtah.Core.QObject (QObjectPtr)
import qualified Graphics.UI.Qtah.Core.QObject as QObject
import Graphics.UI.Qtah.SceneEvent (SceneEvent)
-- Note, Generated import, since the non-Generated import imports this module.
import Graphics.UI.Qtah.Generated.Core.QEvent (QEvent)
import Graphics.UI.Qtah.Internal.EventListener (EventListener)
import qualified Graphics.UI.Qtah.Internal.EventListener as EventListener
import Graphics.UI.Qtah.Signal (connect)
data Receiver = forall a. QObjectPtr a => Receiver a
-- | A typeclass for Qt event classes (subclasses of @QEvent@).
class SceneEvent e => Event e where
......@@ -57,15 +74,14 @@ class SceneEvent e => Event e where
-- | A record that an event handler was registered with a receiver object. This
-- can be given to 'unregister' to destroy the corresponding handler.
data EventRegistration = EventRegistration
{ regReceiver :: Receiver
, regListener :: EventListener
, regActive :: MVar Bool
{ regListener :: EventListener
, regLive :: MVar Bool
}
-- | An filter that can handle any type of event.
type EventFilter = QObject -> QEvent -> IO Bool
type EventFilter = QObject.QObject -> QEvent -> IO Bool
-- | Registers an 'EventFilter' to listen to events that a 'QObject' receives.
-- | Registers an 'EventFilter' to listen to events that a 'QObject.QObject' receives.
-- A filter can return false to allow the event to propagate further, or true to
-- indicate that the event has been handled, and stop propagation. When
-- multiple filters are attached to an object, the last one installed is called
......@@ -74,33 +90,25 @@ type EventFilter = QObject -> QEvent -> IO Bool
--
-- This function uses 'QObject.installEventFilter' under the hood.
onAnyEvent :: QObjectPtr target => target -> EventFilter -> IO EventRegistration
onAnyEvent receiver filter = internalOnEvent receiver nullPtr filter
-- | Internal function, do not use outside of Qtah.
--
-- Implements 'onAnyEvent'. Also takes a pointer to an @int@ that is passed to
-- the underlying 'EventListener.EventListener' object to be set to 1 when the
-- listener is deleted. This is used for testing purposes.
internalOnEvent :: QObjectPtr target => target -> Ptr CInt -> EventFilter -> IO EventRegistration
internalOnEvent receiver deletedPtr filter = do
listener <- EventListener.new filter deletedPtr
activeVar <- newMVar True
let reg = EventRegistration
{ regReceiver = Receiver receiver
, regListener = listener
, regActive = activeVar
}
QObject.installEventFilter receiver listener
_ <- connect receiver QObject.destroyedSignal $ \_ -> unregister reg
return reg
onAnyEvent receiver filter = do
liveVar <- newMVar True
listener <- EventListener.new receiver filter $ modifyMVar_ liveVar $ const $ return False
return $ EventRegistration
{ regListener = listener
, regLive = liveVar
}
-- | Disconnects an event handler and frees its resources. This function is
-- idempotent.
unregister :: EventRegistration -> IO ()
unregister reg = modifyMVar_ (regActive reg) $ \active -> do
when active $ do
unregister reg = modifyMVar_ (regLive reg) $ \live -> do
when live $ do
let listener = regListener reg
case regReceiver reg of
Receiver receiver -> QObject.removeEventFilter receiver listener
-- The on-delete callback tries to modify regLive too, so skip it to avoid
-- deadlock, because we already know the listener is being deleted.
EventListener.doNotNotifyOnDelete listener
delete listener
return False
internalRegistrationIsLive :: EventRegistration -> IO Bool
internalRegistrationIsLive = readMVar . regLive
......@@ -17,35 +17,49 @@
{-# LANGUAGE ExistentialQuantification #-}
-- | General routines for managing 'QEvent's.
-- This file is part of Qtah.
--
-- Copyright 2016-2017 Bryan Gardiner <bog@khumba.net>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
{-# LANGUAGE ExistentialQuantification #-}
-- | General routines for managing 'QEvent's.
module Graphics.UI.Qtah.SceneEvent (
-- * High-level interface.
SceneEvent (..),
SceneEventRegistration (..),
SceneEventRegistration,
unregister,
-- * Low-level interface
SceneEventFilter,
onAnySceneEvent,
-- * Internal
internalOnSceneEvent,
internalRegistrationIsLive,
) where
import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar)
import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar, readMVar)
import Control.Monad (when)
import Foreign.C.Types (CInt)
import Foreign.Hoppy.Runtime (delete)
import Foreign.Ptr (Ptr, nullPtr)
-- import Graphics.UI.Qtah.Core.QObject (QObject, QObjectPtr)
import Graphics.UI.Qtah.Generated.Widgets.QGraphicsScene (addItem)
import Graphics.UI.Qtah.Generated.Widgets.QGraphicsItem
import Graphics.UI.Qtah.Widgets.QGraphicsScene (addItem)
import Graphics.UI.Qtah.Widgets.QGraphicsItem
(QGraphicsItem, QGraphicsItemPtr, scene, installSceneEventFilter, removeSceneEventFilter)
-- Note, Generated import, since the non-Generated import imports this module.
import Graphics.UI.Qtah.Generated.Core.QEvent (QEvent)
import Graphics.UI.Qtah.Internal.SceneEventListener (SceneEventListener)
import qualified Graphics.UI.Qtah.Internal.SceneEventListener as SceneEventListener
-- import Graphics.UI.Qtah.Signal (connect)
data Receiver = forall a. QGraphicsItemPtr a => Receiver a
-- | A typeclass for Qt event classes (subclasses of @QEvent@).
class SceneEvent e where
......@@ -58,9 +72,8 @@ class SceneEvent e where
-- | A record that an event handler was registered with a receiver object. This
-- can be given to 'unregister' to destroy the corresponding handler.
data SceneEventRegistration = SceneEventRegistration
{ regReceiver :: Receiver
, regListener :: SceneEventListener
, regActive :: MVar Bool
{ regListener :: SceneEventListener
, regLive :: MVar Bool
}
-- | An filter that can handle any type of event.
......@@ -76,36 +89,25 @@ type SceneEventFilter = QGraphicsItem -> QEvent -> IO Bool
-- This function uses 'QGraphicsItem.installSceneEventFilter' under the hood.
onAnySceneEvent :: QGraphicsItemPtr target =>
target -> SceneEventFilter -> IO SceneEventRegistration
onAnySceneEvent receiver filter = internalOnSceneEvent receiver nullPtr filter
-- | Internal function, do not use outside of Qtah.
--
-- Implements 'onAnySceneEvent'. Also takes a pointer to an @int@ that is passed to
-- the underlying 'SceneEventListener.SceneEventListener' object to be set to 1 when the
-- listener is deleted. This is used for testing purposes.
internalOnSceneEvent :: QGraphicsItemPtr target =>
target -> Ptr CInt -> SceneEventFilter -> IO SceneEventRegistration
internalOnSceneEvent receiver deletedPtr filter = do
listener <- SceneEventListener.new filter deletedPtr
activeVar <- newMVar True
let reg = SceneEventRegistration
{ regReceiver = Receiver receiver
, regListener = listener
, regActive = activeVar
}
-- 'addItem' is due to the fact that 'listener' must be on the same scene as 'receiver'.
scene receiver >>= flip addItem listener
installSceneEventFilter receiver listener
-- _ <- connect receiver QGraphicsItem.destroyedSignal $ \_ -> unregister reg
return reg
onAnySceneEvent receiver filter = do
liveVar <- newMVar True
listener <- SceneEventListener.new receiver filter $ modifyMVar_ liveVar $ const $ return False
return $ SceneEventRegistration
{ regListener = listener
, regLive = liveVar
}
-- | Disconnects an event handler and frees its resources. This function is
-- idempotent.
unregister :: SceneEventRegistration -> IO ()
unregister reg = modifyMVar_ (regActive reg) $ \active -> do
when active $ do
unregister reg = modifyMVar_ (regLive reg) $ \live -> do
when live $ do
let listener = regListener reg
case regReceiver reg of
Receiver receiver -> removeSceneEventFilter receiver listener
-- The on-delete callback tries to modify regLive too, so skip it to avoid
-- deadlock, because we already know the listener is being deleted.
SceneEventListener.doNotNotifyOnDelete listener
delete listener
return False
internalRegistrationIsLive :: SceneEventRegistration -> IO Bool
internalRegistrationIsLive = readMVar . regLive
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment