I'm writing a simple calculator in GTK using Haskell and the gtk2hs bindings.
I'm trying to implement a Basic/Scientific view like in windows calculator using Glade.
I've got a GTKTable of buttons, but when I try and hide some of them a blank space is left where the buttons where. To hide the buttons I'm have some code like this:
bSqrt <- xmlGetWidget xml castToButton "bSqrt"
widgetHide bSqrt
But when I hide the four buttons I want I have a gap like below on the right hand side:
I'm new to GTK and I couldn't find layout managers like you get in java swing. Is there an easier way to do this? Can I somehow invoke the layout manager to resize the buttons for me? I haven't been able to find a method for doing this in the documentation.
Thanks in advance,
Consider using a nested combination of HBox and VBox to achieve a table-like effect. Calling widgetHideAll on a VBox of "scientific" buttons will hide that column and refresh the display as desired.
import Control.Monad (forM_)
import Data.IORef as IORef
import qualified Graphics.UI.Gtk as Gtk
data Mode = Basic | Scientific
main = do
Gtk.initGUI
window <- Gtk.windowNew
outerVbox <- Gtk.vBoxNew False 0
-- Create a "table" of buttons as an HBox of VBoxes.
hbox <- Gtk.hBoxNew True 5
-- Load the "table" with dummy 'basic' buttons.
forM_ [0..2] $ \i -> do
vbox <- Gtk.vBoxNew False 5
forM_ [0..2] $ \j -> do
dummy <- Gtk.buttonNewWithLabel $ show (3*i+j :: Int)
Gtk.boxPackStartDefaults vbox dummy
Gtk.boxPackStartDefaults hbox vbox
-- Load rightmost column with 'scientific' buttons.
scibox <- Gtk.vBoxNew False 5
forM_ [0..2] $ \j -> do
dummy <- Gtk.buttonNewWithLabel $ "sci" ++ show (j :: Int)
Gtk.boxPackStartDefaults scibox dummy
Gtk.boxPackStartDefaults hbox scibox
-- Begin in Scientific mode.
let mode = Scientific
modeRef <- IORef.newIORef mode
-- Create a mode-toggling Button.
button <- Gtk.buttonNewWithLabel $ getButtonText mode
Gtk.on button Gtk.buttonActivated $
toggleMode button modeRef scibox
-- Pack the "table" and button vertically into window.
Gtk.boxPackStartDefaults outerVbox hbox
Gtk.boxPackStartDefaults outerVbox button
Gtk.containerAdd window outerVbox
-- Standard Gtk stuff.
Gtk.onDestroy window Gtk.mainQuit
Gtk.widgetShowAll window
Gtk.mainGUI
getButtonText Basic = "Switch to Scientific"
getButtonText Scientific = "Switch to Basic"
toggleMode button modeRef scibox = do
mode <- IORef.readIORef modeRef
case mode of
Basic -> do
IORef.writeIORef modeRef Scientific
Gtk.buttonSetLabel button $ getButtonText Scientific
Gtk.widgetShowAll scibox
Scientific -> do
IORef.writeIORef modeRef Basic
Gtk.buttonSetLabel button $ getButtonText Basic
Gtk.widgetHideAll scibox
Related
I'm having difficulty getting widgets to space properly with GTK, I'm trying to make a simple window that displays a TextView in the majority of the window, and has a little bar at the top with a few buttons. I'm thinking something like this:
------------------------------
- -
------------------------------
- -
- -
- -
- -
- -
------------------------------
But GTK is actually giving me something more like this:
-----------------------------
- -
- -
- -
-----------------------------
- -
- -
- -
-----------------------------
I don't want the respective boxes to be split in half like that. How can I fix my code?
import Graphics.UI.Gtk
main :: IO ()
main = do
initGUI
window <- windowNew
windowBox <- vBoxNew True 2
menuBox <- hBoxNew True 2
buttonOne <- buttonNewWithLabel "Button 1"
buttonTwo <- buttonNewWithLabel "Button 2"
textBuf <- textBufferNew Nothing
textView <- textViewNewWithBuffer textBuf
textBufferSetText textBuf "some text"
boxPackStart menuBox buttonOne PackNatural 0
boxPackStart menuBox buttonTwo PackNatural 0
boxPackStart windowBox menuBox PackNatural 0
boxPackStart windowBox textView PackGrow 0
set window [containerChild := windowBox]
widgetShowAll window
mainGUI
The "windowBox" is being created with homogeneous set to True. Simply setting this to False will give the desired result:
windowBox <- vBoxNew False 2
See: http://hackage.haskell.org/package/gtk-0.12.3/docs/Graphics-UI-Gtk-Layout-VBox.html
I'm working on a program that contains a text editor on it and will be used for programming (which means that the font is monospaced and has the same size in the entire text). As such, it would be useful if it were possible to see the number of each line (paragraph, not display line) at the side of the TextView widget.
In another question (GTK+ line numbering for Text View), it was said that the SourceView widget does the job, but this widget doesn't seem to exist in gtk2hs, so I'm stuck with TextView.
Does the library offer a simple solution to this problem, or do I have to do it the hard way?
The minimalist code for using a SourceView with numbered lines with Haskell and Gtk2Hs is :
import Graphics.UI.Gtk
import Graphics.UI.Gtk.SourceView
main :: IO ()
main= do
initGUI
window <- windowNew
set window [ windowTitle := "SourceView"
, windowDefaultWidth := 100
, windowDefaultHeight := 100
, windowResizable :=True ]
sview <- sourceViewNew
sourceViewSetShowLineNumbers sview True
containerAdd window sview
onDestroy window mainQuit
widgetShowAll window
mainGUI
I would like to
remove borders (only) from floating windows covering the full screen (like mplayer), and
use a different border color (normalBorderColor) when there is only one window in a workspace.
Currently, I am using smartBorders from XMonad.Layout.NoBorders to remove the border from mplayer, and to remove the border of a window when that window is the only window in a workspace. However, when I switch between two workspaces which both have a single non-floating window (regardless of mode (tall/mirror/full)), then I see the window (in the workspace I am changing into) "jump" a bit, as its border is drawn, and then removed a brief moment thereafter (the effect is more visible if you set your borderWidth to a large number).
The relevant part of my ~/.xmonad/xmonad.hs is given below.
import XMonad.Hooks.ManageDocks
import XMonad.Layout.NoBorders
myLayout = tiled ||| Mirror tiled ||| Full
where
tiled = Tall 1 (3/100) (3/5)
main = xmonad $ defaultConfig
{ layoutHook = avoidStruts $ smartBorders $ myLayout
, borderWidth = 4
, normalBorderColor = "#000000" -- black
, focusedBorderColor = "#ff3f3f" -- reddish
}
Do you folks know how I achieve this effect? (is part 2. even possible?) Suggestions and pointers to extensions and/or existing configurations that achieve a similar effect greatly appreciated.
I solved pt. 1 using the Ambiguity constructor named OnlyFloat from XMonad.Layout.NoBorders.
import XMonad.Hooks.ManageDocks
import XMonad.Layout.NoBorders
myLayout = tiled ||| Mirror tiled ||| Full
where
tiled = Tall 1 (3/100) (3/5)
main = xmonad $ defaultConfig
{ layoutHook = lessBorders OnlyFloat $ avoidStruts $ myLayout
, borderWidth = 4
, normalBorderColor = "#000000" -- black
, focusedBorderColor = "#ff3f3f" -- reddish
}
I haven't addressed pt. 2. Furthermore, when I switch into a workspace, the border color of the focused window "flickers", since initially, the window is not focused (an thus its border is colored as per normalBorderColor), whereafter the window becomes focused (and thus its border gets the color focusedBorderColor).
Regarding part 2: Version 0.17.0 of the xmonad-contrib package introduced the new XMonad.Layout.VoidBorders layout modifier which removes borders just like XMonad.Layout.NoBorders does, but kind of permanently (the windows will retain their zero-width borders even when moved off of a VoidBorders-controlled workspace). This side-effect may or may not be a deal-breaker but it solves the "jumping" windows issue when switching workspaces.
I need entry controls to automatically select-all text when they receive focus. If you run the code and press tab to switch focus between the two controls, all of the text in the control is automatically selected. I need the same behavior when using the mouse to select the other control. My question is: what GTK event is signaled when an entry control is clicked with the mouse?
import Graphics.UI.Gtk
main :: IO ()
main = do
initGUI
vbox <- vBoxNew False 4
window <- windowNew
set window [ containerBorderWidth := 8,
containerChild := vbox ]
mkEntry "Entry 1" vbox
mkEntry "Entry 2" vbox
onDestroy window mainQuit
widgetShowAll window
mainGUI
mkEntry :: String -> VBox -> IO Entry
mkEntry txt vbox = do
entry <- entryNew
entrySetText entry txt
boxPackStart vbox entry PackNatural 0
-- selects all text when tabbing into the control
entry `on` entryActivate $ do editableSelectRegion entry 0 (-1)
return entry
The main problem is that the click itself causes the entry selection to be changed by GTK+ itself. I got this working by
Using focusInEvent as the trigger
Changing the selection in an idle callback, once everything is sufficiently "calmed down"
Put together:
-- selects all text when tabbing into the control
on entry focusInEvent $ do
liftIO $ flip idleAdd priorityDefaultIdle $ do
editableSelectRegion entry 0 (-1)
return False
return True
I have menus created with Glade in my gtk2hs application. Is it possible to use this in my Haskell code to write actions for each activated menu item? Is there a simple example somewhere or project on Hackage that can be used as an illustrative example?
The only menu example I was able to find http://www.muitovar.com/gtk2hs/chap7-1.html does not seem to help to work with Gtk Builder XML.
Here two examples from an application of mine. In main, I bind the GTK widgets to variables and those to callback functions that I implement in other modules.
--To load the Glade file:
gui <- builderNew
builderAddFromFile gui "myGUI.glade"
-- To close the application:
-- bind the window "mainWindow" (defined in the Glade file) to a variable
mainWindow <- builderGetObject gui castToWindow "mainWindow"
-- bind the menu item "menu_Quit" (defined in the Glade file) to a variable
mQuit <- builderGetObject gui castToMenuItem "menu_Quit"
-- bind the menu item to the GTK function "widgetDestroy"
on mQuit menuItemActivate $ widgetDestroy mainWindow
-- bind mainWindow's event "objectDestroy" to the GTK function "mainQuit"
on mainWindow objectDestroy mainQuit
-- To call a function when the user selects another menu item, say "menu_About":
-- bind the menu item to a variable
mAbout <- builderGetObject gui castToMenuItem "menu_About"
-- make the menu item show the About Dialog (defined in the Glade file)
on mAbout menuItemActivate $ do
aboutDialog <- builderGetObject gui castToDialog "aboutDialog"
set aboutDialog [ widgetVisible := True ]
dialogRun aboutDialog
set aboutDialog [ widgetVisible := False ]
You could place the do block in a function, maybe in a module where you define all your responses to GTK events:
showAboutDialog :: Builder -> IO ()
showAboutDialog gui = [insert do block here]
Then you could substitute the do block after menuItemActivate with just:
on mAbout menuItemActivate $ showAboutDialog gui
Note that I pass the Builder object to showAboutDialog because that function needs to get the dialog from the Glade file.