From: Axel S. <A....@ke...> - 2007-12-02 21:39:09
|
Wed Nov 28 08:50:52 PST 2007 hth...@zo... * Tutorial-Port Popup, Radio and Toggle Actions (Chapter7.2) addfile ./docs/tutorial/Tutorial_Port/Example_Code/GtkChap7-2a.hs hunk ./docs/tutorial/Tutorial_Port/Example_Code/GtkChap7-2a.hs 1 +import Graphics.UI.Gtk + +main :: IO () +main= do + initGUI + window <- windowNew + set window [windowTitle := "Click Right Popup", + windowDefaultWidth := 250, + windowDefaultHeight := 150 ] + + eda <- actionNew "EDA" "Edit" Nothing Nothing + pra <- actionNew "PRA" "Process" Nothing Nothing + rma <- actionNew "RMA" "Remove" Nothing Nothing + saa <- actionNew "SAA" "Save" Nothing Nothing + + agr <- actionGroupNew "AGR1" [_$_] + mapM_ (actionGroupAddAction agr) [eda,pra,rma,saa] + + uiman <- uiManagerNew + uiManagerAddUiFromString uiman uiDecl + uiManagerInsertActionGroup uiman agr 0 + + maybePopup <- uiManagerGetWidget uiman "/ui/popup" + let pop = case maybePopup of [_$_] + (Just x) -> x + Nothing -> error "Cannot get popup from string" + + onButtonPress window (\x -> if (eventButton x) == RightButton + then do menuPopup (castToMenu pop) Nothing + return (eventSent x) + else return (eventSent x)) + + mapM_ prAct [eda,pra,rma,saa] + + widgetShowAll window + onDestroy window mainQuit + mainGUI + +uiDecl = "<ui> \ +\ <popup>\ +\ <menuitem action=\"EDA\" />\ +\ <menuitem action=\"PRA\" />\ +\ <menuitem action=\"RMA\" />\ +\ <separator />\ +\ <menuitem action=\"SAA\" />\ +\ </popup>\ +\ </ui>" + +prAct :: ActionClass self => self -> IO (ConnectId self) +prAct a = onActionActivate a $ do name <- actionGetName a + putStrLn ("Action Name: " ++ name) addfile ./docs/tutorial/Tutorial_Port/Example_Code/GtkChap7-2b.hs hunk ./docs/tutorial/Tutorial_Port/Example_Code/GtkChap7-2b.hs 1 +import Graphics.UI.Gtk + +main :: IO () +main= do + initGUI + window <- windowNew + set window [windowTitle := "Radio and Toggle Actions", + windowDefaultWidth := 400, + windowDefaultHeight := 200 ] + [_$_] + mhma <- actionNew "MHMA" "Highlight\nMode" Nothing Nothing + msma <- actionNew "MSMA" "Source" Nothing Nothing + mmma <- actionNew "MMMA" "Markup" Nothing Nothing [_$_] + + agr1 <- actionGroupNew "AGR1" + mapM_ (actionGroupAddAction agr1) [mhma,msma,mmma] + actionGroupAddRadioActions agr1 hlmods 0 myOnChange + + vima <- actionNew "VIMA" "View" Nothing Nothing [_$_] + + agr2 <- actionGroupNew "AGR2" + actionGroupAddAction agr2 vima + actionGroupAddToggleActions agr2 togls + + uiman <- uiManagerNew + uiManagerAddUiFromString uiman uiDef1 + uiManagerInsertActionGroup uiman agr1 0 + + uiManagerAddUiFromString uiman uiDef2 + uiManagerInsertActionGroup uiman agr2 1 + + + mayMenubar <- uiManagerGetWidget uiman "/ui/menubar" + let mb = case mayMenubar of [_$_] + (Just x) -> x + Nothing -> error "Cannot get menu bar." + + mayToolbar <- uiManagerGetWidget uiman "/ui/toolbar" + let tb = case mayToolbar of [_$_] + (Just x) -> x + Nothing -> error "Cannot get tool bar." + + box <- vBoxNew False 0 + containerAdd window box + boxPackStart box mb PackNatural 0 + boxPackStart box tb PackNatural 0 + + widgetShowAll window + onDestroy window mainQuit + mainGUI + +hlmods :: [RadioActionEntry] +hlmods = [ + RadioActionEntry "NOA" "None" Nothing Nothing Nothing 0, [_$_] + RadioActionEntry "SHA" "Haskell" (Just stockHome) Nothing Nothing 1, [_$_] + RadioActionEntry "SCA" "C" Nothing Nothing Nothing 2, + RadioActionEntry "SJA" "Java" Nothing Nothing Nothing 3, + RadioActionEntry "MHA" "HTML" Nothing Nothing Nothing 4, + RadioActionEntry "MXA" "XML" Nothing Nothing Nothing 5] + +myOnChange :: RadioAction -> IO () +myOnChange ra = do val <- radioActionGetCurrentValue ra + putStrLn ("RadioAction " ++ (show val) ++ " now active.") + +uiDef1 = " <ui> \ +\ <menubar>\ +\ <menu action=\"MHMA\">\ +\ <menuitem action=\"NOA\" />\ +\ <separator />\ +\ <menu action=\"MSMA\">\ +\ <menuitem action= \"SHA\" /> \ +\ <menuitem action= \"SCA\" /> \ +\ <menuitem action= \"SJA\" /> \ +\ </menu>\ +\ <menu action=\"MMMA\">\ +\ <menuitem action= \"MHA\" /> \ +\ <menuitem action= \"MXA\" /> \ +\ </menu>\ +\ </menu>\ +\ </menubar>\ +\ <toolbar>\ +\ <toolitem action=\"SHA\" />\ +\ </toolbar>\ +\ </ui> " + [_$_] + +togls :: [ToggleActionEntry] +togls = let mste = ToggleActionEntry "MST" "Messages" Nothing Nothing Nothing + (myTog mste) False [_$_] + ttte = ToggleActionEntry "ATT" "Attributes" Nothing Nothing Nothing + (myTog ttte) False [_$_] + erte = ToggleActionEntry "ERT" "Errors" (Just stockInfo) Nothing Nothing + (myTog erte) True [_$_] + in [mste,ttte,erte] + +myTog :: ToggleActionEntry -> IO () +myTog te = putStrLn ("The state of " ++ (toggleActionName te) [_$_] + ++ " (" ++ (toggleActionLabel te) ++ ") " [_$_] + ++ " is now " ++ (show $ not (toggleActionIsActive te))) + +uiDef2 = "<ui>\ +\ <menubar>\ +\ <menu action=\"VIMA\">\ +\ <menuitem action=\"MST\" />\ +\ <menuitem action=\"ATT\" />\ +\ <menuitem action=\"ERT\" />\ +\ </menu>\ +\ </menubar>\ +\ <toolbar>\ +\ <toolitem action=\"MST\" />\ +\ <toolitem action=\"ERT\" />\ +\ </toolbar>\ +\ </ui>" addfile ./docs/tutorial/Tutorial_Port/Images/GtkChap7-2.png binary ./docs/tutorial/Tutorial_Port/Images/GtkChap7-2.png hunk ./docs/tutorial/Tutorial_Port/chap7-1.xhtml 309 - <br />7.2</span> + <br />Popup Menus, Radio Actions and Toggle Actions</span> addfile ./docs/tutorial/Tutorial_Port/chap7-2.xhtml hunk ./docs/tutorial/Tutorial_Port/chap7-2.xhtml 1 +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <meta http-equiv="Content-Type" + content="text/html; charset=utf-8" /> + <title>Gtk2Hs Tutorial: Menus and Toolbars</title> + <link href="default.css" type="text/css" rel="stylesheet" /> + </head> + <body> + <div id="header"> + <h1>Gtk2Hs Tutorial</h1> + <span class="nav-previous"> + <a href="chap7-1.xhtml">Previous</a> + </span> + <span class="nav-home"> + <a href="index.xhtml">Home</a> + </span> + <span class="nav-next"> + <a href="chap8-1.xhtml">Next</a> + </span> + </div> + <h2>7.2 Popup Menus, Radio Actions and Toggle Actions</h2> + <p>Menus are normally just added to a window, but they can also + be displayed temporarily as the result of a mouse button click. + For instance, a context menu might be displayed when the user + clicks their right mouse button.</p> + <p>The UI layout for a popup menu should use the [_$_] + <code>popup</code> node. For instance:</p> + <pre class="codebox">uiDecl = "<ui> \ +\ <popup>\ +\ <menuitem action=\"EDA\" />\ +\ <menuitem action=\"PRA\" />\ +\ <menuitem action=\"RMA\" />\ +\ <separator />\ +\ <menuitem action=\"SAA\" />\ +\ </popup>\ +\ </ui>" [_$_] +</pre> + <p>Constructing a popup menu takes the same steps as a menu or + a toolbar (but also see below). Once you've created the actions [_$_] + and put them into one or more groups you create the ui manager, [_$_] + add the XML string and add the groups. Then you extract the widget(s). + In the pop up example we've created the 4 actions with the names + listed above. The popup menu doesn't show in a screen shot, so + we've omitted the picture.</p> + <p>Because it's a popup we don't pack the widget. To show it we + need the function:</p> + <pre class="codebox">menuPopup :: MenuClass self => self -> Maybe (MouseButton,TimeStamp) +</pre> + <p>This is documented in Graphics.UI.Gtk.MenuComboToolbar.Menu + in the API documentation. In the example we pop up the menu by + clicking the right mouse button, and the second argument can be [_$_] + <code>Nothing</code>. The function is the same as with the + event box in Chapter 6.2. Here, however, we can use the + window itself instead of an event box.</p> + <pre class="codebox">onButtonPress window (\x -> if (eventButton x) == RightButton + then do menuPopup (castToMenu pop) Nothing + return (eventSent x) + else return (eventSent x)) +</pre> + <p>The only hitch is that the widget returned by the ui manager + is of type <code>Widget</code>and the [_$_] + <code>menuPopup</code>function takes an argument of a type + which is an instance of <code>MenuClass</code>. So we have to use:</p> + <pre class="codebox">castToMenu :: GObjectClass obj => obj -> Menu +</pre> + <p>This function is also documented in the Graphics.UI.Gtk.MenuComboToolbar.Menu + section. The complete listing of the example is:</p> + <pre class="codebox"> +import Graphics.UI.Gtk + +main :: IO () +main= do + initGUI + window <- windowNew + set window [windowTitle := "Click Right Popup", + windowDefaultWidth := 250, + windowDefaultHeight := 150 ] + + eda <- actionNew "EDA" "Edit" Nothing Nothing + pra <- actionNew "PRA" "Process" Nothing Nothing + rma <- actionNew "RMA" "Remove" Nothing Nothing + saa <- actionNew "SAA" "Save" Nothing Nothing + + agr <- actionGroupNew "AGR1" [_$_] + mapM_ (actionGroupAddAction agr) [eda,pra,rma,saa] + + uiman <- uiManagerNew + uiManagerAddUiFromString uiman uiDecl + uiManagerInsertActionGroup uiman agr 0 + + maybePopup <- uiManagerGetWidget uiman "/ui/popup" + let pop = case maybePopup of [_$_] + (Just x) -> x + Nothing -> error "Cannot get popup from string" + + onButtonPress window (\x -> if (eventButton x) == RightButton + then do menuPopup (castToMenu pop) Nothing + return (eventSent x) + else return (eventSent x)) + + mapM_ prAct [eda,pra,rma,saa] + + widgetShowAll window + onDestroy window mainQuit + mainGUI + +uiDecl = "<ui> \ +\ <popup>\ +\ <menuitem action=\"EDA\" />\ +\ <menuitem action=\"PRA\" />\ +\ <menuitem action=\"RMA\" />\ +\ <separator />\ +\ <menuitem action=\"SAA\" />\ +\ </popup>\ +\ </ui>" [_$_] + +prAct :: ActionClass self => self -> IO (ConnectId self) +prAct a = onActionActivate a $ do name <- actionGetName a + putStrLn ("Action Name: " ++ name) +</pre> + <p>There is another way to use actions, without explicitly + creating them, through the <code>ActionEntry</code> datatype:</p> + <pre class="codebox">data ActionEntry = ActionEntry { +actionEntryName :: String +actionEntryLabel :: String +actionEntryStockId :: (Maybe String) +actionEntryAccelerator :: (Maybe String) +actionEntryTooltip :: (Maybe String) +actionEntryCallback :: (IO ()) +} +</pre> + <p>The use of these fields is as their names indicate and as + has been described above and in Chapter 7.1. The [_$_] + <code>actionEntryCallback</code> function must be supplied by + the programmer, and will be executed when that particular + action is activated.</p> + <p>Add a list of entries to an action group with:</p> + <pre class="codebox">actionGroupAddActions :: ActionGroup -> [ActionEntry] -> IO () +</pre> + <p>The group then is inserted using [_$_] + <code>uiManagerInsertActionGroup</code> as before. </p> + <p>Similar functions exist for <code>RadioAction</code> and <code>ToggleAction</code> . + Radio actions let the user choose from a number of + possibilities, of which only one can be active. Because of this it makes sense [_$_] + to define them all together. The definition is:</p> + <pre class="codebox">data RadioActionEntry = RadioActionEntry { +radioActionName :: String +radioActionLabel :: String +radioActionStockId :: (Maybe String) +radioActionAccelerator :: (Maybe String) +radioActionTooltip :: (Maybe String) +radioActionValue :: Int +} +</pre> + <p>The first 5 fields are again used as expected. The [_$_] + <code>radioActionValue</code> identifies each of the possible + selections. Addition to a group is done with:</p> + <pre class="codebox">actionGroupAddRadioActions :: [_$_] + ActionGroup -> [RadioActionEntry] -> Int -> (RadioAction -> IO ()) -> IO () +</pre> + <p>The [_$_] + <code>Int</code> parameter is the value of the action to + activate initially, or -1 for none.</p> + <p class="notebox"> + <strong>Note:</strong> In the example below this appeared to + have no effect; the last action is always selected + initially.</p> + <p>The function of type [_$_] + <code>(RadioAction -> IO ())</code>is executed whenever that + action is activated.</p> + <p>Toggle actions have a [_$_] + <code>Bool</code> value and each may be set or not. The [_$_] + <code>ToggleActionEntry</code> is defined as:</p> + <pre class="codebox">data ToggleActionEntry = ToggleActionEntry { +toggleActionName :: String +toggleActionLabel :: String +toggleActionStockId :: (Maybe String) +toggleActionAccelerator :: (Maybe String) +toggleActionTooltip :: (Maybe String) +toggleActionCallback :: (IO ()) +toggleActionIsActive :: Bool +} +</pre> + <p>The example below demonstrates the use of toggle + actions as well as radio actions.</p> + <p class="notebox"> + <strong>Note:</strong> The [_$_] + <code>toggleActionCallback</code> function has the wrong + value on my platform; the workaround is, of course, to use the + <code>not</code> function.</p> + <img src="Images/GtkChap7-2.png" + alt="RadioAction and ToggleAction" id="imgGtkChap7-2" /> + <p>The radio buttons could control a highlight mode, as in the + gedit text editor, from which this was copied. The first menu + has one button and two sub menus which contain the remaining + items. Furthermore, one of the radio buttons is an item in a + tool bar. This layout is controlled completely by the first XML + definition.</p> + <p>The toggle actions are items in another menu, and two of + those are also placed in a toolbar. This layout is determined by + the second XML definition.</p> + <p>The interesting thing is that the [_$_] + <code>uiManager</code> can merge these ui definitions just by + adding them, as shown below. So you can define your menus in separate modules + and easily combine them later in the main module. According to + the documentation the ui manager is quite smart at this, and of + course you can also use names in the XML definitions to + distinguish paths. But recall that the [_$_] + <code>String</code> denoting an action name must be unique for each action.</p> + <p>It is also possible to unmerge menus and toolbars, using the [_$_] + <code>MergeId</code> and the <code>uiManagerRemoveUi</code> function. [_$_] + In this way you can manage menus and toolbars dynamically.</p> + <pre class="codebox"> +import Graphics.UI.Gtk + +main :: IO () +main= do + initGUI + window <- windowNew + set window [windowTitle := "Radio and Toggle Actions", + windowDefaultWidth := 400, + windowDefaultHeight := 200 ] + [_$_] + mhma <- actionNew "MHMA" "Highlight\nMode" Nothing Nothing + msma <- actionNew "MSMA" "Source" Nothing Nothing + mmma <- actionNew "MMMA" "Markup" Nothing Nothing [_$_] + + agr1 <- actionGroupNew "AGR1" + mapM_ (actionGroupAddAction agr1) [mhma,msma,mmma] + actionGroupAddRadioActions agr1 hlmods 0 myOnChange + + vima <- actionNew "VIMA" "View" Nothing Nothing [_$_] + + agr2 <- actionGroupNew "AGR2" + actionGroupAddAction agr2 vima + actionGroupAddToggleActions agr2 togls + + uiman <- uiManagerNew + uiManagerAddUiFromString uiman uiDef1 + uiManagerInsertActionGroup uiman agr1 0 + + uiManagerAddUiFromString uiman uiDef2 + uiManagerInsertActionGroup uiman agr2 1 + + mayMenubar <- uiManagerGetWidget uiman "/ui/menubar" + let mb = case mayMenubar of [_$_] + (Just x) -> x + Nothing -> error "Cannot get menu bar." + + mayToolbar <- uiManagerGetWidget uiman "/ui/toolbar" + let tb = case mayToolbar of [_$_] + (Just x) -> x + Nothing -> error "Cannot get tool bar." + + box <- vBoxNew False 0 + containerAdd window box + boxPackStart box mb PackNatural 0 + boxPackStart box tb PackNatural 0 + + widgetShowAll window + onDestroy window mainQuit + mainGUI + +hlmods :: [RadioActionEntry] +hlmods = [ + RadioActionEntry "NOA" "None" Nothing Nothing Nothing 0, [_$_] + RadioActionEntry "SHA" "Haskell" (Just stockHome) Nothing Nothing 1, [_$_] + RadioActionEntry "SCA" "C" Nothing Nothing Nothing 2, + RadioActionEntry "SJA" "Java" Nothing Nothing Nothing 3, + RadioActionEntry "MHA" "HTML" Nothing Nothing Nothing 4, + RadioActionEntry "MXA" "XML" Nothing Nothing Nothing 5] + +myOnChange :: RadioAction -> IO () +myOnChange ra = do val <- radioActionGetCurrentValue ra + putStrLn ("RadioAction " ++ (show val) ++ " now active.") + +uiDef1 = " <ui> \ +\ <menubar>\ +\ <menu action=\"MHMA\">\ +\ <menuitem action=\"NOA\" />\ +\ <separator />\ +\ <menu action=\"MSMA\">\ +\ <menuitem action= \"SHA\" /> \ +\ <menuitem action= \"SCA\" /> \ +\ <menuitem action= \"SJA\" /> \ +\ </menu>\ +\ <menu action=\"MMMA\">\ +\ <menuitem action= \"MHA\" /> \ +\ <menuitem action= \"MXA\" /> \ +\ </menu>\ +\ </menu>\ +\ </menubar>\ +\ <toolbar>\ +\ <toolitem action=\"SHA\" />\ +\ </toolbar>\ +\ </ui> " [_$_] + +togls :: [ToggleActionEntry] +togls = let mste = ToggleActionEntry "MST" "Messages" Nothing Nothing Nothing (myTog mste) False [_$_] + ttte = ToggleActionEntry "ATT" "Attributes" Nothing Nothing Nothing (myTog ttte) False [_$_] + erte = ToggleActionEntry "ERT" "Errors" (Just stockInfo) Nothing Nothing (myTog erte) True [_$_] + in [mste,ttte,erte] + +myTog :: ToggleActionEntry -> IO () +myTog te = putStrLn ("The state of " ++ (toggleActionName te) [_$_] + ++ " (" ++ (toggleActionLabel te) ++ ") " [_$_] + ++ " is now " ++ (show $ not (toggleActionIsActive te))) +uiDef2 = "<ui>\ +\ <menubar>\ +\ <menu action=\"VIMA\">\ +\ <menuitem action=\"MST\" />\ +\ <menuitem action=\"ATT\" />\ +\ <menuitem action=\"ERT\" />\ +\ </menu>\ +\ </menubar>\ +\ <toolbar>\ +\ <toolitem action=\"MST\" />\ +\ <toolitem action=\"ERT\" />\ +\ </toolbar>\ +\ </ui>" +</pre> + <div id="footer"> + <span class="nav-previous"> + <a href="chap7-1.xhtml">Previous</a> + <br />7.1 Menus and Toolbars</span> + <span class="nav-home"> + <a href="index.xhtml">Home</a> + </span> + <span class="nav-next"> + <a href="chap8-1.xhtml">Next</a> + <br />8.1</span> + </div> + </body> +</html> hunk ./docs/tutorial/Tutorial_Port/index.xhtml 109 + </li> + <li> + <a href="chap7-2.xhtml">7.2 Popup Menus, Radio Actions and Toggle Actions</a> |