wxHaskell :: Notebook で tab を選択したときのイベントを取得する

radio box や list box 、choice は on selectによって選択したときのイベントを取得できますが、NotebookにはSelectingインスタンスがありません。

そこで on click イベントによりどこのタブがクリックされたか判定します。

  • on click イベントはマウスがクリックされたPointを引数として指定した関数を呼びます。
  • notebookHitTest(wxNotebook::HitTest)によりヒットしたタブのインデックスを返します。
  • notebookHitTest :: Notebook a -> Point -> Ptr CInt -> IO Int

 notebookHitTestの3番目の引数はHaskellなのにポインタ渡しです。

そこで malloc でメモリを確保し、pokeで値を書き込み、そのアドレスを返す関数を作ります。

flag :: Ptr CInt
flag  =  unsafePerformIO flag' where
           flag' = do 
             work <- malloc::IO (Ptr CInt) 
             poke work (fromIntegral wxBK_HITTEST_ONPAGE)
             return work
  • GetSelectionは変化がひとつ遅れます。タブが変化したときはGetSelection変化せず、ひとつ前のSelectionを返します。
onMouse :: Notebook() -> Point -> IO ()
onMouse nb p = do
  propagateEvent
  i  <- notebookHitTest nb p flag
  n  <- notebookGetSelection nb
  logMessage ("Notebook index: " ++ show i ++ " GetSelection: " ++ show n)
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{--------------------------------------------------------------------------------
 Copyright (c) Daan Leijen 2003
 wxWindows License.

 Demonstrates: 
 - many different kind of controls
 - message logging.

$ ghc -Wall -package wx -o Controls.exe Controls.hs
--------------------------------------------------------------------------------}
module Main where

import Graphics.UI.WX
import Graphics.UI.WXCore 
import System.IO.Unsafe
import Foreign.Marshal.Alloc
import Foreign.Storable
import Foreign.Ptr
import Foreign.C.Types

main :: IO ()
main = start gui

gui :: IO ()
gui
  = do -- main gui elements: frame, panel, text control, and the notebook
       f       <- frame [text := "Controls"]
       p       <- panel    f []
       nb      <- notebook p []

       textlog <- textCtrl p [enabled := False, wrap := WrapNone] 

       -- use text control as logger
       textCtrlMakeLogActiveTarget textlog
       logMessage "logging enabled"              
       -- set f [on closing :~ \prev -> do logSetActiveTarget oldlog; logDelete log; prev]

       -- button page
       p1   <- panel  nb []

       ok   <- button p1 [text := "Ok", on command := logMessage "ok button pressed"]
       quit <- button p1 [text := "Quit", on command := close f]

       -- radio box page
       p2   <- panel  nb []
       let rlabels = ["first", "second", "third"]
       r1   <- radioBox p2 Vertical rlabels   [text := "radio box", on select ::= logSelect]
       r2   <- radioBox p2 Horizontal rlabels [tooltip := "radio group two", on select ::= logSelect]
       rb1  <- button   p2 [text := "disable", on command ::= onEnable r1]

       -- choice
       p3   <- panel nb []
       let clabels = ["mies","noot","aap"]
       c1   <- choice p3 [tooltip := "unsorted choices", on select ::= logSelect, sorted  := False, items := clabels]
       c2   <- choice p3 [tooltip := "sorted choices", on select ::= logSelect, sorted  := True, items := clabels] 
       cb1  <- button p3 [text := "disable", on command ::= onEnable c1]

       -- list box page
       p4   <- panel nb []
       sl1  <- singleListBox p4 
                  [items      := clabels
                  ,tooltip    := "unsorted single-selection listbox"
                  ,on select ::= logSelect]
       sl2  <- singleListBox p4 
                  [items      := clabels
                  ,tooltip    := "sorted listbox"
                  ,on select ::= logSelect, sorted     := True]
       sc1  <- checkBox p4 [text := "enable the listbox", checked := True, on command := set sl1 [enabled :~ not]]

       -- slider/gauge page
       p5   <- panel nb []
       s    <- hslider p5 True {- show labels -} 1 100 [selection := 50]
       g    <- hgauge  p5 100 [selection := 50]
       set s [on command := do{ i <- get s selection; set g [selection := i]} ]

       let tab1 = tab "buttons"   (container p1(margin 10 (floatCentre(row 5 [widget ok, widget quit]))))
           tab2 = tab "radio box" (container p2(margin 10 (column 5 [hstretch (widget rb1), row 0 [floatLeft (widget r1),floatRight(widget r2)]])))
           tab3 = tab "choice"    (container p3(margin 10 (column 5 [hstretch (widget cb1), row 0 [floatLeft (widget c1),floatRight(row 5 [label "sorted: ", widget c2])]])))
           tab4 = tab "listbox"   (container p4(margin 10 (column 5 [ hstretch (dynamic (widget sc1)), floatLeft (row 0 [widget sl1, widget sl2])])))
           tab5 = tab "slider"    (container p5(margin 10 (column 5 [hfill (widget s),hfill (widget g) ,glue ])))
           nbtab= tabs nb [tab1, tab2,tab3,tab4,tab5]

       -- ここでNotebookの選択イベントを設定
       set nb [on click := onMouse nb]
       -- specify layout
       set f [layout :=
              container p $
                column 0
                   [ nbtab, fill $ minsize (sz 100 200) $ widget textlog ]
             , clientSize := sz 800 300 ]
       return ()

  where
    -- logSelect :: (Selection w, Items w String) => w -> IO ()
    logSelect w
      = do i <- get w selection
           s <- get w (item i)
           logMessage ("selected index: " ++ show i ++ ": " ++ s)

    onEnable w b
      = do set w [enabled :~ not]
           enable <- get w enabled
           set b [text := (if enable then "disable" else "enable")]


flag :: Ptr CInt
flag  =  unsafePerformIO flag' where
           flag' = do 
             work <- malloc::IO (Ptr CInt) 
             poke work (fromIntegral wxBK_HITTEST_ONPAGE)
             return work

onMouse :: Notebook() -> Point -> IO ()
onMouse nb p = do
  propagateEvent
  i  <- notebookHitTest nb p flag
  n  <- notebookGetSelection nb
  putStrLn (show (i,n, i == n))

-- kindof :: Object a -> String -> IO ()
kindof obj className
  = do classInfo <- classInfoFindClass className
       if (objectIsNull classInfo)
        then logMessage ("kindof " ++ className ++ ": no such class")
        else if (objectIsNull obj)
              then logMessage ("kindof " ++ className ++ ": null object")
              else do haskind <- objectIsKindOf obj classInfo
                      logMessage ("kindof " ++ className ++ ": " ++ show haskind)
  • withを使えばunsafePerformIOも使わずにすむ。
import Foreign.Marshal.Utils

onMouse :: Notebook() -> Point -> IO ()
onMouse nb p = do
  propagateEvent
  i  <- with (fromIntegral wxBK_HITTEST_ONPAGE)  (\ptr -> notebookHitTest nb p ptr)
  n  <- notebookGetSelection nb
  logMessage ("Notebook index: " ++ show i ++ " GetSelection: " ++ show n)