qtah-examples 0.1.0 → 0.1.1
raw patch · 2 files changed
+147/−42 lines, 2 filesdep +filepath
Dependencies added: filepath
Files
- qtah-examples.cabal +2/−1
- src/Graphics/UI/Qtah/Example/Notepad.hs +145/−41
qtah-examples.cabal view
@@ -1,5 +1,5 @@ name: qtah-examples-version: 0.1.0+version: 0.1.1 synopsis: Example programs for Qtah Qt bindings homepage: http://khumba.net/projects/qtah license: LGPL-3@@ -23,6 +23,7 @@ base >=4 && <5 , binary >=0.7 && <0.8 , bytestring >=0.10 && <0.11+ , filepath >=1.0 && <1.5 , hoppy-runtime >=0.2 && <0.3 , qtah-qt5 >=0.1 && <0.2 ghc-options: -W -fwarn-incomplete-patterns -fwarn-unused-do-bind
src/Graphics/UI/Qtah/Example/Notepad.hs view
@@ -17,34 +17,45 @@ {-# LANGUAGE ScopedTypeVariables #-} --- | The Qt notepad example.+-- | A notepad based on the Qt notepad example. module Graphics.UI.Qtah.Example.Notepad (run) where -import Control.Monad (unless)+import Control.Monad (forM_, unless, when)+import Data.Bits ((.|.))+import Data.Functor (void)+import Data.IORef (IORef, newIORef, readIORef, writeIORef) import Foreign.Hoppy.Runtime (withScopedPtr) import qualified Graphics.UI.Qtah.Core.QCoreApplication as QCoreApplication import Graphics.UI.Qtah.Event import Graphics.UI.Qtah.Gui.QCloseEvent (QCloseEvent) import Graphics.UI.Qtah.Signal (connect_)-import Graphics.UI.Qtah.Widgets.QAbstractButton (clickedSignal)+import qualified Graphics.UI.Qtah.Core.QEvent as QEvent+import qualified Graphics.UI.Qtah.Widgets.QAction as QAction import Graphics.UI.Qtah.Widgets.QAction (triggeredSignal) import qualified Graphics.UI.Qtah.Widgets.QApplication as QApplication import qualified Graphics.UI.Qtah.Widgets.QFileDialog as QFileDialog-import qualified Graphics.UI.Qtah.Widgets.QLayout as QLayout import qualified Graphics.UI.Qtah.Widgets.QMainWindow as QMainWindow import Graphics.UI.Qtah.Widgets.QMainWindow (QMainWindow) import qualified Graphics.UI.Qtah.Widgets.QMenu as QMenu import qualified Graphics.UI.Qtah.Widgets.QMenuBar as QMenuBar-import qualified Graphics.UI.Qtah.Widgets.QPushButton as QPushButton+import qualified Graphics.UI.Qtah.Widgets.QMessageBox as QMessageBox import qualified Graphics.UI.Qtah.Widgets.QTextEdit as QTextEdit-import Graphics.UI.Qtah.Widgets.QTextEdit (QTextEdit)-import qualified Graphics.UI.Qtah.Widgets.QVBoxLayout as QVBoxLayout+import Graphics.UI.Qtah.Widgets.QTextEdit (+ QTextEdit,+ copyAvailableSignal,+ redoAvailableSignal,+ textChangedSignal,+ undoAvailableSignal,+ ) import qualified Graphics.UI.Qtah.Widgets.QWidget as QWidget import System.Environment (getArgs)+import System.FilePath (takeFileName) data Notepad = Notepad { myWindow :: QMainWindow , myText :: QTextEdit+ , myFilePathRef :: IORef (Maybe FilePath)+ , myDirtyRef :: IORef Bool } run :: IO ()@@ -58,64 +69,157 @@ window <- QMainWindow.new menu <- QMenuBar.new+ QMainWindow.setMenuBar window menu+ menuFile <- QMenuBar.addNewMenu menu "&File" menuFileNew <- QMenu.addNewAction menuFile "&New"- menuFileOpen <- QMenu.addNewAction menuFile "&Open"+ menuFileOpen <- QMenu.addNewAction menuFile "&Open..." menuFileSave <- QMenu.addNewAction menuFile "&Save"- QMainWindow.setMenuBar window menu+ menuFileSaveAs <- QMenu.addNewAction menuFile "Sa&ve as..."+ _ <- QMenu.addSeparator menuFile+ menuFileQuit <- QMenu.addNewAction menuFile "&Quit" - contents <- QWidget.new- layout <- QVBoxLayout.new- QWidget.setLayout contents layout+ menuEdit <- QMenuBar.addNewMenu menu "&Edit"+ menuEditUndo <- QMenu.addNewAction menuEdit "&Undo"+ menuEditRedo <- QMenu.addNewAction menuEdit "&Redo"+ _ <- QMenu.addSeparator menuEdit+ menuEditCut <- QMenu.addNewAction menuEdit "Cu&t"+ menuEditCopy <- QMenu.addNewAction menuEdit "&Copy"+ menuEditPaste <- QMenu.addNewAction menuEdit "&Paste"+ _ <- QMenu.addSeparator menuEdit+ menuEditSelectAll <- QMenu.addNewAction menuEdit "Select all"+ forM_ [menuEditUndo, menuEditRedo, menuEditCut, menuEditCopy] $ \action ->+ QAction.setEnabled action False text <- QTextEdit.new- quitButton <- QPushButton.newWithText "Quit"- QLayout.addWidget layout text- QLayout.addWidget layout quitButton- QMainWindow.setCentralWidget window contents+ QMainWindow.setCentralWidget window text+ QTextEdit.setUndoRedoEnabled text True + filePathRef <- newIORef Nothing+ dirtyRef <- newIORef False+ let me = Notepad { myWindow = window , myText = text+ , myFilePathRef = filePathRef+ , myDirtyRef = dirtyRef } - _ <- onEvent window $ \(_ :: QCloseEvent) -> do- putStrLn "Goodbye!"- return False+ _ <- onEvent window $ \(event :: QCloseEvent) -> do+ continue <- confirmSaveIfDirty me "Quit"+ unless continue $ QEvent.ignore event+ return $ not continue connect_ menuFileNew triggeredSignal $ \_ -> fileNew me connect_ menuFileOpen triggeredSignal $ \_ -> fileOpen me- connect_ menuFileSave triggeredSignal $ \_ -> fileSave me- connect_ quitButton clickedSignal $ \_ -> QWidget.close window+ connect_ menuFileSave triggeredSignal $ \_ -> void $ fileSave me+ connect_ menuFileSaveAs triggeredSignal $ \_ -> void $ fileSaveAs me+ connect_ menuFileQuit triggeredSignal $ \_ -> QWidget.close window + connect_ menuEditUndo triggeredSignal $ \_ -> QTextEdit.undo text+ connect_ menuEditRedo triggeredSignal $ \_ -> QTextEdit.redo text+ connect_ menuEditCut triggeredSignal $ \_ -> QTextEdit.cut text+ connect_ menuEditCopy triggeredSignal $ \_ -> QTextEdit.copy text+ connect_ menuEditPaste triggeredSignal $ \_ -> QTextEdit.paste text+ connect_ menuEditSelectAll triggeredSignal $ \_ -> QTextEdit.selectAll text++ connect_ text textChangedSignal $ setDirty me True+ connect_ text undoAvailableSignal $ \b -> QAction.setEnabled menuEditUndo b+ connect_ text redoAvailableSignal $ \b -> QAction.setEnabled menuEditRedo b+ connect_ text copyAvailableSignal $ \b -> do+ QAction.setEnabled menuEditCut b+ QAction.setEnabled menuEditCopy b++ updateTitle me return window fileNew :: Notepad -> IO ()-fileNew me = QTextEdit.setText (myText me) ""+fileNew me = do+ continue <- confirmSaveIfDirty me "New file"+ when continue $ do+ QTextEdit.clear $ myText me+ setFilePath me Nothing+ setDirty me False fileOpen :: Notepad -> IO () fileOpen me = do- fileName <- QFileDialog.getOpenFileName- (myWindow me)- "Open File"- ""- fileDialogFilter-- unless (null fileName) $ do- contents <- readFile fileName- QTextEdit.setText (myText me) contents+ continue <- confirmSaveIfDirty me "Open file"+ when continue $ do+ path <- QFileDialog.getOpenFileName (myWindow me) "Open file" "" fileDialogFilter+ unless (null path) $ do+ contents <- readFile path+ QTextEdit.setText (myText me) contents+ setFilePath me $ Just path+ setDirty me False -fileSave :: Notepad -> IO ()+-- | Returns true if the save was performed.+fileSave :: Notepad -> IO Bool fileSave me = do- fileName <- QFileDialog.getSaveFileName- (myWindow me)- "Save File"- ""- fileDialogFilter+ pathMaybe <- readIORef $ myFilePathRef me+ case pathMaybe of+ Nothing -> fileSaveAs me+ Just path -> do+ contents <- QTextEdit.toPlainText $ myText me+ writeFile path contents+ setDirty me False+ return True - unless (null fileName) $ do- contents <- QTextEdit.toPlainText $ myText me- writeFile fileName contents+-- | Returns true if the save was performed.+fileSaveAs :: Notepad -> IO Bool+fileSaveAs me = do+ path <- QFileDialog.getSaveFileName+ (myWindow me)+ "Save file"+ ""+ fileDialogFilter + if null path+ then return False+ else do setFilePath me $ Just path+ fileSave me++-- | Returns true if the surrounding action should continue: that is, if the+-- editor was not dirty, or if the editor was dirty and the save was performed.+confirmSaveIfDirty :: Notepad -> String -> IO Bool+confirmSaveIfDirty me title = do+ let dirtyRef = myDirtyRef me+ dirty <- readIORef dirtyRef+ if dirty+ then do response <- QMessageBox.questionWithButtons+ (myWindow me)+ title+ "There are unsaved changes. Would you like to save them?"+ (QMessageBox.qMessageBoxStandardButtons_Yes .|.+ QMessageBox.qMessageBoxStandardButtons_No .|.+ QMessageBox.qMessageBoxStandardButtons_Cancel)+ QMessageBox.QMessageBoxStandardButton_Cancel+ case response of+ QMessageBox.QMessageBoxStandardButton_Yes -> fileSave me+ QMessageBox.QMessageBoxStandardButton_No -> return True+ _ -> return False+ else return True++setDirty :: Notepad -> Bool -> IO ()+setDirty me dirty = do+ let ref = myDirtyRef me+ dirtyOld <- readIORef ref+ when (dirty /= dirtyOld) $ do+ writeIORef ref dirty+ updateTitle me++setFilePath :: Notepad -> Maybe FilePath -> IO ()+setFilePath me path = do+ writeIORef (myFilePathRef me) path+ updateTitle me++updateTitle :: Notepad -> IO ()+updateTitle me = do+ dirty <- readIORef $ myDirtyRef me+ file <- fmap (maybe "(Untitled)" takeFileName) $ readIORef $ myFilePathRef me+ QWidget.setWindowTitle (myWindow me) $+ (if dirty then ('*':) else id) $+ file ++ " - Notepad"+ fileDialogFilter :: String-fileDialogFilter = "Text Files (*.txt);;C++ Files (*.cpp *.h);;All Files (*)"+fileDialogFilter =+ "Text Files (*.txt);;Haskell sources (*.hs *.hs-boot *.lhs *.chs);;All Files (*)"