mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-08-18 21:53:47 -07:00
Compare commits
175 Commits
v0.12
...
feature/fi
Author | SHA1 | Date | |
---|---|---|---|
|
1833003404 | ||
|
be036f9bb9 | ||
|
217abc39a2 | ||
|
5790913eae | ||
|
d21ed81801 | ||
|
cc44be649d | ||
|
a7059e1a32 | ||
|
7ada94df42 | ||
|
c0a0a44fbc | ||
|
ca5fbc155b | ||
|
856b125186 | ||
|
c51bd739d7 | ||
|
0e1cecd135 | ||
|
ec5f9a9e59 | ||
|
65bbe1a995 | ||
|
7b8798cb30 | ||
|
0a74e3479e | ||
|
8e061c0c6d | ||
|
49fecdf4eb | ||
|
993dedf6d3 | ||
|
fcb57bd657 | ||
|
05d7493888 | ||
|
4983ecfd23 | ||
|
ae7fd21e29 | ||
|
6cb10c9300 | ||
|
e98fedfaa5 | ||
|
dcc2759c4d | ||
|
b871a0c7ee | ||
|
c71f72ff66 | ||
|
e5ca066057 | ||
|
444986d993 | ||
|
1553d81ce7 | ||
|
082c64ec37 | ||
|
01ddbb7b82 | ||
|
feec53c78c | ||
|
e4e120bb8e | ||
|
858a906240 | ||
|
1b81ac7314 | ||
|
262e78770f | ||
|
1c8e17e127 | ||
|
f3de3e2719 | ||
|
464a99b842 | ||
|
f6ded1a4d7 | ||
|
3b4a3d2bd2 | ||
|
753e9ce4b0 | ||
|
bf1f4fcc76 | ||
|
305c8eff0d | ||
|
52b180e6b2 | ||
|
c2331f9657 | ||
|
953f1576f4 | ||
|
7629f774c6 | ||
|
a1adb0b801 | ||
|
c3081bd783 | ||
|
e38fb3bdb8 | ||
|
c48d81e378 | ||
|
94c7cb513c | ||
|
61038f95fb | ||
|
4358f58de8 | ||
|
899ff52316 | ||
|
ea6e1a5d6d | ||
|
4aaf053273 | ||
|
2e53a6cdd6 | ||
|
76565e42c4 | ||
|
a7d5696e5a | ||
|
b9215181bb | ||
|
806a501d51 | ||
|
529683660c | ||
|
15a2a86d46 | ||
|
25df357a4a | ||
|
c2e0fc517c | ||
|
1087844a7f | ||
|
8bfbafeae9 | ||
|
7e777bebfd | ||
|
8d2582f032 | ||
|
81f1eab1ee | ||
|
4e880b37a2 | ||
|
637c5c67b1 | ||
|
8fd8c5d02d | ||
|
d414c76da8 | ||
|
ddcc9e0209 | ||
|
e280f62a57 | ||
|
a8ca8bcd6f | ||
|
f3dc89f821 | ||
|
6a9e9e5a78 | ||
|
1d5cdc108a | ||
|
d6243c9564 | ||
|
c2c6a94834 | ||
|
6aa289c713 | ||
|
cc77b5019d | ||
|
a421da29e6 | ||
|
2831378f8f | ||
|
34f9dda006 | ||
|
e698e5fe53 | ||
|
fce36bda16 | ||
|
73134369ea | ||
|
26b50c043c | ||
|
ed4909aa65 | ||
|
f12167b298 | ||
|
00c6a44bdc | ||
|
b41544b6cc | ||
|
da44e76f75 | ||
|
75b3cae49f | ||
|
72956159b6 | ||
|
9a187f243c | ||
|
e0211ad7d6 | ||
|
c08d48f6aa | ||
|
81dd1cba1d | ||
|
abe911a8d6 | ||
|
1452c9e273 | ||
|
44abb6c8d4 | ||
|
43fccf1a6c | ||
|
f429843b66 | ||
|
dd5a36cc08 | ||
|
01ea659a06 | ||
|
f1d3118417 | ||
|
ceb2df8931 | ||
|
99cc0b6c85 | ||
|
9c95c81a90 | ||
|
15c645d9f2 | ||
|
94a7e97ac8 | ||
|
c736d52268 | ||
|
c27ef4d418 | ||
|
571193a219 | ||
|
bbbdad8faa | ||
|
311d3a0582 | ||
|
b14db06f65 | ||
|
30f657a437 | ||
|
a6f286dbdc | ||
|
3796569268 | ||
|
d5eb7316d1 | ||
|
baf1dd9251 | ||
|
5e96324d80 | ||
|
5df7ba160e | ||
|
431fd66527 | ||
|
f79e3fadea | ||
|
28e9f8bce7 | ||
|
f73eb1c938 | ||
|
83ee18ad94 | ||
|
f4d4bde026 | ||
|
f1b9a0c193 | ||
|
34beb76562 | ||
|
028ad6d6ec | ||
|
a5e87e3894 | ||
|
2855ed3d70 | ||
|
68cfa84b91 | ||
|
b20e7fa1e4 | ||
|
58c3062910 | ||
|
889cd97d08 | ||
|
4a9e28ca8b | ||
|
604a262f38 | ||
|
0510da7659 | ||
|
93b2620ad3 | ||
|
727e214195 | ||
|
9a7a63bfb4 | ||
|
a61ce8dd74 | ||
|
d638dc8b0a | ||
|
bce9c551ef | ||
|
26309d1622 | ||
|
d81b4e5bcb | ||
|
6043914841 | ||
|
ed7be9a791 | ||
|
becb724f95 | ||
|
0447c76d48 | ||
|
e47794148c | ||
|
edd6b8be55 | ||
|
ddcf5abcbf | ||
|
e19460677a | ||
|
b23f56d65d | ||
|
c3b05ceb7f | ||
|
9f68077c6c | ||
|
723494f01e | ||
|
ae6b8db29b | ||
|
1ce26e8cd2 | ||
|
800ae670e2 | ||
|
d81c48d022 |
@@ -1,80 +0,0 @@
|
||||
Alejandro Serrano Mena <trupill@gmail.com>, trupill@gmail.com
|
||||
Alexandre Buisse <buisse@cs.chalmers.se>, buisse@cs.chalmers.se
|
||||
Audun Skaugen <audun@skaugen.name>, Audun Skaugen <audunskaugen@gmail.com>, audunskaugen@gmail.com
|
||||
Bas van Dijk <v.dijk.bas@gmail.com>, v.dijk.bas@gmail.com
|
||||
Ben Boeckel <mathstuf@gmail.com>, mathstuf@gmail.com
|
||||
Brandon S Allbery KF8NH <allbery.b@gmail.com>, allbery@ece.cmu.edu
|
||||
Brent Yorgey <byorgey@cis.upenn.edu>, Brent Yorgey <byorgey@gmail.com>
|
||||
Carlos Lopez-Camey <c.lopez@kmels.net>, c.lopez@kmels.net
|
||||
Carsten Otto <xmonad@c-otto.de>, xmonad@c-otto.de
|
||||
Christian Dietrich <stettberger@dokucode.de>, stettberger@dokucode.de
|
||||
Daniel Neri <daniel.neri@sigicom.com>, Daniel Neri <daniel.neri@sigicom.se>
|
||||
Daniel Schoepe <daniel.schoepe@googlemail.com>, Daniel Schoepe <daniel.schoepe@gmail.com>, Daniel Schoepe <asgaroth_@gmx.de>
|
||||
Daniel Wagner <daniel@wagner-home.com>, daniel@wagner-home.com
|
||||
Dave Harrison <dave@nullcube.com>, dave@nullcube.com
|
||||
David Glasser <glasser@mit.edu>, glasser@mit.edu
|
||||
David McLean <gopsychonauts@gmail.com>, gopsychonauts@gmail.com
|
||||
Dominik Bruhn <dominik@dbruhn.de>, dominik@dbruhn.de
|
||||
Don Stewart <dons00@gmail.com>, Don Stewart <dons@cse.unsw.edu.au>, Don Stewart <dons@galois.com>
|
||||
Gwern Branwen <gwern0@gmail.com>, gwern0@gmail.com
|
||||
Henrique Abreu <hgabreu@gmail.com>, `Henrique Abreu <hgabreu@gmail.com>'
|
||||
Ilya Portnov <portnov84@rambler.ru>, portnov84@rambler.ru
|
||||
intrigeri <intrigeri@boum.org>, intrigeri@boum.org
|
||||
Ivan Miljenovic <Ivan.Miljenovic@gmail.com>, Ivan.Miljenovic@gmail.com
|
||||
Jan-David Quesel <quesel@informatik.uni-oldenburg.de>, quesel@informatik.uni-oldenburg.de
|
||||
Jeremy Apthorp <nornagon@gmail.com>, nornagon@gmail.com
|
||||
Joachim Breitner <mail@joachim-breitner.de>, mail@joachim-breitner.de
|
||||
Joachim Fasting <joachim.fasting@gmail.com>, joachim.fasting@gmail.com
|
||||
Joel Suovaniemi <joel.suovaniemi@iki.fi>, joel.suovaniemi@iki.fi
|
||||
Joe Thornber <joe.thornber@gmail.com>, joe.thornber@gmail.com
|
||||
Johann Giwer <johanngiwer@web.de>, johanngiwer@web.de
|
||||
Jussi Maki <joamaki@gmail.com>, joamaki@gmail.com
|
||||
kedals0 <kedals0@gmail.com>, kedals0@gmail.com
|
||||
Konstantin Sobolev <konstantin.sobolev@gmail.com>, konstantin.sobolev@gmail.com
|
||||
Lanny Ripple <lan3ny@gmail.com>, lan3ny@gmail.com
|
||||
Leonardo Serra <leoserra@minaslivre.org>, leoserra@minaslivre.org
|
||||
lithis <xmonad@selg.hethrael.org>, xmonad@selg.hethrael.org, xmonad@s001.hethrael.com
|
||||
Luis Cabellos <zhen.sydow@gmail.com>, zhen.sydow@gmail.com
|
||||
Lukas Mai <l.mai@web.de>, l.mai@web.de
|
||||
Mario Pastorelli <pastorelli.mario@gmail.com>, pastorelli.mario@gmail.com
|
||||
Mathias Stearn <redbeard0531@gmail.com>, redbeard0531@gmail.com
|
||||
Matt Brown <deadguysfrom@gmail.com>, deadguysfrom@gmail.com
|
||||
Nelson Elhage <nelhage@mit.edu>, nelhage@mit.edu
|
||||
Nicolas Dudebout <nicolas.dudebout@gatech.edu>, nicolas.dudebout@gatech.edu
|
||||
Nicolas Pouillard <nicolas.pouillard@gmail.com>, nicolas.pouillard@gmail.com
|
||||
Nils Schweinsberg <mail@n-sch.de>, mail@n-sch.de
|
||||
Norbert Zeh <nzeh@cs.dal.ca>, nzeh@cs.dal.ca
|
||||
Quentin Moser <moserq@gmail.com>, moserq@gmail.com
|
||||
Quentin Moser <quentin.moser@unifr.ch>, quentin.moser@unifr.ch
|
||||
Rickard Gustafsson <acura@allyourbase.se>, acura@allyourbase.se
|
||||
Robert Marlow <bobstopper@bobturf.org>, bobstopper@bobturf.org, robreim@bobturf.org
|
||||
Rohan Jain <crodjer@gmail.com>, crodjer@gmail.com
|
||||
Sean Escriva <sean.escriva@gmail.com>, sean.escriva@gmail.com
|
||||
Spencer Janssen <spencerjanssen@gmail.com>, sjanssen@cse.unl.edu
|
||||
Tomohiro Matsuyama <matsuyama3@ariel-networks.com>, matsuyama3@ariel-networks.com
|
||||
Tom Rauchenwald <its.sec@gmx.net>, <its.sec@gmx.net>
|
||||
Tony Morris <haskell@tmorris.net>, haskell@tmorris.net
|
||||
Valery V. Vorotyntsev <valery.vv@gmail.com>, valery.vv@gmail.com
|
||||
Will Farrington <wcfarrington@gmail.com>, wcfarrington@gmail.com
|
||||
Wirt Wolff <wirtwolff@gmail.com>, wirtwolff@gmail.com
|
||||
Yaakov Nemoy <loupgaroublond@gmail.com>, loupgaroublond@gmail.com
|
||||
timthelion <tim.thelion@gmail.com>, tim.thelion@gmail.com
|
||||
seanmce33 <seanmce33@gmail.com>, seanmce33@gmail.com
|
||||
rupa <rupa@lrrr.us>, rupa@lrrr.us
|
||||
perlkat <perlkat@katspace.org>, perlkat@katspace.org
|
||||
longpoke <longpoke@gmail.com>, longpoke@gmail.com
|
||||
cardboard42 <cardboard42@gmail.com>, cardboard42@gmail.com
|
||||
daedalusinfinity <daedalusinfinity@gmail.com>, daedalusinfinity@gmail.com
|
||||
Jens Petersen <juhp@community.haskell.org>, Jens Petersen <petersen@haskell.org>
|
||||
|
||||
Neil Mitchell <http://www.cs.york.ac.uk/~ndm/>, Neil Mitchell
|
||||
Nick Burlett <nickburlett@mac.com>, nickburlett@mac.com
|
||||
Sam Hughes <hughes@rpi.edu>, hughes@rpi.edu
|
||||
Shae Erisson <shae@ScannedInAvian.com>, shae@ScannedInAvian.com
|
||||
Conrad Irwin <conrad.irwin@gmail.com>, conrad.irwin@gmail.com
|
||||
|
||||
-- unknown:
|
||||
-- xmonad-contrib@hexago.nl
|
||||
-- brian@lorf.org
|
||||
-- jakob@pipefour.org
|
||||
|
102
.mailmap
Normal file
102
.mailmap
Normal file
@@ -0,0 +1,102 @@
|
||||
Alejandro Serrano Mena <trupill@gmail.com>
|
||||
Alexandre Buisse <buisse@cs.chalmers.se>
|
||||
Anders Engstrom <ankaan@gmail.com>
|
||||
Antoine R. Dumont <eniotna.t@gmail.com>
|
||||
Anton Pirogov <anton.pirogov@gmail.com>
|
||||
Anton Pirogov <anton.pirogov@gmail.com> anton.pirogov at gmail . com <unknown>
|
||||
Arjun Comar <nrujac@gmail.com>
|
||||
Audun Skaugen <audun@skaugen.name> <audunskaugen@gmail.com>
|
||||
Bas van Dijk <v.dijk.bas@gmail.com>
|
||||
Ben Boeckel <mathstuf@gmail.com>
|
||||
Ben Weitzman <benweitzman@gmail.com>
|
||||
Bogdan Sinitsyn <bogdan.sinitsyn@gmail.com>
|
||||
Brandon S Allbery KF8NH <allbery.b@gmail.com>
|
||||
Brandon S Allbery KF8NH <allbery.b@gmail.com> <allbery@ece.cmu.edu>
|
||||
Brent Yorgey <byorgey@gmail.com> <byorgey@cis.upenn.edu>
|
||||
Carlos Lopez-Camey <c.lopez@kmels.net>
|
||||
Carsten Otto <xmonad@c-otto.de>
|
||||
Christian Dietrich <stettberger@dokucode.de>
|
||||
Christian Wills <cwills.dev@gmail.com>
|
||||
Daniel Neri <daniel.neri@sigicom.com> <daniel.neri@sigicom.se>
|
||||
Daniel Schoepe <daniel.schoepe@googlemail.com> <asgaroth_@gmx.de>
|
||||
Daniel Schoepe <daniel.schoepe@googlemail.com> <daniel.schoepe@gmail.com>
|
||||
Daniel Wagner <me@dmwit.com> <daniel@wagner-home.com>
|
||||
Dave Harrison <dave@nullcube.com>
|
||||
David Glasser <glasser@mit.edu>
|
||||
David McLean <gopsychonauts@gmail.com>
|
||||
Devin Mullins <devin.mullins@gmail.com> <me@twifkak.com>
|
||||
Dominik Bruhn <dominik@dbruhn.de>
|
||||
Don Stewart <dons00@gmail.com> <dons@cse.unsw.edu.au>
|
||||
Don Stewart <dons00@gmail.com> <dons@galois.com>
|
||||
Edward Z. Yang <ezyang@cs.stanford.edu>
|
||||
Gwern Branwen <gwern@gwern.net>
|
||||
Gwern Branwen <gwern@gwern.net> <gwern0@gmail.com>
|
||||
Henrique Abreu <hgabreu@gmail.com>
|
||||
Ilya Portnov <portnov84@rambler.ru>
|
||||
intrigeri <intrigeri@boum.org>
|
||||
Ivan Miljenovic <Ivan.Miljenovic@gmail.com>
|
||||
Jan-David Quesel <quesel@informatik.uni-oldenburg.de>
|
||||
Jens Petersen <juhp@community.haskell.org> <petersen@haskell.org>
|
||||
Jeremy Apthorp <nornagon@gmail.com>
|
||||
Joachim Breitner <mail@joachim-breitner.de>
|
||||
Joachim Fasting <joachim.fasting@gmail.com>
|
||||
Joel Suovaniemi <joel.suovaniemi@iki.fi>
|
||||
Joe Thornber <joe.thornber@gmail.com>
|
||||
Johann Giwer <johanngiwer@web.de>
|
||||
Jussi Maki <joamaki@gmail.com>
|
||||
Konstantin Sobolev <konstantin.sobolev@gmail.com>
|
||||
Lanny Ripple <lan3ny@gmail.com>
|
||||
Lei Chen <linxray@gmail.com>
|
||||
Leonardo Serra <leoserra@minaslivre.org>
|
||||
Luis Cabellos <zhen.sydow@gmail.com>
|
||||
Lukas Mai <l.mai@web.de>
|
||||
Mario Pastorelli <pastorelli.mario@gmail.com>
|
||||
Mathias Stearn <redbeard0531@gmail.com>
|
||||
Matt Brown <deadguysfrom@gmail.com>
|
||||
Matthew Hague <matthewhague@zoho.com>
|
||||
Nathaniel Filardo <nwfilardo@gmail.com>
|
||||
Nelson Elhage <nelhage@mit.edu>
|
||||
Nicolas Dudebout <nicolas.dudebout@gatech.edu>
|
||||
Nicolas Pouillard <nicolas.pouillard@gmail.com>
|
||||
Nils Schweinsberg <mail@n-sch.de>
|
||||
Norbert Zeh <nzeh@cs.dal.ca>
|
||||
Peter Olson <polson2@hawk.iit.edu>
|
||||
Quentin Moser <moserq@gmail.com>
|
||||
Quentin Moser <quentin.moser@unifr.ch>
|
||||
Rickard Gustafsson <acura@allyourbase.se>
|
||||
Robert Marlow <bobstopper@bobturf.org>
|
||||
Robert Marlow <bobstopper@bobturf.org> <robreim@bobturf.org>
|
||||
Rohan Jain <crodjer@gmail.com>
|
||||
Sibi Prabakaran <sibi@psibi.in> <psibi2000@gmail.com>
|
||||
Sean Escriva <sean.escriva@gmail.com>
|
||||
Sean McEligot <seanmce33@gmail.com>
|
||||
Spencer Janssen <spencerjanssen@gmail.com> <sjanssen@cse.unl.edu>
|
||||
Tomohiro Matsuyama <matsuyama3@ariel-networks.com>
|
||||
Tom Rauchenwald <its.sec@gmx.net>
|
||||
Tony Morris <haskell@tmorris.net>
|
||||
Valery V. Vorotyntsev <valery.vv@gmail.com>
|
||||
Will Farrington <wcfarrington@gmail.com>
|
||||
Wirt Wolff <wirtwolff@gmail.com>
|
||||
Yaakov Nemoy <loupgaroublond@gmail.com>
|
||||
|
||||
brian <brian@lorf.org>
|
||||
cardboard42 <cardboard42@gmail.com>
|
||||
daedalusinfinity <daedalusinfinity@gmail.com>
|
||||
hexago.nl <xmonad-contrib@hexago.nl>
|
||||
intrigeri <intrigeri@boum.org>
|
||||
jakob <jakob@pipefour.org>
|
||||
kedals0 <kedals0@gmail.com>
|
||||
lithis <xmonad@selg.hethrael.org>
|
||||
lithis <xmonad@selg.hethrael.org> <xmonad@s001.hethrael.com>
|
||||
longpoke <longpoke@gmail.com>
|
||||
md143rbh7f <md143rbh7f@gmail.com>
|
||||
perlkat <perlkat@katspace.org>
|
||||
rupa <rupa@lrrr.us> <rupa@lrrr.us>
|
||||
timthelion <tim.thelion@gmail.com>
|
||||
|
||||
# for core only
|
||||
Neil Mitchell <http://www.cs.york.ac.uk/~ndm/>, Neil Mitchell
|
||||
Nick Burlett <nickburlett@mac.com>
|
||||
Sam Hughes <hughes@rpi.edu>
|
||||
Shae Erisson <shae@ScannedInAvian.com>
|
||||
Conrad Irwin <conrad.irwin@gmail.com>
|
@@ -31,6 +31,9 @@ before_install:
|
||||
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
||||
|
||||
install:
|
||||
# build xmonad from HEAD
|
||||
- git clone https://github.com/xmonad/xmonad.git
|
||||
|
||||
- cabal --version
|
||||
- echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
|
||||
- if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];
|
||||
@@ -66,6 +69,8 @@ install:
|
||||
cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
|
||||
fi
|
||||
|
||||
- cabal install xmonad/
|
||||
|
||||
# Here starts the actual work to be performed for the package under test;
|
||||
# any command which exits with a non-zero exit code causes the build to fail.
|
||||
script:
|
||||
@@ -73,7 +78,8 @@ script:
|
||||
- cabal configure --enable-tests --enable-benchmarks -v2 # -v2 provides useful information for debugging
|
||||
- cabal build # this builds all libraries and executables (including tests/benchmarks)
|
||||
- cabal test
|
||||
- cabal check
|
||||
# - cabal check # complains about -Werror even though it is
|
||||
# hidden behind a manual flag with default false
|
||||
- cabal sdist # tests that a source-distribution can be generated
|
||||
|
||||
# Check that the resulting source distribution can be built & installed.
|
||||
|
59
CHANGES.md
59
CHANGES.md
@@ -1,5 +1,62 @@
|
||||
# Change Log / Release Notes
|
||||
|
||||
## 0.13
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The type of `completionKey` (of `XPConfig` record) has been
|
||||
changed from `KeySym` to `(KeyMask, KeySym)`. The default value
|
||||
for this is still binded to `Tab` key.
|
||||
|
||||
* New constructor `CenteredAt Rational Rational` added for
|
||||
`XMonad.Prompt.XPPosition`.
|
||||
|
||||
### New Modules
|
||||
|
||||
* `XMonad.Layout.SortedLayout`
|
||||
|
||||
A new LayoutModifier that sorts a given layout by a list of
|
||||
properties. The order of properties in the list determines
|
||||
the order of windows in the final layout. Any unmatched windows
|
||||
go to the end of the order.
|
||||
|
||||
* `XMonad.Prompt.Unicode`
|
||||
|
||||
A prompt to search a unicode character by its name, and put it into the
|
||||
clipboard.
|
||||
|
||||
* `XMonad.Util.Ungrab`
|
||||
|
||||
Release xmonad's keyboard and pointer grabs immediately, so
|
||||
screen grabbers and lock utilities, etc. will work. Replaces
|
||||
the short sleep hackaround.
|
||||
|
||||
* `XMonad.Util.Loggers.NamedScratchpad`
|
||||
|
||||
A collection of Loggers (see `XMonad.Util.Loggers`) for NamedScratchpads
|
||||
(see `XMonad.Util.NamedScratchpad`).
|
||||
|
||||
* `XMonad.Util.NoTaskbar`
|
||||
|
||||
Utility function and `ManageHook` to mark a window to be ignored by
|
||||
EWMH taskbars and pagers. Useful for `NamedScratchpad` windows, since
|
||||
you will usually be taken to the `NSP` workspace by them.
|
||||
|
||||
### Minor Changes
|
||||
|
||||
* `XMonad.Layout.LayoutBuilder`
|
||||
|
||||
Merge all functionality from `XMonad.Layout.LayoutBuilderP` into
|
||||
`XMonad.Layout.LayoutBuilder`.
|
||||
|
||||
* `XMonad.Actions.DynamicProjects`
|
||||
|
||||
- Switching away from a dynamic project that contains no windows
|
||||
automatically deletes that project's workspace.
|
||||
|
||||
The project itself was already being deleted, this just deletes
|
||||
the workspace created for it as well.
|
||||
|
||||
## 0.12 (December 14, 2015)
|
||||
|
||||
### Breaking Changes
|
||||
@@ -12,7 +69,7 @@
|
||||
| < 0.12 | >= 0.12 |
|
||||
|-------------------------------------|----------------------------------|
|
||||
| `updatePointer Nearest` | `updatePointer (0.5, 0.5) (1,1)` |
|
||||
| `updatePointer (Relative x y)` | `updatePointer (x,y) (1,1)` |
|
||||
| `updatePointer (Relative x y)` | `updatePointer (x,y) (0,0)` |
|
||||
| `updatePointer (TowardsCentre x y)` | `updatePointer (0.5,0.5) (x,y)` |
|
||||
|
||||
### New Modules
|
||||
|
@@ -146,21 +146,21 @@ instance ExtensionClass ProjectState where
|
||||
-- | Add dynamic projects support to the given config.
|
||||
dynamicProjects :: [Project] -> XConfig a -> XConfig a
|
||||
dynamicProjects ps c =
|
||||
c { startupHook = startupHook c <> dynamicProjectsStartupHook ps
|
||||
, logHook = logHook c <> dynamicProjectsLogHook
|
||||
c { startupHook = dynamicProjectsStartupHook ps <> startupHook c
|
||||
, logHook = dynamicProjectsLogHook <> logHook c
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Log hook for tracking workspace changes.
|
||||
dynamicProjectsLogHook :: X ()
|
||||
dynamicProjectsLogHook = do
|
||||
name <- gets (W.tag . W.workspace . W.current . windowset)
|
||||
state <- XS.get
|
||||
name <- gets (W.tag . W.workspace . W.current . windowset)
|
||||
xstate <- XS.get
|
||||
|
||||
unless (Just name == previousProject state) $ do
|
||||
XS.put (state {previousProject = Just name})
|
||||
unless (Just name == previousProject xstate) $ do
|
||||
XS.put (xstate {previousProject = Just name})
|
||||
activateProject . fromMaybe (defProject name) $
|
||||
Map.lookup name (projects state)
|
||||
Map.lookup name (projects xstate)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Start-up hook for recording configured projects.
|
||||
@@ -210,7 +210,8 @@ switchProject p = do
|
||||
|
||||
-- If the project we are switching away from has no windows, and
|
||||
-- it's a dynamic project, remove it from the configuration.
|
||||
when (null ws && isNothing (projectStartHook oldp)) $
|
||||
when (null ws && isNothing (projectStartHook oldp)) $ do
|
||||
removeWorkspaceByTag name -- also remove the old workspace
|
||||
XS.modify (\s -> s {projects = Map.delete name $ projects s})
|
||||
|
||||
appendWorkspace (projectName p)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.DynamicWorkspaces
|
||||
@@ -21,13 +23,16 @@ module XMonad.Actions.DynamicWorkspaces (
|
||||
removeWorkspace,
|
||||
removeWorkspaceByTag,
|
||||
removeEmptyWorkspace,
|
||||
removeEmptyWorkspaceByTag,
|
||||
removeEmptyWorkspaceAfter,
|
||||
removeEmptyWorkspaceAfterExcept,
|
||||
addHiddenWorkspace, addHiddenWorkspaceAt,
|
||||
withWorkspace,
|
||||
selectWorkspace, renameWorkspace,
|
||||
renameWorkspaceByName,
|
||||
toNthWorkspace, withNthWorkspace
|
||||
toNthWorkspace, withNthWorkspace,
|
||||
setWorkspaceIndex, withWorkspaceIndex,
|
||||
WorkspaceIndex
|
||||
) where
|
||||
|
||||
import XMonad hiding (workspaces)
|
||||
@@ -38,6 +43,8 @@ import XMonad.Util.WorkspaceCompare ( getSortByIndex )
|
||||
import Data.List (find)
|
||||
import Data.Maybe (isNothing)
|
||||
import Control.Monad (when)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
||||
@@ -53,17 +60,55 @@ import Control.Monad (when)
|
||||
-- > , ((modm .|. shiftMask, xK_m ), withWorkspace def (windows . copy))
|
||||
-- > , ((modm .|. shiftMask, xK_r ), renameWorkspace def)
|
||||
--
|
||||
-- > -- mod-[1..9] %! Switch to workspace N
|
||||
-- > -- mod-shift-[1..9] %! Move client to workspace N
|
||||
-- > -- mod-[1..9] %! Switch to workspace N in the list of workspaces
|
||||
-- > -- mod-shift-[1..9] %! Move client to workspace N in the list of workspaces
|
||||
-- > ++
|
||||
-- > zip (zip (repeat (modm)) [xK_1..xK_9]) (map (withNthWorkspace W.greedyView) [0..])
|
||||
-- > ++
|
||||
-- > zip (zip (repeat (modm .|. shiftMask)) [xK_1..xK_9]) (map (withNthWorkspace W.shift) [0..])
|
||||
--
|
||||
-- Alternatively, you can associate indexes (which don't depend of the
|
||||
-- workspace list order) to workspaces by using following keybindings:
|
||||
--
|
||||
-- > -- mod-[1..9] %! Switch to workspace of index N
|
||||
-- > -- mod-control-[1..9] %! Set index N to the current workspace
|
||||
-- > ++
|
||||
-- > zip (zip (repeat (modm)) [xK_1..xK_9]) (map (withWorkspaceIndex W.greedyView) [1..])
|
||||
-- > ++
|
||||
-- > zip (zip (repeat (modm .|. controlMask)) [xK_1..xK_9]) (map (setWorkspaceIndex) [1..])
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings". See also the documentation for
|
||||
-- "XMonad.Actions.CopyWindow", 'windows', 'shift', and 'XPConfig'.
|
||||
|
||||
type WorkspaceTag = String
|
||||
-- | The workspace index is mapped to a workspace tag by the user and
|
||||
-- can be updated.
|
||||
type WorkspaceIndex = Int
|
||||
|
||||
-- | Internal dynamic project state that stores a mapping between
|
||||
-- workspace indexes and workspace tags.
|
||||
data DynamicWorkspaceState = DynamicWorkspaceState {workspaceIndexMap :: Map.Map WorkspaceIndex WorkspaceTag}
|
||||
deriving (Typeable, Read, Show)
|
||||
|
||||
instance ExtensionClass DynamicWorkspaceState where
|
||||
initialValue = DynamicWorkspaceState Map.empty
|
||||
extensionType = PersistentExtension
|
||||
|
||||
-- | Set the index of the current workspace.
|
||||
setWorkspaceIndex :: WorkspaceIndex -> X ()
|
||||
setWorkspaceIndex widx = do
|
||||
wtag <- gets (currentTag . windowset)
|
||||
wmap <- XS.gets workspaceIndexMap
|
||||
XS.modify $ \s -> s {workspaceIndexMap = Map.insert widx wtag wmap}
|
||||
|
||||
withWorkspaceIndex :: (String -> WindowSet -> WindowSet) -> WorkspaceIndex -> X ()
|
||||
withWorkspaceIndex job widx = do
|
||||
wtag <- ilookup widx
|
||||
maybe (return ()) (windows . job) wtag
|
||||
where
|
||||
ilookup :: WorkspaceIndex -> X (Maybe WorkspaceTag)
|
||||
ilookup idx = Map.lookup idx `fmap` XS.gets workspaceIndexMap
|
||||
|
||||
|
||||
mkCompl :: [String] -> String -> IO [String]
|
||||
@@ -81,10 +126,15 @@ renameWorkspace :: XPConfig -> X ()
|
||||
renameWorkspace conf = workspacePrompt conf renameWorkspaceByName
|
||||
|
||||
renameWorkspaceByName :: String -> X ()
|
||||
renameWorkspaceByName w = windows $ \s -> let sett wk = wk { tag = w }
|
||||
setscr scr = scr { workspace = sett $ workspace scr }
|
||||
sets q = q { current = setscr $ current q }
|
||||
in sets $ removeWorkspace' w s
|
||||
renameWorkspaceByName w = do old <- gets (currentTag . windowset)
|
||||
windows $ \s -> let sett wk = wk { tag = w }
|
||||
setscr scr = scr { workspace = sett $ workspace scr }
|
||||
sets q = q { current = setscr $ current q }
|
||||
in sets $ removeWorkspace' w s
|
||||
updateIndexMap old w
|
||||
where updateIndexMap old new = do
|
||||
wmap <- XS.gets workspaceIndexMap
|
||||
XS.modify $ \s -> s {workspaceIndexMap = Map.map (\t -> if t == old then new else t) wmap}
|
||||
|
||||
toNthWorkspace :: (String -> X ()) -> Int -> X ()
|
||||
toNthWorkspace job wnum = do sort <- getSortByIndex
|
||||
@@ -128,7 +178,7 @@ addWorkspaceAt add newtag = addHiddenWorkspaceAt add newtag >> windows (greedyVi
|
||||
addWorkspacePrompt :: XPConfig -> X ()
|
||||
addWorkspacePrompt conf = mkXPrompt (Wor "New workspace name: ") conf (const (return [])) addWorkspace
|
||||
|
||||
-- | Prompt for the name of a new workspace, appending it to the end of the list of workspaces
|
||||
-- | Prompt for the name of a new workspace, appending it to the end of the list of workspaces
|
||||
-- if it does not already exist, and switch to it.
|
||||
appendWorkspacePrompt :: XPConfig -> X ()
|
||||
appendWorkspacePrompt conf = mkXPrompt (Wor "New workspace name: ") conf (const (return [])) appendWorkspace
|
||||
|
@@ -14,7 +14,8 @@
|
||||
module XMonad.Actions.FocusNth (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
focusNth,focusNth') where
|
||||
focusNth,focusNth',
|
||||
swapNth,swapNth') where
|
||||
|
||||
import XMonad.StackSet
|
||||
import XMonad
|
||||
@@ -41,6 +42,17 @@ focusNth' :: Int -> Stack a -> Stack a
|
||||
focusNth' n s@(Stack _ ls rs) | (n < 0) || (n > length(ls) + length(rs)) = s
|
||||
| otherwise = listToStack n (integrate s)
|
||||
|
||||
-- | Swap current window with nth. Focus stays in the same position
|
||||
swapNth :: Int -> X ()
|
||||
swapNth = windows . modify' . swapNth'
|
||||
|
||||
swapNth' :: Int -> Stack a -> Stack a
|
||||
swapNth' n s@(Stack c l r)
|
||||
| (n < 0) || (n > length l + length r) || (n == length l) = s
|
||||
| n < length l = let (nl, nc:nr) = splitAt (length l - n - 1) l in Stack nc (nl ++ c : nr) r
|
||||
| otherwise = let (nl, nc:nr) = splitAt (n - length l - 1) r in Stack nc l (nl ++ c : nr)
|
||||
|
||||
|
||||
listToStack :: Int -> [a] -> Stack a
|
||||
listToStack n l = Stack t ls rs
|
||||
where
|
||||
|
@@ -653,7 +653,7 @@ gridselect gsconfig elements =
|
||||
liftIO $ mapWindow dpy win
|
||||
liftIO $ selectInput dpy win (exposureMask .|. keyPressMask .|. buttonReleaseMask)
|
||||
status <- io $ grabKeyboard dpy win True grabModeAsync grabModeAsync currentTime
|
||||
io $ grabButton dpy button1 anyModifier win True buttonReleaseMask grabModeAsync grabModeAsync none none
|
||||
io $ grabPointer dpy win True buttonReleaseMask grabModeAsync grabModeAsync none none currentTime
|
||||
font <- initXMF (gs_font gsconfig)
|
||||
let screenWidth = toInteger $ rect_width scr
|
||||
screenHeight = toInteger $ rect_height scr
|
||||
@@ -682,6 +682,7 @@ gridselect gsconfig elements =
|
||||
liftIO $ do
|
||||
unmapWindow dpy win
|
||||
destroyWindow dpy win
|
||||
ungrabPointer dpy currentTime
|
||||
sync dpy False
|
||||
releaseXMF font
|
||||
return selectedElement
|
||||
|
@@ -43,6 +43,7 @@ module XMonad.Actions.Navigation2D ( -- * Usage
|
||||
, Navigation2D
|
||||
, lineNavigation
|
||||
, centerNavigation
|
||||
, hybridNavigation
|
||||
, fullScreenRect
|
||||
, singleWindowRect
|
||||
, switchLayer
|
||||
@@ -74,10 +75,11 @@ import XMonad.Util.Types
|
||||
-- natural but may make it impossible to navigate to a given window from the
|
||||
-- current window, particularly in the floating layer. /Center navigation/
|
||||
-- feels less natural in certain situations but ensures that all windows can be
|
||||
-- reached without the need to involve the mouse. Navigation2D allows different
|
||||
-- navigation strategies to be used in the two layers and allows customization
|
||||
-- of the navigation strategy for the tiled layer based on the layout currently
|
||||
-- in effect.
|
||||
-- reached without the need to involve the mouse. A third option is to use
|
||||
-- /Hybrid navigation/, which automatically chooses between the two whenever
|
||||
-- navigation is attempted. Navigation2D allows different navigation strategies
|
||||
-- to be used in the two layers and allows customization of the navigation strategy
|
||||
-- for the tiled layer based on the layout currently in effect.
|
||||
--
|
||||
-- You can use this module with (a subset of) the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
@@ -316,6 +318,13 @@ lineNavigation = N 1 doLineNavigation
|
||||
centerNavigation :: Navigation2D
|
||||
centerNavigation = N 2 doCenterNavigation
|
||||
|
||||
-- | Hybrid navigation. This attempts Line navigation, then falls back on Center
|
||||
-- navigation if it does not find any suitable target windows. This is useful since
|
||||
-- Line navigation tends to fail on gaps, but provides more intuitive motions
|
||||
-- when it succeeds—provided there are no floating windows.
|
||||
hybridNavigation :: Navigation2D
|
||||
hybridNavigation = N 2 doHybridNavigation
|
||||
|
||||
-- | Stores the configuration of directional navigation. The 'Default' instance
|
||||
-- uses line navigation for the tiled layer and for navigation between screens,
|
||||
-- and center navigation for the float layer. No custom navigation strategies
|
||||
@@ -758,6 +767,13 @@ doCenterNavigation dir (cur, rect) winrects
|
||||
-- or it has the same distance but comes later
|
||||
-- in the window stack
|
||||
|
||||
-- | Implements Hybrid navigation. This attempts Line navigation first,
|
||||
-- then falls back on Center navigation if it finds no suitable target window.
|
||||
doHybridNavigation :: Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
|
||||
doHybridNavigation = applyToBoth (<|>) doLineNavigation doCenterNavigation
|
||||
where
|
||||
applyToBoth f g h a b c = f (g a b c) (h a b c)
|
||||
|
||||
-- | Swaps the current window with the window given as argument
|
||||
swap :: Window -> WindowSet -> WindowSet
|
||||
swap win winset = W.focusWindow cur
|
||||
|
@@ -284,7 +284,7 @@ searchEngineF = SearchEngine
|
||||
amazon, alpha, codesearch, deb, debbts, debpts, dictionary, google, hackage, hoogle,
|
||||
images, imdb, isohunt, lucky, maps, mathworld, openstreetmap, scholar, stackage, thesaurus, vocabulary, wayback, wikipedia, wiktionary,
|
||||
youtube, duckduckgo :: SearchEngine
|
||||
amazon = searchEngine "amazon" "http://www.amazon.com/exec/obidos/external-search?index=all&keyword="
|
||||
amazon = searchEngine "amazon" "http://www.amazon.com/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords="
|
||||
alpha = searchEngine "alpha" "http://www.wolframalpha.com/input/?i="
|
||||
codesearch = searchEngine "codesearch" "http://www.google.com/codesearch?q="
|
||||
deb = searchEngine "deb" "http://packages.debian.org/"
|
||||
|
@@ -16,9 +16,11 @@ module XMonad.Actions.Submap (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
submap,
|
||||
submapDefault
|
||||
submapDefault,
|
||||
submapDefaultWithKey
|
||||
) where
|
||||
import Data.Bits
|
||||
import Data.Maybe (fromMaybe)
|
||||
import XMonad hiding (keys)
|
||||
import qualified Data.Map as M
|
||||
import Control.Monad.Fix (fix)
|
||||
@@ -58,11 +60,18 @@ For detailed instructions on editing your key bindings, see
|
||||
-- corresponding action, or does nothing if the key is not found in
|
||||
-- the map.
|
||||
submap :: M.Map (KeyMask, KeySym) (X ()) -> X ()
|
||||
submap keys = submapDefault (return ()) keys
|
||||
submap = submapDefault (return ())
|
||||
|
||||
-- | Like 'submap', but executes a default action if the key did not match.
|
||||
submapDefault :: X () -> M.Map (KeyMask, KeySym) (X ()) -> X ()
|
||||
submapDefault defAction keys = do
|
||||
submapDefault = submapDefaultWithKey . const
|
||||
|
||||
-- | Like 'submapDefault', but sends the unmatched key to the default
|
||||
-- action as argument.
|
||||
submapDefaultWithKey :: ((KeyMask, KeySym) -> X ())
|
||||
-> M.Map (KeyMask, KeySym) (X ())
|
||||
-> X ()
|
||||
submapDefaultWithKey defAction keys = do
|
||||
XConf { theRoot = root, display = d } <- ask
|
||||
|
||||
io $ grabKeyboard d root False grabModeAsync grabModeAsync currentTime
|
||||
@@ -79,4 +88,4 @@ submapDefault defAction keys = do
|
||||
|
||||
io $ ungrabKeyboard d currentTime
|
||||
|
||||
maybe defAction id (M.lookup (m', s) keys)
|
||||
fromMaybe (defAction (m', s)) (M.lookup (m', s) keys)
|
||||
|
657
XMonad/Actions/TreeSelect.hs
Normal file
657
XMonad/Actions/TreeSelect.hs
Normal file
@@ -0,0 +1,657 @@
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.TreeSelect
|
||||
-- Copyright : (c) Tom Smeets <tom.tsmeets@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Tom Smeets <tom.tsmeets@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
--
|
||||
-- TreeSelect displays your workspaces or actions in a Tree-like format.
|
||||
-- You can select the desired workspace/action with the cursor or hjkl keys.
|
||||
--
|
||||
-- This module is fully configurable and very useful if you like to have a
|
||||
-- lot of workspaces.
|
||||
--
|
||||
-- Only the nodes up to the currently selected are displayed.
|
||||
-- This will be configurable in the near future by changing 'ts_hidechildren' to @False@, this is not yet implemented.
|
||||
--
|
||||
-- <<https://wiki.haskell.org/wikiupload/thumb/0/0b/Treeselect-Workspace.png/800px-Treeselect-Workspace.png>>
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
module XMonad.Actions.TreeSelect
|
||||
(
|
||||
-- * Usage
|
||||
-- $usage
|
||||
treeselectWorkspace
|
||||
, toWorkspaces
|
||||
, treeselectAction
|
||||
|
||||
-- * Configuring
|
||||
-- $config
|
||||
, Pixel
|
||||
-- $pixel
|
||||
|
||||
, TSConfig(..)
|
||||
, tsDefaultConfig
|
||||
|
||||
-- * Navigation
|
||||
-- $navigation
|
||||
, defaultNavigation
|
||||
, select
|
||||
, cancel
|
||||
, moveParent
|
||||
, moveChild
|
||||
, moveNext
|
||||
, movePrev
|
||||
, moveHistBack
|
||||
, moveHistForward
|
||||
, moveTo
|
||||
|
||||
-- * Advanced usage
|
||||
-- $advusage
|
||||
, TSNode(..)
|
||||
, treeselect
|
||||
, treeselectAt
|
||||
) where
|
||||
|
||||
import Control.Applicative
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.State
|
||||
import Data.List (find)
|
||||
import Data.Maybe
|
||||
import Data.Tree
|
||||
import Foreign
|
||||
import System.IO
|
||||
import System.Posix.Process (forkProcess, executeFile)
|
||||
import XMonad hiding (liftX)
|
||||
import XMonad.StackSet as W
|
||||
import XMonad.Util.Font
|
||||
import XMonad.Util.NamedWindows
|
||||
import XMonad.Util.TreeZipper
|
||||
import XMonad.Hooks.WorkspaceHistory
|
||||
import qualified Data.Map as M
|
||||
|
||||
#ifdef XFT
|
||||
import Graphics.X11.Xft
|
||||
import Graphics.X11.Xrender
|
||||
#endif
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- These imports are used in the following example
|
||||
--
|
||||
-- > import Data.Tree
|
||||
-- > import XMonad.Actions.TreeSelect
|
||||
-- > import XMonad.Hooks.WorkspaceHistory
|
||||
-- > import qualified XMonad.StackSet as W
|
||||
--
|
||||
-- For selecting Workspaces, you need to define them in a tree structure using 'Data.Tree.Node' instead of just a standard list
|
||||
--
|
||||
-- Here is an example workspace-tree
|
||||
--
|
||||
-- > myWorkspaces :: Forest String
|
||||
-- > myWorkspaces = [ Node "Browser" [] -- a workspace for your browser
|
||||
-- > , Node "Home" -- for everyday activity's
|
||||
-- > [ Node "1" [] -- with 4 extra sub-workspaces, for even more activity's
|
||||
-- > , Node "2" []
|
||||
-- > , Node "3" []
|
||||
-- > , Node "4" []
|
||||
-- > ]
|
||||
-- > , Node "Programming" -- for all your programming needs
|
||||
-- > [ Node "Haskell" []
|
||||
-- > , Node "Docs" [] -- documentation
|
||||
-- > ]
|
||||
-- > ]
|
||||
--
|
||||
-- Then add it to your 'XMonad.Core.workspaces' using the 'toWorkspaces' function.
|
||||
--
|
||||
-- Optionally, if you add 'workspaceHistoryHook' to your 'logHook' you can use the \'o\' and \'i\' keys to select from previously-visited workspaces
|
||||
--
|
||||
-- > xmonad $ defaultConfig { ...
|
||||
-- > , workspaces = toWorkspaces myWorkspaces
|
||||
-- > , logHook = workspaceHistoryHook
|
||||
-- > }
|
||||
--
|
||||
-- After that you still need to bind buttons to 'treeselectWorkspace' to start selecting a workspaces and moving windows
|
||||
--
|
||||
-- you could bind @Mod-f@ to switch workspace
|
||||
--
|
||||
-- > , ((modMask, xK_f), treeselectWorkspace myTreeConf myWorkspaces W.greedyView)
|
||||
--
|
||||
-- and bind @Mod-Shift-f@ to moving the focused windows to a workspace
|
||||
--
|
||||
-- > , ((modMask .|. shiftMask, xK_f), treeselectWorkspace myTreeConf myWorkspaces W.shift)
|
||||
|
||||
-- $config
|
||||
-- The selection menu is very configurable, you can change the font, all colors and the sizes of the boxes.
|
||||
--
|
||||
-- The default config defined as 'tsDefaultConfig'
|
||||
--
|
||||
-- > tsDefaultConfig = TSConfig { ts_hidechildren = True
|
||||
-- > , ts_background = 0xc0c0c0c0
|
||||
-- > , ts_font = "xft:Sans-16"
|
||||
-- > , ts_node = (0xff000000, 0xff50d0db)
|
||||
-- > , ts_nodealt = (0xff000000, 0xff10b8d6)
|
||||
-- > , ts_highlight = (0xffffffff, 0xffff0000)
|
||||
-- > , ts_extra = 0xff000000
|
||||
-- > , ts_node_width = 200
|
||||
-- > , ts_node_height = 30
|
||||
-- > , ts_originX = 0
|
||||
-- > , ts_originY = 0
|
||||
-- > , ts_indent = 80
|
||||
-- > , ts_navigate = defaultNavigation
|
||||
-- > }
|
||||
|
||||
-- $pixel
|
||||
--
|
||||
-- The 'Pixel' Color format is in the form of @0xaarrggbb@
|
||||
--
|
||||
-- Note that transparency is only supported if you have a window compositor running like <https://github.com/chjj/compton compton>
|
||||
--
|
||||
-- Some Examples:
|
||||
--
|
||||
-- @
|
||||
-- white = 0xffffffff
|
||||
-- black = 0xff000000
|
||||
-- red = 0xffff0000
|
||||
-- blue = 0xff00ff00
|
||||
-- green = 0xff0000ff
|
||||
-- transparent = 0x00000000
|
||||
-- @
|
||||
|
||||
-- $navigation
|
||||
--
|
||||
-- Keybindings for navigations can also be modified
|
||||
--
|
||||
-- This is the definition of 'defaultNavigation'
|
||||
--
|
||||
-- > defaultNavigation :: M.Map (KeyMask, KeySym) (TreeSelect a (Maybe a))
|
||||
-- > defaultNavigation = M.fromList
|
||||
-- > [ ((0, xK_Escape), cancel)
|
||||
-- > , ((0, xK_Return), select)
|
||||
-- > , ((0, xK_space), select)
|
||||
-- > , ((0, xK_Up), movePrev)
|
||||
-- > , ((0, xK_Down), moveNext)
|
||||
-- > , ((0, xK_Left), moveParent)
|
||||
-- > , ((0, xK_Right), moveChild)
|
||||
-- > , ((0, xK_k), movePrev)
|
||||
-- > , ((0, xK_j), moveNext)
|
||||
-- > , ((0, xK_h), moveParent)
|
||||
-- > , ((0, xK_l), moveChild)
|
||||
-- > , ((0, xK_o), moveHistBack)
|
||||
-- > , ((0, xK_i), moveHistForward)
|
||||
-- > ]
|
||||
|
||||
-- $advusage
|
||||
-- This module can also be used to select any other action
|
||||
|
||||
-- | Extensive configuration for displaying the tree.
|
||||
--
|
||||
-- This class also has a 'Default' instance
|
||||
data TSConfig a = TSConfig { ts_hidechildren :: Bool -- ^ when enabled, only the parents (and their first children) of the current node will be shown (This feature is not yet implemented!)
|
||||
, ts_background :: Pixel -- ^ background color filling the entire screen.
|
||||
|
||||
, ts_font :: String -- ^ XMF font for drawing the node name extra text
|
||||
|
||||
, ts_node :: (Pixel, Pixel) -- ^ node foreground (text) and background color when not selected
|
||||
, ts_nodealt :: (Pixel, Pixel) -- ^ every other node will use this color instead of 'ts_node'
|
||||
, ts_highlight :: (Pixel, Pixel) -- ^ node foreground (text) and background color when selected
|
||||
|
||||
, ts_extra :: Pixel -- ^ extra text color
|
||||
|
||||
, ts_node_width :: Int -- ^ node width in pixels
|
||||
, ts_node_height :: Int -- ^ node height in pixels
|
||||
, ts_originX :: Int -- ^ tree X position on the screen in pixels
|
||||
, ts_originY :: Int -- ^ tree Y position on the screen in pixels
|
||||
|
||||
, ts_indent :: Int -- ^ indentation amount for each level in pixels
|
||||
|
||||
, ts_navigate :: M.Map (KeyMask, KeySym) (TreeSelect a (Maybe a)) -- ^ key bindings for navigating the tree
|
||||
}
|
||||
|
||||
instance Default (TSConfig a) where
|
||||
def = TSConfig { ts_hidechildren = True
|
||||
, ts_background = 0xc0c0c0c0
|
||||
, ts_font = "xft:Sans-16"
|
||||
, ts_node = (0xff000000, 0xff50d0db)
|
||||
, ts_nodealt = (0xff000000, 0xff10b8d6)
|
||||
, ts_highlight = (0xffffffff, 0xffff0000)
|
||||
, ts_extra = 0xff000000
|
||||
, ts_node_width = 200
|
||||
, ts_node_height = 30
|
||||
, ts_originX = 0
|
||||
, ts_originY = 0
|
||||
, ts_indent = 80
|
||||
, ts_navigate = defaultNavigation
|
||||
}
|
||||
|
||||
-- | Default navigation
|
||||
--
|
||||
-- * navigation using either arrow key or vi style hjkl
|
||||
-- * Return or Space to confirm
|
||||
-- * Escape or Backspace to cancel to
|
||||
defaultNavigation :: M.Map (KeyMask, KeySym) (TreeSelect a (Maybe a))
|
||||
defaultNavigation = M.fromList
|
||||
[ ((0, xK_Escape), cancel)
|
||||
, ((0, xK_Return), select)
|
||||
, ((0, xK_space), select)
|
||||
, ((0, xK_Up), movePrev)
|
||||
, ((0, xK_Down), moveNext)
|
||||
, ((0, xK_Left), moveParent)
|
||||
, ((0, xK_Right), moveChild)
|
||||
, ((0, xK_k), movePrev)
|
||||
, ((0, xK_j), moveNext)
|
||||
, ((0, xK_h), moveParent)
|
||||
, ((0, xK_l), moveChild)
|
||||
, ((0, xK_o), moveHistBack)
|
||||
, ((0, xK_i), moveHistForward)
|
||||
]
|
||||
|
||||
-- | Default configuration.
|
||||
--
|
||||
-- Using nice alternating blue nodes
|
||||
tsDefaultConfig :: TSConfig a
|
||||
tsDefaultConfig = def
|
||||
|
||||
-- | Tree Node With a name and extra text
|
||||
data TSNode a = TSNode { tsn_name :: String
|
||||
, tsn_extra :: String -- ^ extra text, displayed next to the node name
|
||||
, tsn_value :: a -- ^ value to return when this node is selected
|
||||
}
|
||||
|
||||
-- | State used by TreeSelect.
|
||||
--
|
||||
-- Contains all needed information such as the window, font and a zipper over the tree.
|
||||
data TSState a = TSState { tss_tree :: TreeZipper (TSNode a)
|
||||
, tss_window :: Window
|
||||
, tss_display :: Display
|
||||
, tss_size :: (Int, Int) -- ^ size of 'tz_window'
|
||||
, tss_xfont :: XMonadFont
|
||||
, tss_gc :: GC
|
||||
, tss_visual :: Visual
|
||||
, tss_colormap :: Colormap
|
||||
, tss_history :: ([[String]], [[String]]) -- ^ history zipper, navigated with 'moveHistBack' and 'moveHistForward'
|
||||
}
|
||||
|
||||
-- | State monad transformer using 'TSState'
|
||||
newtype TreeSelect a b = TreeSelect { runTreeSelect :: ReaderT (TSConfig a) (StateT (TSState a) X) b }
|
||||
deriving (Monad, Applicative, Functor, MonadState (TSState a), MonadReader (TSConfig a), MonadIO)
|
||||
|
||||
-- | Lift the 'X' action into the 'XMonad.Actions.TreeSelect.TreeSelect' monad
|
||||
liftX :: X a -> TreeSelect b a
|
||||
liftX = TreeSelect . lift . lift
|
||||
|
||||
-- | Run Treeselect with a given config and tree.
|
||||
-- This can be used for selectiong anything
|
||||
--
|
||||
-- * for switching workspaces and moving windows use 'treeselectWorkspace'
|
||||
-- * for selecting actions use 'treeselectAction'
|
||||
treeselect :: TSConfig a -- ^ config file
|
||||
-> Forest (TSNode a) -- ^ a list of 'Data.Tree.Tree's to select from.
|
||||
-> X (Maybe a)
|
||||
treeselect c t = treeselectAt c (fromForest t) []
|
||||
|
||||
-- | Same as 'treeselect' but ad a specific starting position
|
||||
treeselectAt :: TSConfig a -- ^ config file
|
||||
-> TreeZipper (TSNode a) -- ^ tree structure with a cursor position (starting node)
|
||||
-> [[String]] -- ^ list of paths that can be navigated with 'moveHistBack' and 'moveHistForward' (bound to the 'o' and 'i' keys)
|
||||
-> X (Maybe a)
|
||||
treeselectAt conf@TSConfig{..} zipper hist = withDisplay $ \display -> do
|
||||
-- create a window on the currently focused screen
|
||||
rootw <- asks theRoot
|
||||
Rectangle{..} <- gets $ screenRect . W.screenDetail . W.current . windowset
|
||||
|
||||
Just vinfo <- liftIO $ matchVisualInfo display (defaultScreen display) 32 4
|
||||
|
||||
colormap <- liftIO $ createColormap display rootw (visualInfo_visual vinfo) allocNone
|
||||
|
||||
win <- liftIO $ allocaSetWindowAttributes $ \attributes -> do
|
||||
set_override_redirect attributes True
|
||||
set_colormap attributes colormap
|
||||
set_background_pixel attributes ts_background
|
||||
set_border_pixel attributes 0
|
||||
createWindow display rootw rect_x rect_y rect_width rect_height 0 (visualInfo_depth vinfo) inputOutput (visualInfo_visual vinfo) (cWColormap .|. cWBorderPixel .|. cWBackPixel) attributes
|
||||
|
||||
liftIO $ do
|
||||
-- TODO: move below?
|
||||
-- make the window visible
|
||||
mapWindow display win
|
||||
|
||||
-- listen to key and mouse button events
|
||||
selectInput display win (exposureMask .|. keyPressMask .|. buttonReleaseMask)
|
||||
|
||||
-- TODO: enable mouse select?
|
||||
-- and mouse button 1
|
||||
grabButton display button1 anyModifier win True buttonReleaseMask grabModeAsync grabModeAsync none none
|
||||
|
||||
-- grab the keyboard
|
||||
status <- liftIO $ grabKeyboard display win True grabModeAsync grabModeAsync currentTime
|
||||
|
||||
r <- if status == grabSuccess
|
||||
then do
|
||||
-- load the XMF font
|
||||
gc <- liftIO $ createGC display win
|
||||
xfont <- initXMF ts_font
|
||||
|
||||
-- run the treeselect Monad
|
||||
ret <- evalStateT (runReaderT (runTreeSelect (redraw >> navigate)) conf)
|
||||
TSState{ tss_tree = zipper
|
||||
, tss_window = win
|
||||
, tss_display = display
|
||||
, tss_xfont = xfont
|
||||
, tss_size = (fromIntegral rect_width, fromIntegral rect_height)
|
||||
, tss_gc = gc
|
||||
, tss_visual = visualInfo_visual vinfo
|
||||
, tss_colormap = colormap
|
||||
, tss_history = ([], hist)
|
||||
}
|
||||
|
||||
-- release the XMF font
|
||||
releaseXMF xfont
|
||||
liftIO $ freeGC display gc
|
||||
return ret
|
||||
|
||||
else return Nothing
|
||||
|
||||
-- destroy the window
|
||||
liftIO $ do
|
||||
unmapWindow display win
|
||||
destroyWindow display win
|
||||
freeColormap display colormap
|
||||
-- Flush the output buffer and wait for all the events to be processed
|
||||
-- TODO: is this needed?
|
||||
sync display False
|
||||
return r
|
||||
|
||||
-- | Select a workspace and execute a \"view\" function from "XMonad.StackSet" on it.
|
||||
treeselectWorkspace :: TSConfig WorkspaceId
|
||||
-> Forest String -- ^ your tree of workspace-names
|
||||
-> (WorkspaceId -> WindowSet -> WindowSet) -- ^ the \"view\" function.
|
||||
-- Instances can be 'W.greedyView' for switching to a workspace
|
||||
-- and/or 'W.shift' for moving the focused window to a selected workspace.
|
||||
--
|
||||
-- These actions can also be combined by doing
|
||||
--
|
||||
-- > \i -> W.greedyView i . W.shift i
|
||||
-> X ()
|
||||
treeselectWorkspace c xs f = do
|
||||
-- get all defined workspaces
|
||||
-- They have to be set with 'toWorkspaces'!
|
||||
ws <- gets (W.workspaces . windowset)
|
||||
|
||||
-- check the 'XConfig.workspaces'
|
||||
if all (`elem` map tag ws) (toWorkspaces xs)
|
||||
then do
|
||||
-- convert the 'Forest WorkspaceId' to 'Forest (TSNode WorkspaceId)'
|
||||
wsf <- forMForest (mkPaths xs) $ \(n, i) -> maybe (return (TSNode n "Does not exist!" "")) (mkNode n) (find (\w -> i == tag w) ws)
|
||||
|
||||
-- get the current workspace path
|
||||
me <- gets (W.tag . W.workspace . W.current . windowset)
|
||||
hist <- workspaceHistory
|
||||
treeselectAt c (fromJust $ followPath tsn_name (splitPath me) $ fromForest wsf) (map splitPath hist) >>= maybe (return ()) (windows . f)
|
||||
|
||||
else liftIO $ do
|
||||
-- error!
|
||||
let msg = unlines $ [ "Please add:"
|
||||
, " workspaces = toWorkspaces myWorkspaces"
|
||||
, "to your XMonad config!"
|
||||
, ""
|
||||
, "XConfig.workspaces: "
|
||||
] ++ map tag ws
|
||||
hPutStrLn stderr msg
|
||||
_ <- forkProcess $ executeFile "xmessage" True [msg] Nothing
|
||||
return ()
|
||||
where
|
||||
mkNode n w = do
|
||||
-- find the focused window's name on this workspace
|
||||
name <- maybe (return "") (fmap show . getName . W.focus) $ stack w
|
||||
return $ TSNode n name (tag w)
|
||||
|
||||
-- | Convert the workspace-tree to a flat list of paths such that XMonad can use them
|
||||
--
|
||||
-- The Nodes will be separated by a dot (\'.\') character
|
||||
toWorkspaces :: Forest String -> [WorkspaceId]
|
||||
toWorkspaces = map snd . concatMap flatten . mkPaths
|
||||
|
||||
mkPaths :: Forest String -> Forest (String, WorkspaceId)
|
||||
mkPaths = map (\(Node n ns) -> Node (n, n) (map (f n) ns))
|
||||
where
|
||||
f pth (Node x xs) = let pth' = pth ++ '.' : x
|
||||
in Node (x, pth') (map (f pth') xs)
|
||||
|
||||
splitPath :: WorkspaceId -> [String]
|
||||
splitPath i = case break (== '.') i of
|
||||
(x, []) -> [x]
|
||||
(x, _:xs) -> x : splitPath xs
|
||||
|
||||
-- | Select from a Tree of 'X' actions
|
||||
--
|
||||
-- <<https://wiki.haskell.org/wikiupload/thumb/9/9b/Treeselect-Action.png/800px-Treeselect-Action.png>>
|
||||
--
|
||||
-- Each of these actions have to be specified inside a 'TSNode'
|
||||
--
|
||||
-- Example
|
||||
--
|
||||
-- > treeselectAction myTreeConf
|
||||
-- > [ Node (TSNode "Hello" "displays hello" (spawn "xmessage hello!")) []
|
||||
-- > , Node (TSNode "Shutdown" "Poweroff the system" (spawn "shutdown")) []
|
||||
-- > , Node (TSNode "Brightness" "Sets screen brightness using xbacklight" (return ()))
|
||||
-- > [ Node (TSNode "Bright" "FULL POWER!!" (spawn "xbacklight -set 100")) []
|
||||
-- > , Node (TSNode "Normal" "Normal Brightness (50%)" (spawn "xbacklight -set 50")) []
|
||||
-- > , Node (TSNode "Dim" "Quite dark" (spawn "xbacklight -set 10")) []
|
||||
-- > ]
|
||||
-- > ]
|
||||
treeselectAction :: TSConfig (X a) -> Forest (TSNode (X a)) -> X ()
|
||||
treeselectAction c xs = treeselect c xs >>= \x -> case x of
|
||||
Just a -> a >> return ()
|
||||
Nothing -> return ()
|
||||
|
||||
forMForest :: (Functor m, Applicative m, Monad m) => [Tree a] -> (a -> m b) -> m [Tree b]
|
||||
forMForest x g = mapM (mapMTree g) x
|
||||
|
||||
mapMTree :: (Functor m, Applicative m, Monad m) => (a -> m b) -> Tree a -> m (Tree b)
|
||||
mapMTree f (Node x xs) = Node <$> f x <*> mapM (mapMTree f) xs
|
||||
|
||||
|
||||
-- | Quit returning the currently selected node
|
||||
select :: TreeSelect a (Maybe a)
|
||||
select = Just <$> gets (tsn_value . cursor . tss_tree)
|
||||
|
||||
-- | Quit without returning anything
|
||||
cancel :: TreeSelect a (Maybe a)
|
||||
cancel = return Nothing
|
||||
|
||||
-- TODO: redraw only what is necessary.
|
||||
-- Examples: redrawAboveCursor, redrawBelowCursor and redrawCursor
|
||||
|
||||
-- | Move the cursor to its parent node
|
||||
moveParent :: TreeSelect a (Maybe a)
|
||||
moveParent = moveWith parent >> redraw >> navigate
|
||||
|
||||
-- | Move the cursor one level down, highlighting its first child-node
|
||||
moveChild :: TreeSelect a (Maybe a)
|
||||
moveChild = moveWith children >> redraw >> navigate
|
||||
|
||||
-- | Move the cursor to the next child-node
|
||||
moveNext :: TreeSelect a (Maybe a)
|
||||
moveNext = moveWith nextChild >> redraw >> navigate
|
||||
|
||||
-- | Move the cursor to the previous child-node
|
||||
movePrev :: TreeSelect a (Maybe a)
|
||||
movePrev = moveWith previousChild >> redraw >> navigate
|
||||
|
||||
-- | Move backwards in history
|
||||
moveHistBack :: TreeSelect a (Maybe a)
|
||||
moveHistBack = do
|
||||
s <- get
|
||||
case tss_history s of
|
||||
(xs, a:y:ys) -> do
|
||||
put s{tss_history = (a:xs, y:ys)}
|
||||
moveTo y
|
||||
_ -> navigate
|
||||
|
||||
-- | Move forward in history
|
||||
moveHistForward :: TreeSelect a (Maybe a)
|
||||
moveHistForward = do
|
||||
s <- get
|
||||
case tss_history s of
|
||||
(x:xs, ys) -> do
|
||||
put s{tss_history = (xs, x:ys)}
|
||||
moveTo x
|
||||
_ -> navigate
|
||||
|
||||
-- | Move to a specific node
|
||||
moveTo :: [String] -- ^ path, always starting from the top
|
||||
-> TreeSelect a (Maybe a)
|
||||
moveTo i = moveWith (followPath tsn_name i . rootNode) >> redraw >> navigate
|
||||
|
||||
-- | Apply a transformation on the internal 'XMonad.Util.TreeZipper.TreeZipper'.
|
||||
moveWith :: (TreeZipper (TSNode a) -> Maybe (TreeZipper (TSNode a))) -> TreeSelect a ()
|
||||
moveWith f = do
|
||||
s <- get
|
||||
case f (tss_tree s) of
|
||||
-- TODO: redraw cursor only?
|
||||
Just t -> put s{ tss_tree = t }
|
||||
Nothing -> return ()
|
||||
|
||||
-- | wait for keys and run navigation
|
||||
navigate :: TreeSelect a (Maybe a)
|
||||
navigate = gets tss_display >>= \d -> join . liftIO . allocaXEvent $ \e -> do
|
||||
maskEvent d (exposureMask .|. keyPressMask .|. buttonReleaseMask) e
|
||||
|
||||
ev <- getEvent e
|
||||
|
||||
if ev_event_type ev == keyPress
|
||||
then do
|
||||
(ks, _) <- lookupString $ asKeyEvent e
|
||||
return $ do
|
||||
mask <- liftX $ cleanMask (ev_state ev)
|
||||
f <- asks ts_navigate
|
||||
fromMaybe navigate $ M.lookup (mask, fromMaybe xK_VoidSymbol ks) f
|
||||
else return navigate
|
||||
|
||||
-- | Request a full redraw
|
||||
redraw :: TreeSelect a ()
|
||||
redraw = do
|
||||
win <- gets tss_window
|
||||
dpy <- gets tss_display
|
||||
|
||||
-- clear window
|
||||
-- TODO: not always needed!
|
||||
liftIO $ clearWindow dpy win
|
||||
|
||||
t <- gets tss_tree
|
||||
_ <- drawLayers 0 0 (reverse $ (tz_before t, cursor t, tz_after t) : tz_parents t)
|
||||
return ()
|
||||
|
||||
drawLayers :: Int -- ^ indentation level
|
||||
-> Int -- ^ height
|
||||
-> [(Forest (TSNode a), TSNode a, Forest (TSNode a))] -- ^ node layers (from top to bottom!)
|
||||
-> TreeSelect a Int
|
||||
drawLayers _ yl [] = return yl
|
||||
drawLayers xl yl ((bs, c, as):xs) = do
|
||||
TSConfig{..} <- ask
|
||||
|
||||
let nodeColor y = if odd y then ts_node else ts_nodealt
|
||||
|
||||
-- draw nodes above
|
||||
forM_ (zip [yl ..] (reverse bs)) $ \(y, Node n _) ->
|
||||
drawNode xl y n (nodeColor y)
|
||||
-- drawLayers (xl + 1) (y + 1) ns
|
||||
-- TODO: draw rest? if not ts_hidechildren
|
||||
-- drawLayers (xl + 1) (y + 1) ns
|
||||
|
||||
-- draw the current / parent node
|
||||
-- if this is the last (currently focused) we use the 'ts_highlight' color
|
||||
let current_level = yl + length bs
|
||||
drawNode xl current_level c $
|
||||
if null xs then ts_highlight
|
||||
else nodeColor current_level
|
||||
|
||||
l2 <- drawLayers (xl + 1) (current_level + 1) xs
|
||||
|
||||
-- draw nodes below
|
||||
forM_ (zip [l2 ..] as) $ \(y, Node n _) ->
|
||||
drawNode xl y n (nodeColor y)
|
||||
-- TODO: draw rest? if not ts_hidechildren
|
||||
-- drawLayers (xl + 1) (y + 1) ns
|
||||
return (l2 + length as)
|
||||
|
||||
|
||||
-- | Draw a node at a given indentation and height level
|
||||
drawNode :: Int -- ^ indentation level (not in pixels)
|
||||
-> Int -- ^ height level (not in pixels)
|
||||
-> TSNode a -- ^ node to draw
|
||||
-> (Pixel, Pixel) -- ^ node foreground (font) and background color
|
||||
-> TreeSelect a ()
|
||||
drawNode ix iy TSNode{..} col = do
|
||||
TSConfig{..} <- ask
|
||||
window <- gets tss_window
|
||||
display <- gets tss_display
|
||||
font <- gets tss_xfont
|
||||
gc <- gets tss_gc
|
||||
colormap <- gets tss_colormap
|
||||
visual <- gets tss_visual
|
||||
liftIO $ drawWinBox window display visual colormap gc font col tsn_name ts_extra tsn_extra
|
||||
(ix * ts_indent) (iy * ts_node_height)
|
||||
ts_node_width ts_node_height
|
||||
|
||||
-- TODO: draw extra text (transparent background? or ts_background)
|
||||
-- drawWinBox window fnt col2 nodeH (scW-x) (mes) (x+nodeW) y 8
|
||||
|
||||
-- | Draw a simple box with text
|
||||
drawWinBox :: Window -> Display -> Visual -> Colormap -> GC -> XMonadFont -> (Pixel, Pixel) -> String -> Pixel -> String -> Int -> Int -> Int -> Int -> IO ()
|
||||
drawWinBox win display visual colormap gc font (fg, bg) text fg2 text2 x y w h = do
|
||||
-- draw box
|
||||
setForeground display gc bg
|
||||
fillRectangle display win gc (fromIntegral x) (fromIntegral y) (fromIntegral w) (fromIntegral h)
|
||||
|
||||
-- dreaw text
|
||||
drawStringXMF display win visual colormap gc font fg
|
||||
(fromIntegral $ x + 8)
|
||||
(fromIntegral $ y + h - 8)
|
||||
text
|
||||
|
||||
-- dreaw extra text
|
||||
drawStringXMF display win visual colormap gc font fg2
|
||||
(fromIntegral $ x + w + 8)
|
||||
(fromIntegral $ y + h - 8)
|
||||
text2
|
||||
|
||||
-- | Modified version of 'XMonad.Util.Font.printStringXMF' that uses 'Pixel' as color format
|
||||
drawStringXMF :: Display -> Drawable -> Visual -> Colormap -> GC
|
||||
-> XMonadFont -- ^ XMF Font
|
||||
-> Pixel -- ^ font color
|
||||
-> Position -- ^ x-position
|
||||
-> Position -- ^ y-position
|
||||
-> String -- ^ string text
|
||||
-> IO ()
|
||||
drawStringXMF display window visual colormap gc font col x y text = case font of
|
||||
Core fnt -> do
|
||||
setForeground display gc col
|
||||
setFont display gc $ fontFromFontStruct fnt
|
||||
drawImageString display window gc x y text
|
||||
Utf8 fnt -> do
|
||||
setForeground display gc col
|
||||
wcDrawImageString display window fnt gc x y text
|
||||
#ifdef XFT
|
||||
Xft fnt -> do
|
||||
withXftDraw display window visual colormap $
|
||||
\ft_draw -> withXftColorValue display visual colormap (fromARGB col) $
|
||||
\ft_color -> xftDrawString ft_draw ft_color fnt x y text
|
||||
|
||||
-- | Convert 'Pixel' to 'XRenderColor'
|
||||
--
|
||||
-- Note that it uses short to represent its components
|
||||
fromARGB :: Pixel -> XRenderColor
|
||||
fromARGB x = XRenderColor (fromIntegral $ 0xff00 .&. shiftR x 8) -- red
|
||||
(fromIntegral $ 0xff00 .&. x) -- green
|
||||
(fromIntegral $ 0xff00 .&. shiftL x 8) -- blue
|
||||
(fromIntegral $ 0xff00 .&. shiftR x 16) -- alpha
|
||||
#endif
|
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE TupleSections #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.WindowBringer
|
||||
@@ -17,13 +18,13 @@
|
||||
module XMonad.Actions.WindowBringer (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
gotoMenu, gotoMenu', gotoMenuArgs, gotoMenuArgs',
|
||||
bringMenu, bringMenu', bringMenuArgs, bringMenuArgs',
|
||||
windowMap,
|
||||
bringWindow
|
||||
WindowBringerConfig(..),
|
||||
gotoMenu, gotoMenuConfig, gotoMenu', gotoMenuArgs, gotoMenuArgs',
|
||||
bringMenu, bringMenuConfig, bringMenu', bringMenuArgs, bringMenuArgs',
|
||||
windowMap, windowMap', bringWindow, actionMenu
|
||||
) where
|
||||
|
||||
import Data.Char (toLower)
|
||||
import Control.Applicative((<$>))
|
||||
import qualified Data.Map as M
|
||||
|
||||
import qualified XMonad.StackSet as W
|
||||
@@ -46,54 +47,74 @@ import XMonad.Util.NamedWindows (getName)
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
-- | Default menu command
|
||||
defaultCmd :: String
|
||||
defaultCmd = "dmenu"
|
||||
data WindowBringerConfig = WindowBringerConfig
|
||||
{ menuCommand :: String -- ^ The shell command that will handle window selection
|
||||
, menuArgs :: [String] -- ^ Arguments to be passed to menuCommand
|
||||
, windowTitler :: X.WindowSpace -> Window -> X String -- ^ A function that produces window titles given a workspace and a window
|
||||
}
|
||||
|
||||
instance Default WindowBringerConfig where
|
||||
def = WindowBringerConfig{ menuCommand = "dmenu"
|
||||
, menuArgs = ["-i"]
|
||||
, windowTitler = decorateName
|
||||
}
|
||||
|
||||
-- | Pops open a dmenu with window titles. Choose one, and you will be
|
||||
-- taken to the corresponding workspace.
|
||||
gotoMenu :: X ()
|
||||
gotoMenu = gotoMenuArgs []
|
||||
gotoMenu = gotoMenuConfig def
|
||||
|
||||
-- | Pops open a dmenu with window titles. Choose one, and you will be
|
||||
-- taken to the corresponding workspace. This version accepts a configuration
|
||||
-- object.
|
||||
gotoMenuConfig :: WindowBringerConfig -> X ()
|
||||
gotoMenuConfig wbConfig = actionMenu wbConfig W.focusWindow
|
||||
|
||||
-- | Pops open a dmenu with window titles. Choose one, and you will be
|
||||
-- taken to the corresponding workspace. This version takes a list of
|
||||
-- arguments to pass to dmenu.
|
||||
gotoMenuArgs :: [String] -> X ()
|
||||
gotoMenuArgs menuArgs = gotoMenuArgs' defaultCmd menuArgs
|
||||
gotoMenuArgs args = gotoMenuConfig def { menuArgs = args }
|
||||
|
||||
-- | Pops open an application with window titles given over stdin. Choose one,
|
||||
-- and you will be taken to the corresponding workspace.
|
||||
gotoMenu' :: String -> X ()
|
||||
gotoMenu' menuCmd = gotoMenuArgs' menuCmd []
|
||||
gotoMenu' cmd = gotoMenuConfig def { menuArgs = [], menuCommand = cmd }
|
||||
|
||||
-- | Pops open an application with window titles given over stdin. Choose one,
|
||||
-- and you will be taken to the corresponding workspace. This version takes a
|
||||
-- list of arguments to pass to dmenu.
|
||||
gotoMenuArgs' :: String -> [String] -> X ()
|
||||
gotoMenuArgs' menuCmd menuArgs = actionMenu menuCmd menuArgs W.focusWindow
|
||||
gotoMenuArgs' cmd args = gotoMenuConfig def { menuCommand = cmd, menuArgs = args }
|
||||
|
||||
-- | Pops open a dmenu with window titles. Choose one, and it will be
|
||||
-- dragged, kicking and screaming, into your current workspace.
|
||||
bringMenu :: X ()
|
||||
bringMenu = bringMenuArgs []
|
||||
bringMenu = bringMenuArgs def
|
||||
|
||||
-- | Pops open a dmenu with window titles. Choose one, and it will be
|
||||
-- dragged, kicking and screaming, into your current workspace. This version
|
||||
-- accepts a configuration object.
|
||||
bringMenuConfig :: WindowBringerConfig -> X ()
|
||||
bringMenuConfig wbConfig = actionMenu wbConfig bringWindow
|
||||
|
||||
-- | Pops open a dmenu with window titles. Choose one, and it will be
|
||||
-- dragged, kicking and screaming, into your current workspace. This version
|
||||
-- takes a list of arguments to pass to dmenu.
|
||||
bringMenuArgs :: [String] -> X ()
|
||||
bringMenuArgs menuArgs = bringMenuArgs' defaultCmd menuArgs
|
||||
bringMenuArgs args = bringMenuConfig def { menuArgs = args }
|
||||
|
||||
-- | Pops open an application with window titles given over stdin. Choose one,
|
||||
-- and it will be dragged, kicking and screaming, into your current
|
||||
-- workspace.
|
||||
bringMenu' :: String -> X ()
|
||||
bringMenu' menuCmd = bringMenuArgs' menuCmd []
|
||||
bringMenu' cmd = bringMenuConfig def { menuArgs = [], menuCommand = cmd }
|
||||
|
||||
-- | Pops open an application with window titles given over stdin. Choose one,
|
||||
-- and it will be dragged, kicking and screaming, into your current
|
||||
-- workspace. This version allows arguments to the chooser to be specified.
|
||||
bringMenuArgs' :: String -> [String] -> X ()
|
||||
bringMenuArgs' menuCmd menuArgs = actionMenu menuCmd menuArgs bringWindow
|
||||
bringMenuArgs' cmd args = bringMenuConfig def { menuArgs = args, menuCommand = cmd }
|
||||
|
||||
-- | Brings the specified window into the current workspace.
|
||||
bringWindow :: Window -> X.WindowSet -> X.WindowSet
|
||||
@@ -101,25 +122,33 @@ bringWindow w ws = W.shiftWin (W.currentTag ws) w ws
|
||||
|
||||
-- | Calls dmenuMap to grab the appropriate Window, and hands it off to action
|
||||
-- if found.
|
||||
actionMenu :: String -> [String] -> (Window -> X.WindowSet -> X.WindowSet) -> X ()
|
||||
actionMenu menuCmd menuArgs action = windowMap >>= menuMapFunction >>= flip X.whenJust (windows . action)
|
||||
actionMenu :: WindowBringerConfig -> (Window -> X.WindowSet -> X.WindowSet) -> X ()
|
||||
actionMenu WindowBringerConfig{ menuCommand = cmd
|
||||
, menuArgs = args
|
||||
, windowTitler = titler
|
||||
} action
|
||||
= windowMap' titler >>= menuMapFunction >>= flip X.whenJust (windows . action)
|
||||
where
|
||||
menuMapFunction :: M.Map String a -> X (Maybe a)
|
||||
menuMapFunction selectionMap = menuMapArgs menuCmd menuArgs selectionMap
|
||||
menuMapFunction = menuMapArgs cmd args
|
||||
|
||||
|
||||
-- | A map from window names to Windows.
|
||||
windowMap :: X (M.Map String Window)
|
||||
windowMap = do
|
||||
windowMap = windowMap' decorateName
|
||||
|
||||
-- | A map from window names to Windows, given a windowTitler function.
|
||||
windowMap' :: (X.WindowSpace -> Window -> X String) -> X (M.Map String Window)
|
||||
windowMap' titler = do
|
||||
ws <- gets X.windowset
|
||||
M.fromList `fmap` concat `fmap` mapM keyValuePairs (W.workspaces ws)
|
||||
M.fromList . concat <$> mapM keyValuePairs (W.workspaces ws)
|
||||
where keyValuePairs ws = mapM (keyValuePair ws) $ W.integrate' (W.stack ws)
|
||||
keyValuePair ws w = flip (,) w `fmap` decorateName ws w
|
||||
keyValuePair ws w = flip (,) w <$> titler ws w
|
||||
|
||||
-- | Returns the window name as will be listed in dmenu.
|
||||
-- Lowercased, for your convenience (since dmenu is case-sensitive).
|
||||
-- Tagged with the workspace ID, to guarantee uniqueness, and to let the user
|
||||
-- know where he's going.
|
||||
decorateName :: X.WindowSpace -> Window -> X String
|
||||
decorateName ws w = do
|
||||
name <- fmap (map toLower . show) $ getName w
|
||||
name <- show <$> getName w
|
||||
return $ name ++ " [" ++ W.tag ws ++ "]"
|
||||
|
@@ -21,6 +21,7 @@ module XMonad.Actions.WindowGo (
|
||||
runOrRaiseNext,
|
||||
raiseMaybe,
|
||||
raiseNextMaybe,
|
||||
raiseNextMaybeCustomFocus,
|
||||
|
||||
raiseBrowser,
|
||||
raiseEditor,
|
||||
@@ -38,7 +39,7 @@ module XMonad.Actions.WindowGo (
|
||||
import Control.Monad
|
||||
import Data.Char (toLower)
|
||||
import Data.Monoid
|
||||
import XMonad (Query(), X(), ManageHook, withWindowSet, runQuery, liftIO, ask)
|
||||
import XMonad (Query(), X(), ManageHook, WindowSet, withWindowSet, runQuery, liftIO, ask)
|
||||
import Graphics.X11 (Window)
|
||||
import XMonad.ManageHook
|
||||
import XMonad.Operations (windows)
|
||||
@@ -137,16 +138,21 @@ raiseNext = raiseNextMaybe $ return ()
|
||||
'raiseNextMaybe' is an alternative version that allows cycling
|
||||
through the matching windows. If the focused window matches the
|
||||
query the next matching window is raised. If no matches are found
|
||||
the function f is executed.
|
||||
-}
|
||||
|
||||
the function f is executed. -}
|
||||
raiseNextMaybe :: X () -> Query Bool -> X ()
|
||||
raiseNextMaybe f qry = flip (ifWindows qry) f $ \ws -> do
|
||||
raiseNextMaybe = raiseNextMaybeCustomFocus W.focusWindow
|
||||
|
||||
{- | See 'raiseMaybe' and 'raiseNextMaybe'.
|
||||
In addition to all of the options offered by 'raiseNextMaybe'
|
||||
'raiseNextMaybeCustomFocus' allows the user to supply the function that
|
||||
should be used to shift the focus to any window that is found. -}
|
||||
raiseNextMaybeCustomFocus :: (Window -> WindowSet -> WindowSet) -> X() -> Query Bool -> X()
|
||||
raiseNextMaybeCustomFocus focusFn f qry = flip (ifWindows qry) f $ \ws -> do
|
||||
foc <- withWindowSet $ return . W.peek
|
||||
case foc of
|
||||
Just w | w `elem` ws -> let (_:y:_) = dropWhile (/=w) $ cycle ws -- cannot fail to match
|
||||
in windows $ W.focusWindow y
|
||||
_ -> windows . W.focusWindow . head $ ws
|
||||
in windows $ focusFn y
|
||||
_ -> windows . focusFn . head $ ws
|
||||
|
||||
-- | Given a function which gets us a String, we try to raise a window with that classname,
|
||||
-- or we then interpret that String as a executable name.
|
||||
@@ -167,7 +173,8 @@ raiseAndDo :: X () -> Query Bool -> (Window -> X ()) -> X ()
|
||||
raiseAndDo f qry after = ifWindow qry (afterRaise `mappend` raiseHook) f
|
||||
where afterRaise = ask >>= (>> idHook) . liftX . after
|
||||
|
||||
{- | If a window matching the second argument is found, the window is focused and the third argument is called;
|
||||
{- | If a window matching the second argument is found, the window is focused and
|
||||
the third argument is called;
|
||||
otherwise, the first argument is called. -}
|
||||
runOrRaiseAndDo :: String -> Query Bool -> (Window -> X ()) -> X ()
|
||||
runOrRaiseAndDo = raiseAndDo . safeSpawnProg
|
||||
@@ -182,7 +189,6 @@ raiseMaster raisef thatUserQuery = raiseAndDo raisef thatUserQuery (\_ -> window
|
||||
{- | If the window is found the window is focused and set to master
|
||||
otherwise, action is run.
|
||||
|
||||
> runOrRaiseMaster "firefox" (className =? "Firefox"))
|
||||
-}
|
||||
> runOrRaiseMaster "firefox" (className =? "Firefox")) -}
|
||||
runOrRaiseMaster :: String -> Query Bool -> X ()
|
||||
runOrRaiseMaster run query = runOrRaiseAndDo run query (\_ -> windows W.swapMaster)
|
||||
|
@@ -31,6 +31,7 @@ module XMonad.Actions.Workscreen (
|
||||
,shiftToWorkscreen
|
||||
,fromWorkspace
|
||||
,expandWorkspace
|
||||
,WorkscreenId
|
||||
) where
|
||||
|
||||
import XMonad hiding (workspaces)
|
||||
|
@@ -24,7 +24,10 @@ module XMonad.Actions.WorkspaceNames (
|
||||
-- * Workspace naming
|
||||
renameWorkspace,
|
||||
workspaceNamesPP,
|
||||
getWorkspaceNames',
|
||||
getWorkspaceNames,
|
||||
getWorkspaceName,
|
||||
getCurrentWorkspaceName,
|
||||
setWorkspaceName,
|
||||
setCurrentWorkspaceName,
|
||||
|
||||
@@ -88,14 +91,27 @@ instance ExtensionClass WorkspaceNames where
|
||||
initialValue = WorkspaceNames M.empty
|
||||
extensionType = PersistentExtension
|
||||
|
||||
-- | Returns a lookup function that maps workspace tags to workspace names.
|
||||
getWorkspaceNames' :: X (WorkspaceId -> Maybe String)
|
||||
getWorkspaceNames' = do
|
||||
WorkspaceNames m <- XS.get
|
||||
return (`M.lookup` m)
|
||||
|
||||
-- | Returns a function that maps workspace tag @\"t\"@ to @\"t:name\"@ for
|
||||
-- workspaces with a name, and to @\"t\"@ otherwise.
|
||||
getWorkspaceNames :: X (WorkspaceId -> String)
|
||||
getWorkspaceNames = do
|
||||
WorkspaceNames m <- XS.get
|
||||
return $ \wks -> case M.lookup wks m of
|
||||
Nothing -> wks
|
||||
Just s -> wks ++ ":" ++ s
|
||||
lookup <- getWorkspaceNames'
|
||||
return $ \wks -> wks ++ maybe "" (':' :) (lookup wks)
|
||||
|
||||
-- | Gets the name of a workspace, if set, otherwise returns nothing.
|
||||
getWorkspaceName :: WorkspaceId -> X (Maybe String)
|
||||
getWorkspaceName w = ($ w) `fmap` getWorkspaceNames'
|
||||
|
||||
-- | Gets the name of the current workspace. See 'getWorkspaceName'
|
||||
getCurrentWorkspaceName :: X (Maybe String)
|
||||
getCurrentWorkspaceName = do
|
||||
getWorkspaceName =<< gets (W.currentTag . windowset)
|
||||
|
||||
-- | Sets the name of a workspace. Empty string makes the workspace unnamed
|
||||
-- again.
|
||||
|
@@ -36,7 +36,7 @@ import qualified Data.Map as M
|
||||
-- If you prefer, an azertyKeys function is provided which you can use as so:
|
||||
--
|
||||
-- > import qualified Data.Map as M
|
||||
-- > main = xmonad someConfig { keys = \c -> azertyKeys c `M.union` keys someConfig c }
|
||||
-- > main = xmonad someConfig { keys = \c -> azertyKeys c <+> keys someConfig c }
|
||||
|
||||
azertyConfig = def { keys = azertyKeys <+> keys def }
|
||||
|
||||
@@ -46,3 +46,9 @@ azertyKeys conf@(XConfig {modMask = modm}) = M.fromList $
|
||||
[((m .|. modm, k), windows $ f i)
|
||||
| (i, k) <- zip (workspaces conf) [0x26,0xe9,0x22,0x27,0x28,0x2d,0xe8,0x5f,0xe7,0xe0],
|
||||
(f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
|
||||
++
|
||||
-- mod-{z,e,r} %! Switch to physical/Xinerama screens 1, 2, or 3
|
||||
-- mod-shift-{z,e,r} %! Move client to screen 1, 2, or 3
|
||||
[((m .|. modm, key), screenWorkspace sc >>= flip whenJust (windows . f))
|
||||
| (key, sc) <- zip [xK_z, xK_e, xK_r] [0..],
|
||||
(f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
||||
|
@@ -180,8 +180,7 @@ bluetileManageHook :: ManageHook
|
||||
bluetileManageHook = composeAll
|
||||
[ workspaceByPos, positionStoreManageHook (Just defaultThemeWithButtons)
|
||||
, className =? "MPlayer" --> doFloat
|
||||
, isFullscreen --> doFullFloat
|
||||
, manageDocks]
|
||||
, isFullscreen --> doFullFloat]
|
||||
|
||||
bluetileLayoutHook = avoidStruts $ minimize $ boringWindows $ (
|
||||
named "Floating" floating |||
|
||||
@@ -199,6 +198,7 @@ bluetileLayoutHook = avoidStruts $ minimize $ boringWindows $ (
|
||||
floatingDeco l = buttonDeco shrinkText defaultThemeWithButtons l
|
||||
|
||||
bluetileConfig =
|
||||
docks $
|
||||
def
|
||||
{ modMask = mod4Mask, -- logo key
|
||||
manageHook = bluetileManageHook,
|
||||
|
@@ -164,10 +164,9 @@ import qualified Data.Map as M
|
||||
-- > adjustEventInput
|
||||
--
|
||||
|
||||
desktopConfig = ewmh def
|
||||
{ startupHook = setDefaultCursor xC_left_ptr
|
||||
desktopConfig = docks $ ewmh def
|
||||
{ startupHook = setDefaultCursor xC_left_ptr <+> startupHook def
|
||||
, layoutHook = desktopLayoutModifiers $ layoutHook def
|
||||
, manageHook = manageHook def <+> manageDocks
|
||||
, keys = desktopKeys <+> keys def }
|
||||
|
||||
desktopKeys (XConfig {modMask = modm}) = M.fromList $
|
||||
|
@@ -205,7 +205,7 @@ instance PPrint ScreenId
|
||||
instance (Show a, Show b) => PPrint (Map a b)
|
||||
-- }}}
|
||||
-- main {{{
|
||||
dmwitConfig nScreens = def {
|
||||
dmwitConfig nScreens = docks $ def {
|
||||
borderWidth = 2,
|
||||
workspaces = withScreens nScreens (map show [1..5]),
|
||||
terminal = "urxvt",
|
||||
@@ -221,7 +221,6 @@ dmwitConfig nScreens = def {
|
||||
<+> (appName =? "huludesktop" --> doRectFloat fullscreen43on169)
|
||||
<+> fullscreenMPlayer
|
||||
<+> floatAll ["Gimp", "Wine"]
|
||||
<+> manageDocks
|
||||
<+> manageSpawn,
|
||||
logHook = allPPs nScreens,
|
||||
startupHook = refresh
|
||||
|
@@ -42,7 +42,7 @@ import XMonad.Actions.DynamicWorkspaces ( withNthWorkspace, withWorkspace,
|
||||
import XMonad.Actions.CycleWS ( moveTo, WSType( HiddenNonEmptyWS ),
|
||||
Direction1D( Prev, Next) )
|
||||
|
||||
import XMonad.Hooks.ManageDocks ( avoidStruts, manageDocks )
|
||||
import XMonad.Hooks.ManageDocks ( avoidStruts, docks )
|
||||
import XMonad.Hooks.EwmhDesktops ( ewmh )
|
||||
|
||||
myXPConfig :: XPConfig
|
||||
@@ -117,7 +117,7 @@ keys x = M.fromList $
|
||||
++
|
||||
zip (zip (repeat (modMask x .|. shiftMask)) [xK_F1..xK_F12]) (map (withNthWorkspace copy) [0..])
|
||||
|
||||
config = ewmh def
|
||||
config = docks $ ewmh def
|
||||
{ borderWidth = 1 -- Width of the window border in pixels.
|
||||
, XMonad.workspaces = ["mutt","iceweasel"]
|
||||
, layoutHook = showWName $ workspaceDir "~" $
|
||||
@@ -129,7 +129,6 @@ config = ewmh def
|
||||
named "widescreen" ((mytab *||* mytab)
|
||||
****//* combineTwo Square mytab mytab) -- |||
|
||||
--mosaic 0.25 0.5
|
||||
, manageHook = manageHook def <+> manageDocks -- add panel-handling
|
||||
, terminal = "xterm" -- The preferred terminal program.
|
||||
, normalBorderColor = "#222222" -- Border color for unfocused windows.
|
||||
, focusedBorderColor = "#00ff00" -- Border color for focused windows.
|
||||
|
@@ -18,7 +18,8 @@ module XMonad.Config.Gnome (
|
||||
-- $usage
|
||||
gnomeConfig,
|
||||
gnomeRun,
|
||||
gnomeRegister
|
||||
gnomeRegister,
|
||||
desktopLayoutModifiers
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
|
@@ -17,7 +17,8 @@ module XMonad.Config.Kde (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
kdeConfig,
|
||||
kde4Config
|
||||
kde4Config,
|
||||
desktopLayoutModifiers
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
|
@@ -20,7 +20,8 @@ module XMonad.Config.Mate (
|
||||
-- $usage
|
||||
mateConfig,
|
||||
mateRun,
|
||||
mateRegister
|
||||
mateRegister,
|
||||
desktopLayoutModifiers
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
|
@@ -21,7 +21,7 @@ import XMonad.Layout.TwoPane
|
||||
import qualified Data.Map as M
|
||||
|
||||
sjanssenConfig =
|
||||
ewmh $ def
|
||||
docks $ ewmh $ def
|
||||
{ terminal = "exec urxvt"
|
||||
, workspaces = ["irc", "web"] ++ map show [3 .. 9 :: Int]
|
||||
, mouseBindings = \(XConfig {modMask = modm}) -> M.fromList $
|
||||
@@ -35,7 +35,7 @@ sjanssenConfig =
|
||||
| (x, w) <- [ ("Firefox", "web")
|
||||
, ("Ktorrent", "7")
|
||||
, ("Amarokapp", "7")]]
|
||||
<+> manageHook def <+> manageDocks <+> manageSpawn
|
||||
<+> manageHook def <+> manageSpawn
|
||||
<+> (isFullscreen --> doFullFloat)
|
||||
, startupHook = mapM_ spawnOnce spawns
|
||||
}
|
||||
|
@@ -16,7 +16,8 @@
|
||||
module XMonad.Config.Xfce (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
xfceConfig
|
||||
xfceConfig,
|
||||
desktopLayoutModifiers
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
|
@@ -116,6 +116,13 @@ beyond the standard keybindings provided by xmonad.
|
||||
See "XMonad.Doc.Extending#Editing_key_bindings" for instructions on how to
|
||||
edit your key bindings.
|
||||
|
||||
* "XMonad.Actions.AfterDrag":
|
||||
Allows you to add actions dependent on the current mouse drag.
|
||||
|
||||
* "XMonad.Actions.BluetileCommands":
|
||||
External commands for interfacing the [Bluetile](https://hackage.haskell.org/package/bluetile)
|
||||
tiling window manager with Xmonad.
|
||||
|
||||
* "XMonad.Actions.Commands":
|
||||
Allows you to run internal xmonad commands (X () actions) using
|
||||
a dmenu menu in addition to key bindings. Requires dmenu and
|
||||
@@ -159,6 +166,20 @@ edit your key bindings.
|
||||
master, swap it with the next window in the stack. Focus stays in the
|
||||
master.
|
||||
|
||||
* "XMonad.Actions.DynamicProjects":
|
||||
Imbues workspaces with additional features so they can be treated as
|
||||
individual project areas.
|
||||
|
||||
* "XMonad.Actions.DynamicWorkspaceGroups":
|
||||
Dynamically manage "workspace groups", sets of workspaces being used
|
||||
together for some common task or purpose, to allow switching between
|
||||
workspace groups in a single action. Note that this only makes sense for
|
||||
multi-head setups.
|
||||
|
||||
* "XMonad.Actions.DynamicWorkspaceOrder":
|
||||
Remember a dynamically updateable ordering on workspaces, together with
|
||||
tools for using this ordering with "XMonad.Actions.CycleWS" and "XMonad.Hooks.DynamicLog".
|
||||
|
||||
* "XMonad.Actions.DynamicWorkspaces":
|
||||
Provides bindings to add and delete workspaces. Note that you may only
|
||||
delete a workspace that is already empty.
|
||||
@@ -186,6 +207,24 @@ edit your key bindings.
|
||||
GridSelect displays items(e.g. the opened windows) in a 2D grid and lets
|
||||
the user select from it with the cursor/hjkl keys or the mouse.
|
||||
|
||||
* "XMonad.Actions.GroupNavigation":
|
||||
Provides methods for cycling through groups of windows across workspaces,
|
||||
ignoring windows that do not belong to this group. A group consists of all
|
||||
windows matching a user-provided boolean query. Also provides a method for
|
||||
jumping back to the most recently used window in any given group.
|
||||
|
||||
* "XMonad.Actions.KeyRemap":
|
||||
Remap Keybinding on the fly, e.g having Dvorak char,
|
||||
but everything with Control/Shift is left US Layout.
|
||||
|
||||
* "XMonad.Actions.Launcher":
|
||||
A set of prompts for XMonad.
|
||||
|
||||
* "XMonad.Actions.LinkWorkspaces":
|
||||
Provides bindings to add and delete links between workspaces. It is aimed at
|
||||
providing useful links between workspaces in a multihead setup.
|
||||
Linked workspaces are view at the same time.
|
||||
|
||||
* "XMonad.Actions.MessageFeedback":
|
||||
Alternative to 'XMonad.Operations.sendMessage' that provides knowledge
|
||||
of whether the message was handled, and utility functions based on
|
||||
@@ -198,6 +237,10 @@ edit your key bindings.
|
||||
A layout modifier to resize windows with the mouse by grabbing the
|
||||
window's lower right corner.
|
||||
|
||||
* "XMonad.Actions.Navigation2D":
|
||||
Navigation2D is an xmonad extension that allows easy directional navigation
|
||||
of windows and screens (in a multi-monitor setup).
|
||||
|
||||
* "XMonad.Actions.NoBorders":
|
||||
This module provides helper functions for dealing with window borders.
|
||||
|
||||
@@ -228,6 +271,10 @@ edit your key bindings.
|
||||
A module for easily running Internet searches on web sites through xmonad.
|
||||
Modeled after the handy Surfraw CLI search tools at <https://secure.wikimedia.org/wikipedia/en/wiki/Surfraw>.
|
||||
|
||||
* "XMonad.Actions.ShowText":
|
||||
ShowText displays text for sometime on the screen similar to
|
||||
"XMonad.Util.Dzen" which offers more features (currently).
|
||||
|
||||
* "XMonad.Actions.SimpleDate":
|
||||
An example external contrib module for XMonad.
|
||||
Provides a simple binding to dzen2 to print the date as a popup menu.
|
||||
@@ -254,6 +301,12 @@ edit your key bindings.
|
||||
* "XMonad.Actions.TopicSpace":
|
||||
Turns your workspaces into a more topic oriented system.
|
||||
|
||||
* "XMonad.Actions.TreeSelect":
|
||||
TreeSelect displays your workspaces or actions in a Tree-like format.
|
||||
You can select the desired workspace/action with the cursor or hjkl keys.
|
||||
This module is fully configurable and very useful if you like to have a
|
||||
lot of workspaces.
|
||||
|
||||
* "XMonad.Actions.UpdateFocus":
|
||||
Updates the focus on mouse move in unfocused windows.
|
||||
|
||||
@@ -283,9 +336,23 @@ edit your key bindings.
|
||||
Provides functions for performing a given action on all windows of
|
||||
the current workspace.
|
||||
|
||||
* "XMonad.Actions.Workscreen":
|
||||
A workscreen permits to display a set of workspaces on several screens. In
|
||||
xinerama mode, when a workscreen is viewed, workspaces associated to all
|
||||
screens are visible. The first workspace of a workscreen is displayed on
|
||||
first screen, second on second screen, etc. Workspace position can be easily
|
||||
changed. If the current workscreen is called again, workspaces are shifted.
|
||||
This also permits to see all workspaces of a workscreen even if just one screen
|
||||
is present, and to move windows from workspace to workscreen.
|
||||
|
||||
* "XMonad.Actions.WorkspaceCursors":
|
||||
Like "XMonad.Actions.Plane" for an arbitrary number of dimensions.
|
||||
|
||||
* "XMonad.Actions.WorkspaceNames":
|
||||
Provides bindings to rename workspaces, show these names in DynamicLog and
|
||||
swap workspaces along with their names. These names survive restart. Together
|
||||
with "XMonad.Layout.WorkspaceDir" this provides for a fully dynamic topic
|
||||
space workflow.
|
||||
-}
|
||||
|
||||
{- $configs
|
||||
@@ -297,25 +364,56 @@ configuration; you can also simply import them and use them as your
|
||||
own configuration, possibly with some modifications.
|
||||
|
||||
|
||||
* "XMonad.Config.Arossato"
|
||||
* "XMonad.Config.Arossato":
|
||||
This module specifies my xmonad defaults.
|
||||
|
||||
* "XMonad.Config.Azerty"
|
||||
* "XMonad.Config.Azerty":
|
||||
Fixes some keybindings for users of French keyboard layouts.
|
||||
|
||||
* "XMonad.Config.Desktop"
|
||||
* "XMonad.Config.Bepo":
|
||||
This module fixes some of the keybindings for the francophone among you who
|
||||
use a BEPO keyboard layout. Based on "XMonad.Config.Azerty".
|
||||
|
||||
* "XMonad.Config.Bluetile":
|
||||
This is the default configuration of [Bluetile](http://projects.haskell.org/bluetile/).
|
||||
If you are migrating from Bluetile to xmonad or want to create a similar setup,
|
||||
then this will give you pretty much the same thing, except for Bluetile's
|
||||
helper applications such as the dock.
|
||||
|
||||
* "XMonad.Config.Desktop":
|
||||
This module provides core desktop environment settings used
|
||||
in the Gnome, Kde, and Xfce config configs. It is also useful
|
||||
for people using other environments such as lxde, or using
|
||||
tray or panel applications without full desktop environments.
|
||||
|
||||
* "XMonad.Config.Gnome"
|
||||
* "XMonad.Config.Dmwit":
|
||||
[dmwit](https://github.com/dmwit)'s xmonad configs and helpers.
|
||||
|
||||
* "XMonad.Config.Kde"
|
||||
* "XMonad.Config.Droundy":
|
||||
Droundy's xmonad config.
|
||||
|
||||
* "XMonad.Config.Sjanssen"
|
||||
* "XMonad.Config.Gnome":
|
||||
This module provides a config suitable for use with the GNOME desktop environment.
|
||||
|
||||
* "XMonad.Config.Xfce"
|
||||
* "XMonad.Config.Kde":
|
||||
This module provides a config suitable for use with the KDE desktop environment.
|
||||
|
||||
* "XMonad.Config.Mate":
|
||||
This module provides a config suitable for use with the MATE desktop environment.
|
||||
|
||||
* "XMonad.Config.Prime":
|
||||
This is a draft of a brand new config syntax for xmonad. It aims to be
|
||||
1) easier to copy/paste snippets from the docs 2) easier to get the gist
|
||||
for what's going on, for you imperative programmers. It's brand new, so it's
|
||||
pretty much guaranteed to break or change syntax. But what's the worst that
|
||||
could happen? Xmonad crashes and logs you out? It probably won't do that.
|
||||
Give it a try.
|
||||
|
||||
* "XMonad.Config.Sjanssen":
|
||||
[spencerjanssen](https://github.com/spencerjanssen)'s xmonad configs.
|
||||
|
||||
* "XMonad.Config.Xfce":
|
||||
This module provides a config suitable for use with the Xfce desktop environment.
|
||||
|
||||
-}
|
||||
|
||||
@@ -349,6 +447,29 @@ occur. The two most important hooks are:
|
||||
|
||||
Here is a list of the modules found in @XMonad.Hooks@:
|
||||
|
||||
* "XMonad.Hooks.CurrentWorkspaceOnTop":
|
||||
Ensures that the windows of the current workspace are always in front of
|
||||
windows that are located on other visible screens. This becomes important if
|
||||
you use decoration and drag windows from one screen to another.
|
||||
Using this module, the dragged window will always be in front of other windows.
|
||||
|
||||
* "XMonad.Hooks.DebugEvents":
|
||||
Module to dump diagnostic information about X11 events received by xmonad.
|
||||
This is incomplete due to "Event" being incomplete and not providing
|
||||
information about a number of events, and enforcing artificial constraints
|
||||
on others (for example ClientMessage); the X11 package will require a number
|
||||
of changes to fix these problems.
|
||||
|
||||
* "XMonad.Hooks.DebugKeyEvents":
|
||||
A debugging module to track key events, useful when you can't tell whether
|
||||
xmonad is processing some or all key events.
|
||||
|
||||
* "XMonad.Hooks.DebugStack":
|
||||
Dump the state of the StackSet. A logHook and handleEventHook are also provided.
|
||||
|
||||
* "Xmonad.Hooks.DynamicBars":
|
||||
Manage per-screen status bars.
|
||||
|
||||
* "XMonad.Hooks.DynamicHooks":
|
||||
One-shot and permanent ManageHooks that can be updated at runtime.
|
||||
|
||||
@@ -367,14 +488,28 @@ Here is a list of the modules found in @XMonad.Hooks@:
|
||||
which causes those windows to become slightly translucent if something
|
||||
like xcompmgr is running
|
||||
|
||||
* "XMonad.Hooks.FadeWindows":
|
||||
A more flexible and general compositing interface than FadeInactive. Windows
|
||||
can be selected and opacity specified by means of FadeHooks, which are very
|
||||
similar to ManageHooks and use the same machinery.
|
||||
|
||||
* "XMonad.Hooks.FloatNext":
|
||||
Hook and keybindings for automatically sending the next
|
||||
spawned window(s) to the floating layer.
|
||||
|
||||
* "XMonad.Hooks.ICCCMFocus":
|
||||
Deprecated.
|
||||
|
||||
* "XMonad.Hooks.InsertPosition":
|
||||
Configure where new windows should be added and which window should be
|
||||
focused.
|
||||
|
||||
* "XMonad.Hooks.ManageDebug":
|
||||
A manageHook and associated logHook for debugging "ManageHooks". Simplest
|
||||
usage: wrap your xmonad config in the debugManageHook combinator. Or use
|
||||
debugManageHookOn for a triggerable version, specifying the triggering key
|
||||
sequence in "EZConfig" syntax. Or use the individual hooks in whatever way you see fit.
|
||||
|
||||
* "XMonad.Hooks.ManageDocks":
|
||||
This module provides tools to automatically manage 'dock' type programs,
|
||||
such as gnome-panel, kicker, dzen, and xmobar.
|
||||
@@ -389,35 +524,54 @@ Here is a list of the modules found in @XMonad.Hooks@:
|
||||
* "XMonad.Hooks.Place":
|
||||
Automatic placement of floating windows.
|
||||
|
||||
* "XMonad.Hooks.PositionStoreHooks":
|
||||
This module contains two hooks for the PositionStore (see XMonad.Util.PositionStore) -
|
||||
a ManageHook and an EventHook. The ManageHook can be used to fill the
|
||||
PositionStore with position and size information about new windows. The advantage
|
||||
of using this hook is, that the information is recorded independent of the
|
||||
currently active layout. So the floating shape of the window can later be restored
|
||||
even if it was opened in a tiled layout initially. The EventHook makes sure
|
||||
that windows are deleted from the PositionStore when they are closed.
|
||||
|
||||
* "XMonad.Hooks.RestoreMinimized":
|
||||
(Deprecated: Use XMonad.Hooks.Minimize) Lets you restore minimized
|
||||
windows (see "XMonad.Layout.Minimize") by selecting them on a
|
||||
taskbar (listens for _NET_ACTIVE_WINDOW and WM_CHANGE_STATE).
|
||||
|
||||
* "XMonad.Hooks.ScreenCorners":
|
||||
Run X () actions by touching the edge of your screen with your mouse.
|
||||
|
||||
* "XMonad.Hooks.Script":
|
||||
Provides a simple interface for running a ~\/.xmonad\/hooks script with the
|
||||
name of a hook.
|
||||
|
||||
* "XMonad.Hooks.ServerMode": Allows sending commands to a running xmonad process.
|
||||
|
||||
* "XMonad.Hooks.SetCursor":
|
||||
Set a default mouse cursor on startup.
|
||||
* "XMonad.Hooks.ServerMode":
|
||||
Allows sending commands to a running xmonad process.
|
||||
|
||||
* "XMonad.Hooks.SetWMName":
|
||||
Sets the WM name to a given string, so that it could be detected using
|
||||
_NET_SUPPORTING_WM_CHECK protocol. May be useful for making Java GUI
|
||||
programs work.
|
||||
|
||||
* "XMonad.Hooks.ToggleHook":
|
||||
Hook and keybindings for toggling hook behavior.
|
||||
|
||||
* "XMonad.Hooks.UrgencyHook":
|
||||
UrgencyHook lets you configure an action to occur when a window demands
|
||||
your attention. (In traditional WMs, this takes the form of \"flashing\"
|
||||
on your \"taskbar.\" Blech.)
|
||||
|
||||
* "XMonad.Hooks.WallpaperSetter":
|
||||
Log hook which changes the wallpapers depending on visible workspaces.
|
||||
|
||||
* "XMonad.Hooks.WorkspaceByPos":
|
||||
Useful in a dual-head setup: Looks at the requested geometry of
|
||||
new windows and moves them to the workspace of the non-focused
|
||||
screen if necessary.
|
||||
|
||||
* "XMonad.Hooks.WorkspaceHistory":
|
||||
Keeps track of workspace viewing order.
|
||||
|
||||
* "XMonad.Hooks.XPropManage":
|
||||
A ManageHook matching on XProperties.
|
||||
|
||||
@@ -450,6 +604,13 @@ For more information on using those modules for customizing your
|
||||
master and slave. Size of slave area automatically changes depending on
|
||||
number of slave windows.
|
||||
|
||||
* "XMonad.Layout.AvoidFloats":
|
||||
Find a maximum empty rectangle around floating windows and use that area to
|
||||
display non-floating windows.
|
||||
|
||||
* "XMonad.Layout.BinarySpacePartition":
|
||||
Layout where new windows will split the focused window in half, based off of BSPWM.
|
||||
|
||||
* "XMonad.Layout.BorderResize":
|
||||
This layout modifier will allow to resize windows by dragging their
|
||||
borders with the mouse. However, it only works in layouts or modified
|
||||
@@ -460,6 +621,11 @@ For more information on using those modules for customizing your
|
||||
* "XMonad.Layout.BoringWindows":
|
||||
BoringWindows is an extension to allow windows to be marked boring
|
||||
|
||||
* "XMonad.Layout.ButtonDecoration":
|
||||
A decoration that includes small buttons on both ends which invoke various
|
||||
actions when clicked on: Show a window menu (see "XMonad.Actions.WindowMenu"),
|
||||
minimize, maximize or close the window.
|
||||
|
||||
* "XMonad.Layout.CenteredMaster":
|
||||
Two layout modifiers. centerMaster places master window at center,
|
||||
on top of all other windows, which are managed by base layout.
|
||||
@@ -488,6 +654,11 @@ For more information on using those modules for customizing your
|
||||
A layout modifier and a class for easily creating decorated
|
||||
layouts.
|
||||
|
||||
* "XMonad.Layout.DecorationAddons":
|
||||
Various stuff that can be added to the decoration. Most of it is intended to
|
||||
be used by other modules. See "XMonad.Layout.ButtonDecoration" for a module
|
||||
that makes use of this.
|
||||
|
||||
* "XMonad.Layout.DecorationMadness":
|
||||
A collection of decorated layouts: some of them may be nice, some
|
||||
usable, others just funny.
|
||||
@@ -502,6 +673,24 @@ For more information on using those modules for customizing your
|
||||
the other is either the currently focused window or the second window in
|
||||
layout order. See also "XMonad.Layout.MouseResizableTall"
|
||||
|
||||
* "XMonad.Layout.DraggingVisualizer":
|
||||
A helper module to visualize the process of dragging a window by making it
|
||||
follow the mouse cursor. See "XMonad.Layout.WindowSwitcherDecoration" for a
|
||||
module that makes use of this.
|
||||
|
||||
* "XMonad.Layout.Drawer":
|
||||
A layout modifier that puts some windows in a "drawer" which retracts and
|
||||
expands depending on whether any window in it has focus. Useful for music
|
||||
players, tool palettes, etc.
|
||||
|
||||
* "XMonad.Layout.Dwindle":
|
||||
Three layouts: The first, Spiral, is a reimplementation of spiral with, at
|
||||
least to me, more intuitive semantics. The second, Dwindle, is inspired by
|
||||
a similar layout in awesome and produces the same sequence of decreasing
|
||||
window sizes as Spiral but pushes the smallest windows into a screen corner
|
||||
rather than the centre. The third, Squeeze arranges all windows in one row
|
||||
or in one column, with geometrically decreasing sizes.
|
||||
|
||||
* "XMonad.Layout.DwmStyle":
|
||||
A layout modifier for decorating windows in a dwm like style.
|
||||
|
||||
@@ -511,6 +700,10 @@ For more information on using those modules for customizing your
|
||||
split. This is useful when you usually leave a text editor or
|
||||
terminal in the master pane and like it to be 80 columns wide.
|
||||
|
||||
* "XMonad.Layout.Fullscreen":
|
||||
Hooks for sending messages about fullscreen windows to layouts, and a few
|
||||
example layout modifier that implement fullscreen windows.
|
||||
|
||||
* "XMonad.Layout.Gaps":
|
||||
Create manually-sized gaps along edges of the screen which will not
|
||||
be used for tiling, along with support for toggling gaps on and
|
||||
@@ -526,6 +719,25 @@ For more information on using those modules for customizing your
|
||||
master area and uses an aspect-ratio-specified layout for the
|
||||
slaves.
|
||||
|
||||
* "XMonad.Layout.Groups":
|
||||
Two-level layout with windows split in individual layout groups, themselves
|
||||
managed by a user-provided layout.
|
||||
|
||||
* * "XMonad.Layout.Groups.Examples":
|
||||
Example layouts for "XMonad.Layout.Groups".
|
||||
|
||||
* * "XMonad.Layout.Groups.Helpers":
|
||||
Utility functions for "XMonad.Layout.Groups".
|
||||
|
||||
* * "XMonad.Layout.Groups.Wmii":
|
||||
A wmii-like layout algorithm.
|
||||
|
||||
* "XMonad.Layout.Hidden":
|
||||
Similar to XMonad.Layout.Minimize but completely removes windows from the
|
||||
window set so XMonad.Layout.BoringWindows isn't necessary. Perfect companion
|
||||
to XMonad.Layout.BinarySpacePartition since it can be used to move windows
|
||||
to another part of the BSP tree.
|
||||
|
||||
* "XMonad.Layout.HintedGrid":
|
||||
A not so simple layout that attempts to put all windows in a square grid
|
||||
while obeying their size hints.
|
||||
@@ -538,6 +750,16 @@ For more information on using those modules for customizing your
|
||||
Layout modfier suitable for workspace with multi-windowed instant messenger
|
||||
(like Psi or Tkabber).
|
||||
|
||||
* "XMonad.Layout.IfMax":
|
||||
Provides IfMax layout, which will run one layout if there are maximum N
|
||||
windows on workspace, and another layout, when number of windows is greater
|
||||
than N.
|
||||
|
||||
* "XMonad.Layout.ImageButtonDecoration":
|
||||
A decoration that includes small image buttons on both ends which invoke
|
||||
various actions when clicked on: Show a window menu (see "XMonad.Actions.WindowMenu"),
|
||||
minimize, maximize or close the window.
|
||||
|
||||
* "XMonad.Layout.IndependentScreens":
|
||||
Utility functions for simulating independent sets of workspaces on
|
||||
each screen (like dwm's workspace model), using internal tags to
|
||||
@@ -613,12 +835,19 @@ For more information on using those modules for customizing your
|
||||
A layout in the spirit of "XMonad.Layout.ResizableTile", but with the option
|
||||
to use the mouse to adjust the layout.
|
||||
|
||||
* "XMonad.Layout.MultiColumns":
|
||||
This layout tiles windows in a growing number of columns. The number of
|
||||
windows in each column can be controlled by messages.
|
||||
|
||||
* "XMonad.Layout.MultiToggle":
|
||||
Dynamically apply and unapply transformers to your window layout. This can
|
||||
be used to rotate your window layout by 90 degrees, or to make the
|
||||
currently focused window occupy the whole screen (\"zoom in\") then undo
|
||||
the transformation (\"zoom out\").
|
||||
|
||||
* * "XMonad.Layout.MultiToggle.Instances":
|
||||
Some convenient common instances of the Transformer class, for use with "XMonad.Layout.MultiToggle".
|
||||
|
||||
* "XMonad.Layout.Named":
|
||||
A module for assigning a name to a given layout.
|
||||
|
||||
@@ -634,17 +863,38 @@ For more information on using those modules for customizing your
|
||||
result in title bars that span the entire window instead of being only the
|
||||
length of the window title.
|
||||
|
||||
* "XMonad.Layout.OnHost":
|
||||
Configure layouts on a per-host basis: use layouts and apply layout modifiers
|
||||
selectively, depending on the host. Heavily based on "XMonad.Layout.PerWorkspace"
|
||||
by Brent Yorgey.
|
||||
|
||||
* "XMonad.Layout.OneBig":
|
||||
Places one (master) window at top left corner of screen, and other (slave)
|
||||
windows at the top.
|
||||
|
||||
* "XMonad.Layout.PerScreen":
|
||||
Configure layouts based on the width of your screen; use your favorite
|
||||
multi-column layout for wide screens and a full-screen layout for small ones.
|
||||
|
||||
* "XMonad.Layout.PerWorkspace":
|
||||
Configure layouts on a per-workspace basis: use layouts and apply
|
||||
layout modifiers selectively, depending on the workspace.
|
||||
|
||||
* "XMonad.Layout.PositionStoreFloat":
|
||||
A floating layout which has been designed with a dual-head setup in mind.
|
||||
It makes use of "XMonad.Util.PositionStore" as well as "XMonad.Hooks.PositionStoreHooks".
|
||||
Since there is currently no way to move or resize windows with the keyboard
|
||||
alone in this layout, it is adviced to use it in combination with a decoration
|
||||
such as "XMonad.Layout.NoFrillsDecoration" (to move windows) and the layout
|
||||
modifier "XMonad.Layout.BorderResize" (to resize windows).
|
||||
|
||||
* "XMonad.Layout.Reflect":
|
||||
Reflect a layout horizontally or vertically.
|
||||
|
||||
* "XMonad.Layout.Renamed":
|
||||
Layout modifier that can modify the description of its underlying layout on
|
||||
a (hopefully) flexible way.
|
||||
|
||||
* "XMonad.Layout.ResizableTile":
|
||||
More useful tiled layout that allows you to change a width\/height of window.
|
||||
See also "XMonad.Layout.MouseResizableTile".
|
||||
@@ -676,6 +926,12 @@ For more information on using those modules for customizing your
|
||||
* "XMonad.Layout.SimplestFloat":
|
||||
A basic floating layout like SimpleFloat but without the decoration.
|
||||
|
||||
* "XMonad.Layout.SortedLayout":
|
||||
A new LayoutModifier that sorts a given layout by a list of
|
||||
properties. The order of properties in the list determines
|
||||
the order of windows in the final layout. Any unmatched windows
|
||||
go to the end of the order.
|
||||
|
||||
* "XMonad.Layout.Spacing":
|
||||
Add a configurable amount of space around windows.
|
||||
|
||||
@@ -694,6 +950,13 @@ For more information on using those modules for customizing your
|
||||
A stacking layout, like dishes but with the ability to resize master pane.
|
||||
Mostly useful on small screens.
|
||||
|
||||
* "XMonad.Layout.Stoppable":
|
||||
This module implements a special kind of layout modifier, which when applied
|
||||
to a layout, causes xmonad to stop all non-visible processes. In a way,
|
||||
this is a sledge-hammer for applications that drain power. For example, given
|
||||
a web browser on a stoppable workspace, once the workspace is hidden the web
|
||||
browser will be stopped.
|
||||
|
||||
* "XMonad.Layout.SubLayouts":
|
||||
A layout combinator that allows layouts to be nested.
|
||||
|
||||
@@ -724,6 +987,10 @@ For more information on using those modules for customizing your
|
||||
WindowNavigation is an extension to allow easy navigation of a workspace.
|
||||
See also "XMonad.Actions.WindowNavigation".
|
||||
|
||||
* "XMonad.Layout.WindowSwitcherDecoration":
|
||||
A decoration that allows to switch the position of windows by dragging them
|
||||
onto each other.
|
||||
|
||||
* "XMonad.Layout.WorkspaceDir":
|
||||
WorkspaceDir is an extension to set the current directory in a workspace.
|
||||
Actually, it sets the current directory in a layout, since there's no way I
|
||||
@@ -757,6 +1024,9 @@ These are the available prompts:
|
||||
you're doing.
|
||||
Who knows, it might be useful for other purposes as well!
|
||||
|
||||
* "XMonad.Prompt.ConfirmPrompt":
|
||||
A module for setting up simple confirmation prompts for keybindings.
|
||||
|
||||
* "XMonad.Prompt.DirExec":
|
||||
A directory file executables prompt for XMonad. This might be useful if you
|
||||
don't want to have scripts in your PATH environment variable (same
|
||||
@@ -786,6 +1056,13 @@ These are the available prompts:
|
||||
* narrow completions by section number, if the one is specified
|
||||
(like @\/etc\/bash_completion@ does)
|
||||
|
||||
* "XMonad.Prompt.Pass":
|
||||
This module provides 3 combinators for ease passwords manipulation (generate, read, remove):
|
||||
1) one to lookup passwords in the password-storage.
|
||||
2) one to generate a password for a given password label that the user inputs.
|
||||
3) one to delete a stored password for a given password label that the user inputs.
|
||||
|
||||
|
||||
* "XMonad.Prompt.RunOrRaise":
|
||||
A prompt for XMonad which will run a program, open a file,
|
||||
or raise an already running program, depending on context.
|
||||
@@ -830,6 +1107,10 @@ A non complete list with a brief description:
|
||||
* "XMonad.Util.CustomKeys": configure key bindings (see
|
||||
"XMonad.Doc.Extending#Editing_key_bindings").
|
||||
|
||||
* "XMonad.Util.DebugWindow":
|
||||
Module to dump window information for diagnostic/debugging purposes. See
|
||||
"XMonad.Hooks.DebugEvents" and "XMonad.Hooks.DebugStack" for practical uses.
|
||||
|
||||
* "XMonad.Util.Dmenu":
|
||||
A convenient binding to dmenu.
|
||||
Requires the process-1.0 package
|
||||
@@ -837,11 +1118,19 @@ A non complete list with a brief description:
|
||||
* "XMonad.Util.Dzen":
|
||||
Handy wrapper for dzen. Requires dzen >= 0.2.4.
|
||||
|
||||
* "XMonad.Util.EZConfig": configure key bindings easily, including a
|
||||
parser for writing key bindings in "M-C-x" style.
|
||||
* "XMonad.Util.EZConfig":
|
||||
Configure key bindings easily, including a
|
||||
parser for writing key bindings in "M-C-x" style.
|
||||
|
||||
* "XMonad.Util.Font": A module for abstracting a font facility over
|
||||
Core fonts and Xft
|
||||
* "XMonad.Util.ExtensibleState":
|
||||
Module for storing custom mutable state in xmonad.
|
||||
|
||||
* "XMonad.Util.Font":
|
||||
A module for abstracting a font facility over
|
||||
Core fonts and Xft.
|
||||
|
||||
* "XMonad.Util.Image":
|
||||
Utilities for manipulating [[Bool]] as images.
|
||||
|
||||
* "XMonad.Util.Invisible":
|
||||
A data type to store the layout state
|
||||
@@ -852,6 +1141,10 @@ A non complete list with a brief description:
|
||||
a pretty-printing status logger format. See "XMonad.Hooks.DynamicLog"
|
||||
for more information.
|
||||
|
||||
* * "XMonad.Util.Loggers.NamedScratchpad":
|
||||
A collection of Loggers (see "XMonad.Util.Loggers") for NamedScratchpads
|
||||
(see "XMonad.Util.NamedScratchpad").
|
||||
|
||||
* "XMonad.Util.NamedActions":
|
||||
A wrapper for keybinding configuration that can list the available
|
||||
keybindings.
|
||||
@@ -864,10 +1157,22 @@ A non complete list with a brief description:
|
||||
This module allows you to associate the X titles of windows with
|
||||
them.
|
||||
|
||||
* "XMonad.Util.NoTaskbar":
|
||||
Utility function and 'ManageHook` to mark a window to be ignored by
|
||||
EWMH taskbars and pagers. Useful for `NamedScratchpad` windows, since
|
||||
you will usually be taken to the `NSP` workspace by them.
|
||||
|
||||
* "XMonad.Util.Paste":
|
||||
A module for sending key presses to windows. This modules provides generalized
|
||||
and specialized functions for this task.
|
||||
|
||||
* "XMonad.Util.PositionStore":
|
||||
A utility module to store information about position and size of a window.
|
||||
See "XMonad.Layout.PositionStoreFloat" for a layout that makes use of this.
|
||||
|
||||
* "XMonad.Util.RemoteWindows":
|
||||
This module implements a proper way of finding out whether the window is remote or local.
|
||||
|
||||
* "XMonad.Util.Replace":
|
||||
Implements a @--replace@ flag outside of core.
|
||||
|
||||
@@ -880,6 +1185,16 @@ A non complete list with a brief description:
|
||||
* "XMonad.Util.Scratchpad":
|
||||
Very handy hotkey-launched toggleable floating terminal window.
|
||||
|
||||
* "XMonad.Util.SpawnNamedPipe":
|
||||
A module for spawning a pipe whose Handle lives in the Xmonad state.
|
||||
|
||||
* "XMonad.Util.SpawnOnce":
|
||||
A module for spawning a command once, and only once. Useful to start status
|
||||
bars and make session settings inside startupHook.
|
||||
|
||||
* "XMonad.Util.Stack":
|
||||
Utility functions for manipulating Maybe Stacks.
|
||||
|
||||
* "XMonad.Util.StringProp":
|
||||
Internal utility functions for storing Strings with the root window.
|
||||
Used for global state like IORefs with string keys, but more latency,
|
||||
@@ -894,10 +1209,21 @@ A non complete list with a brief description:
|
||||
* "XMonad.Util.Types":
|
||||
Miscellaneous commonly used types.
|
||||
|
||||
* "XMonad.Util.Ungrab":
|
||||
Release xmonad's keyboard and pointer grabs immediately, so
|
||||
screen grabbers and lock utilities, etc. will work. Replaces
|
||||
the short sleep hackaround.
|
||||
|
||||
* "XMonad.Util.WindowProperties":
|
||||
EDSL for specifying window properties; various utilities related to window
|
||||
properties.
|
||||
|
||||
* "XMonad.Util.WindowState":
|
||||
Functions for saving per-window data.
|
||||
|
||||
* "XMonad.Util.WorkspaceCompare":
|
||||
Functions for examining, comparing, and sorting workspaces.
|
||||
|
||||
* "XMonad.Util.XSelection":
|
||||
A module for accessing and manipulating X Window's mouse selection (the buffer used in copy and pasting).
|
||||
'getSelection' and 'putSelection' are adaptations of Hxsel.hs and Hxput.hs from the XMonad-utils
|
||||
|
@@ -18,9 +18,13 @@ module XMonad.Hooks.DynamicBars (
|
||||
-- $usage
|
||||
DynamicStatusBar
|
||||
, DynamicStatusBarCleanup
|
||||
, DynamicStatusBarPartialCleanup
|
||||
, dynStatusBarStartup
|
||||
, dynStatusBarStartup'
|
||||
, dynStatusBarEventHook
|
||||
, dynStatusBarEventHook'
|
||||
, multiPP
|
||||
, multiPPFormat
|
||||
) where
|
||||
|
||||
import Prelude
|
||||
@@ -29,9 +33,10 @@ import Control.Monad
|
||||
import Control.Monad.Trans (lift)
|
||||
import Control.Monad.Writer (WriterT, execWriterT, tell)
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.Monoid
|
||||
import Data.Traversable (traverse)
|
||||
import Data.Foldable (traverse_)
|
||||
|
||||
import Graphics.X11.Xinerama
|
||||
import Graphics.X11.Xlib
|
||||
@@ -50,13 +55,28 @@ import qualified XMonad.Util.ExtensibleState as XS
|
||||
-- dynamically responding to screen changes. A startup action, event hook, and
|
||||
-- a way to separate PP styles based on the screen's focus are provided:
|
||||
--
|
||||
-- * The 'dynStatusBarStartup' hook which initializes the status bars.
|
||||
-- * The 'dynStatusBarStartup' hook which initializes the status bars. The
|
||||
-- first argument is an `ScreenId -> IO Handle` which spawns a status bar on the
|
||||
-- given screen and returns the pipe which the string should be written to.
|
||||
-- The second argument is a `IO ()` to shut down all status bars. This should
|
||||
-- be placed in your `startupHook`.
|
||||
--
|
||||
-- * The 'dynStatusBarEventHook' hook which respawns status bars when the
|
||||
-- number of screens changes.
|
||||
-- number of screens changes. The arguments are the same as for the
|
||||
-- `dynStatusBarStartup` function. This should be placed in your
|
||||
-- `handleEventHook`.
|
||||
--
|
||||
-- * Each of the above functions have an alternate form
|
||||
-- (`dynStatusBarStartup'` and `dynStatusBarEventHook'`) which use a cleanup
|
||||
-- function which takes an additional `ScreenId` argument which allows for
|
||||
-- more fine-grained control for shutting down a specific screen's status bar.
|
||||
--
|
||||
-- * The 'multiPP' function which allows for different output based on whether
|
||||
-- the screen for the status bar has focus.
|
||||
-- the screen for the status bar has focus (the first argument) or not (the
|
||||
-- second argument). This is for use in your `logHook`.
|
||||
--
|
||||
-- * The 'multiPPFormat' function is the same as the 'multiPP' function, but it
|
||||
-- also takes in a function that can customize the output to status bars.
|
||||
--
|
||||
-- The hooks take a 'DynamicStatusBar' function which is given the id of the
|
||||
-- screen to start up and returns the 'Handle' to the pipe to write to. The
|
||||
@@ -65,37 +85,67 @@ import qualified XMonad.Util.ExtensibleState as XS
|
||||
--
|
||||
|
||||
data DynStatusBarInfo = DynStatusBarInfo
|
||||
{ dsbInfoScreens :: [ScreenId]
|
||||
, dsbInfoHandles :: [Handle]
|
||||
{ dsbInfo :: [(ScreenId, Handle)]
|
||||
} deriving (Typeable)
|
||||
|
||||
instance ExtensionClass DynStatusBarInfo where
|
||||
initialValue = DynStatusBarInfo [] []
|
||||
initialValue = DynStatusBarInfo []
|
||||
|
||||
type DynamicStatusBar = ScreenId -> IO Handle
|
||||
type DynamicStatusBarCleanup = IO ()
|
||||
type DynamicStatusBarPartialCleanup = ScreenId -> IO ()
|
||||
|
||||
dynStatusBarStartup :: DynamicStatusBar -> DynamicStatusBarCleanup -> X ()
|
||||
dynStatusBarStartup sb cleanup = do
|
||||
dynStatusBarSetup :: X ()
|
||||
dynStatusBarSetup = do
|
||||
dpy <- asks display
|
||||
root <- asks theRoot
|
||||
io $ xrrSelectInput dpy root rrScreenChangeNotifyMask
|
||||
|
||||
dynStatusBarStartup :: DynamicStatusBar -> DynamicStatusBarCleanup -> X ()
|
||||
dynStatusBarStartup sb cleanup = do
|
||||
dynStatusBarSetup
|
||||
updateStatusBars sb cleanup
|
||||
|
||||
dynStatusBarStartup' :: DynamicStatusBar -> DynamicStatusBarPartialCleanup -> X ()
|
||||
dynStatusBarStartup' sb cleanup = do
|
||||
dynStatusBarSetup
|
||||
updateStatusBars' sb cleanup
|
||||
|
||||
dynStatusBarEventHook :: DynamicStatusBar -> DynamicStatusBarCleanup -> Event -> X All
|
||||
dynStatusBarEventHook sb cleanup (RRScreenChangeNotifyEvent {}) = updateStatusBars sb cleanup >> return (All True)
|
||||
dynStatusBarEventHook _ _ _ = return (All True)
|
||||
dynStatusBarEventHook sb cleanup = dynStatusBarRun (updateStatusBars sb cleanup)
|
||||
|
||||
dynStatusBarEventHook' :: DynamicStatusBar -> DynamicStatusBarPartialCleanup -> Event -> X All
|
||||
dynStatusBarEventHook' sb cleanup = dynStatusBarRun (updateStatusBars' sb cleanup)
|
||||
|
||||
dynStatusBarRun :: X () -> Event -> X All
|
||||
dynStatusBarRun action (RRScreenChangeNotifyEvent {}) = action >> return (All True)
|
||||
dynStatusBarRun _ _ = return (All True)
|
||||
|
||||
updateStatusBars :: DynamicStatusBar -> DynamicStatusBarCleanup -> X ()
|
||||
updateStatusBars sb cleanup = do
|
||||
dsbInfo <- XS.get
|
||||
(dsbInfoScreens, dsbInfoHandles) <- XS.get >>= return . unzip . dsbInfo
|
||||
screens <- getScreens
|
||||
when (screens /= dsbInfoScreens dsbInfo) $ do
|
||||
when (screens /= dsbInfoScreens) $ do
|
||||
newHandles <- liftIO $ do
|
||||
hClose `mapM_` dsbInfoHandles dsbInfo
|
||||
hClose `mapM_` dsbInfoHandles
|
||||
cleanup
|
||||
mapM sb screens
|
||||
XS.put $ DynStatusBarInfo screens newHandles
|
||||
XS.put $ DynStatusBarInfo (zip screens newHandles)
|
||||
|
||||
updateStatusBars' :: DynamicStatusBar -> DynamicStatusBarPartialCleanup -> X ()
|
||||
updateStatusBars' sb cleanup = do
|
||||
(dsbInfoScreens, dsbInfoHandles) <- XS.get >>= return . unzip . dsbInfo
|
||||
screens <- getScreens
|
||||
when (screens /= dsbInfoScreens) $ do
|
||||
let oldInfo = zip dsbInfoScreens dsbInfoHandles
|
||||
let (infoToKeep, infoToClose) = partition (flip elem screens . fst) oldInfo
|
||||
newInfo <- liftIO $ do
|
||||
mapM_ hClose $ map snd infoToClose
|
||||
mapM_ cleanup $ map fst infoToClose
|
||||
let newScreens = screens \\ dsbInfoScreens
|
||||
newHandles <- mapM sb newScreens
|
||||
return $ zip newScreens newHandles
|
||||
XS.put . DynStatusBarInfo $ infoToKeep ++ newInfo
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- The following code is from adamvo's xmonad.hs file.
|
||||
@@ -104,9 +154,12 @@ updateStatusBars sb cleanup = do
|
||||
multiPP :: PP -- ^ The PP to use if the screen is focused
|
||||
-> PP -- ^ The PP to use otherwise
|
||||
-> X ()
|
||||
multiPP focusPP unfocusPP = do
|
||||
dsbInfo <- XS.get
|
||||
multiPP' dynamicLogString focusPP unfocusPP (dsbInfoHandles dsbInfo)
|
||||
multiPP = multiPPFormat dynamicLogString
|
||||
|
||||
multiPPFormat :: (PP -> X String) -> PP -> PP -> X ()
|
||||
multiPPFormat dynlStr focusPP unfocusPP = do
|
||||
(_, dsbInfoHandles) <- XS.get >>= return . unzip . dsbInfo
|
||||
multiPP' dynlStr focusPP unfocusPP dsbInfoHandles
|
||||
|
||||
multiPP' :: (PP -> X String) -> PP -> PP -> [Handle] -> X ()
|
||||
multiPP' dynlStr focusPP unfocusPP handles = do
|
||||
@@ -118,10 +171,9 @@ multiPP' dynlStr focusPP unfocusPP handles = do
|
||||
out <- lift $ dynlStr $ if isFoc then focusPP else unfocusPP
|
||||
when isFoc $ get >>= tell . Last . Just
|
||||
return out
|
||||
traverse put . getLast
|
||||
traverse_ put . getLast
|
||||
=<< execWriterT . (io . zipWithM_ hPutStrLn handles <=< mapM pickPP) . catMaybes
|
||||
=<< mapM screenWorkspace (zipWith const [0 .. ] handles)
|
||||
return ()
|
||||
|
||||
getScreens :: MonadIO m => m [ScreenId]
|
||||
getScreens = liftIO $ do
|
||||
|
@@ -199,12 +199,11 @@ statusBar :: LayoutClass l Window
|
||||
-> IO (XConfig (ModifiedLayout AvoidStruts l))
|
||||
statusBar cmd pp k conf = do
|
||||
h <- spawnPipe cmd
|
||||
return $ conf
|
||||
return $ docks $ conf
|
||||
{ layoutHook = avoidStruts (layoutHook conf)
|
||||
, logHook = do
|
||||
logHook conf
|
||||
dynamicLogWithPP pp { ppOutput = hPutStrLn h }
|
||||
, manageHook = manageHook conf <+> manageDocks
|
||||
, keys = liftM2 M.union keys' (keys conf)
|
||||
}
|
||||
where
|
||||
|
@@ -47,7 +47,7 @@ import XMonad.Util.WindowProperties (getProp32)
|
||||
-- > main = xmonad $ ewmh def{ handleEventHook =
|
||||
-- > handleEventHook def <+> fullscreenEventHook }
|
||||
--
|
||||
-- You may also be interested in 'avoidStruts' from "XMonad.Hooks.ManageDocks".
|
||||
-- You may also be interested in 'docks' from "XMonad.Hooks.ManageDocks".
|
||||
|
||||
|
||||
-- | Add EWMH functionality to the given config. See above for an example.
|
||||
|
@@ -15,8 +15,8 @@
|
||||
module XMonad.Hooks.ManageDocks (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
manageDocks, checkDock, AvoidStruts, avoidStruts, avoidStrutsOn,
|
||||
docksEventHook,
|
||||
docks, manageDocks, checkDock, AvoidStruts, avoidStruts, avoidStrutsOn,
|
||||
docksEventHook, docksStartupHook,
|
||||
ToggleStruts(..),
|
||||
SetStruts(..),
|
||||
module XMonad.Util.Types,
|
||||
@@ -39,34 +39,29 @@ import XMonad.Layout.LayoutModifier
|
||||
import XMonad.Util.Types
|
||||
import XMonad.Util.WindowProperties (getProp32s)
|
||||
import XMonad.Util.XUtils (fi)
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
import Data.Monoid (All(..), mempty)
|
||||
import Data.Functor((<$>))
|
||||
|
||||
import qualified Data.Set as S
|
||||
import qualified Data.Map as M
|
||||
import Control.Monad (when, forM_, filterM)
|
||||
|
||||
-- $usage
|
||||
-- To use this module, add the following import to @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Hooks.ManageDocks
|
||||
--
|
||||
-- The first component is a 'ManageHook' which recognizes these
|
||||
-- windows and de-manages them, so that xmonad does not try to tile
|
||||
-- them. To enable it:
|
||||
-- Wrap your xmonad config with a call to 'docks', like so:
|
||||
--
|
||||
-- > manageHook = ... <+> manageDocks
|
||||
-- > main = xmonad $ docks def
|
||||
--
|
||||
-- The second component is a layout modifier that prevents windows
|
||||
-- from overlapping these dock windows. It is intended to replace
|
||||
-- xmonad's so-called \"gap\" support. First, you must add it to your
|
||||
-- list of layouts:
|
||||
-- Then add 'avoidStruts' or 'avoidStrutsOn' layout modifier to your layout
|
||||
-- to prevent windows from overlapping these windows.
|
||||
--
|
||||
-- > layoutHook = avoidStruts (tall ||| mirror tall ||| ...)
|
||||
-- > where tall = Tall 1 (3/100) (1/2)
|
||||
--
|
||||
-- The third component is an event hook that causes new docks to appear
|
||||
-- immediately, instead of waiting for the next focus change.
|
||||
--
|
||||
-- > handleEventHook = ... <+> docksEventHook
|
||||
--
|
||||
-- 'AvoidStruts' also supports toggling the dock gaps; add a keybinding
|
||||
-- similar to:
|
||||
--
|
||||
@@ -86,23 +81,43 @@ import qualified Data.Set as S
|
||||
--
|
||||
-- > layoutHook = avoidStrutsOn [U,L] (tall ||| mirror tall ||| ...)
|
||||
--
|
||||
-- /Important note/: if you are switching from manual gaps
|
||||
-- (defaultGaps in your config) to avoidStruts (recommended, since
|
||||
-- manual gaps will probably be phased out soon), be sure to switch
|
||||
-- off all your gaps (with mod-b) /before/ reloading your config with
|
||||
-- avoidStruts! Toggling struts with a 'ToggleStruts' message will
|
||||
-- not work unless your gaps are set to zero.
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
--
|
||||
|
||||
-- | Add docks functionality to the given config. See above for an example.
|
||||
docks :: XConfig a -> XConfig a
|
||||
docks c = c { startupHook = docksStartupHook <+> startupHook c
|
||||
, handleEventHook = docksEventHook <+> handleEventHook c
|
||||
, manageHook = manageDocks <+> manageHook c }
|
||||
|
||||
newtype StrutCache = StrutCache { fromStrutCache :: M.Map Window [Strut] }
|
||||
deriving (Eq, Typeable)
|
||||
|
||||
data UpdateDocks = UpdateDocks deriving Typeable
|
||||
instance Message UpdateDocks
|
||||
|
||||
refreshDocks :: X ()
|
||||
refreshDocks = sendMessage UpdateDocks
|
||||
|
||||
instance ExtensionClass StrutCache where
|
||||
initialValue = StrutCache M.empty
|
||||
|
||||
updateStrutCache :: Window -> [Strut] -> X Bool
|
||||
updateStrutCache w strut = do
|
||||
XS.modified $ StrutCache . M.insert w strut . fromStrutCache
|
||||
|
||||
deleteFromStructCache :: Window -> X Bool
|
||||
deleteFromStructCache w = do
|
||||
XS.modified $ StrutCache . M.delete w . fromStrutCache
|
||||
|
||||
-- | Detects if the given window is of type DOCK and if so, reveals
|
||||
-- it, but does not manage it.
|
||||
manageDocks :: ManageHook
|
||||
manageDocks = checkDock --> (doIgnore <+> clearGapCache)
|
||||
where clearGapCache = do
|
||||
liftX (broadcastMessage ClearGapCache)
|
||||
manageDocks = checkDock --> (doIgnore <+> setDocksMask)
|
||||
where setDocksMask = do
|
||||
ask >>= \win -> liftX $ withDisplay $ \dpy -> do
|
||||
io $ selectInput dpy win (propertyChangeMask .|. structureNotifyMask)
|
||||
mempty
|
||||
|
||||
-- | Checks if a window is a DOCK or DESKTOP window
|
||||
@@ -118,13 +133,35 @@ checkDock = ask >>= \w -> liftX $ do
|
||||
-- | Whenever a new dock appears, refresh the layout immediately to avoid the
|
||||
-- new dock.
|
||||
docksEventHook :: Event -> X All
|
||||
docksEventHook (MapNotifyEvent {ev_window = w}) = do
|
||||
whenX ((not `fmap` (isClient w)) <&&> runQuery checkDock w) $ do
|
||||
broadcastMessage ClearGapCache
|
||||
refresh
|
||||
docksEventHook (MapNotifyEvent { ev_window = w }) = do
|
||||
whenX (runQuery checkDock w <&&> (not <$> isClient w)) $ do
|
||||
strut <- getStrut w
|
||||
whenX (updateStrutCache w strut) refreshDocks
|
||||
return (All True)
|
||||
docksEventHook (PropertyEvent { ev_window = w
|
||||
, ev_atom = a }) = do
|
||||
whenX (runQuery checkDock w) $ do
|
||||
nws <- getAtom "_NET_WM_STRUT"
|
||||
nwsp <- getAtom "_NET_WM_STRUT_PARTIAL"
|
||||
when (a == nws || a == nwsp) $ do
|
||||
strut <- getStrut w
|
||||
whenX (updateStrutCache w strut) refreshDocks
|
||||
return (All True)
|
||||
docksEventHook (DestroyWindowEvent {ev_window = w}) = do
|
||||
whenX (deleteFromStructCache w) refreshDocks
|
||||
return (All True)
|
||||
docksEventHook _ = return (All True)
|
||||
|
||||
docksStartupHook :: X ()
|
||||
docksStartupHook = withDisplay $ \dpy -> do
|
||||
rootw <- asks theRoot
|
||||
(_,_,wins) <- io $ queryTree dpy rootw
|
||||
docks <- filterM (runQuery checkDock) wins
|
||||
forM_ docks $ \win -> do
|
||||
strut <- getStrut win
|
||||
updateStrutCache win strut
|
||||
refreshDocks
|
||||
|
||||
-- | Gets the STRUT config, if present, in xmonad gap order
|
||||
getStrut :: Window -> X [Strut]
|
||||
getStrut w = do
|
||||
@@ -146,9 +183,7 @@ getStrut w = do
|
||||
calcGap :: S.Set Direction2D -> X (Rectangle -> Rectangle)
|
||||
calcGap ss = withDisplay $ \dpy -> do
|
||||
rootw <- asks theRoot
|
||||
-- We don't keep track of dock like windows, so we find all of them here
|
||||
(_,_,wins) <- io $ queryTree dpy rootw
|
||||
struts <- (filter careAbout . concat) `fmap` mapM getStrut wins
|
||||
struts <- (filter careAbout . concat) `fmap` XS.gets (M.elems . fromStrutCache)
|
||||
|
||||
-- we grab the window attributes of the root window rather than checking
|
||||
-- the width of the screen because xlib caches this info and it tends to
|
||||
@@ -170,12 +205,9 @@ avoidStrutsOn :: LayoutClass l a =>
|
||||
[Direction2D]
|
||||
-> l a
|
||||
-> ModifiedLayout AvoidStruts l a
|
||||
avoidStrutsOn ss = ModifiedLayout $ AvoidStruts (S.fromList ss) Nothing
|
||||
avoidStrutsOn ss = ModifiedLayout $ AvoidStruts (S.fromList ss)
|
||||
|
||||
data AvoidStruts a = AvoidStruts {
|
||||
avoidStrutsDirection :: S.Set Direction2D,
|
||||
avoidStrutsRectCache :: Maybe (S.Set Direction2D, Rectangle, Rectangle )
|
||||
} deriving ( Read, Show )
|
||||
data AvoidStruts a = AvoidStruts (S.Set Direction2D) deriving ( Read, Show )
|
||||
|
||||
-- | Message type which can be sent to an 'AvoidStruts' layout
|
||||
-- modifier to alter its behavior.
|
||||
@@ -185,13 +217,6 @@ data ToggleStruts = ToggleStruts
|
||||
|
||||
instance Message ToggleStruts
|
||||
|
||||
|
||||
-- | message sent to ensure that caching the gaps won't give a wrong result
|
||||
-- because a new dock has been added
|
||||
data ClearGapCache = ClearGapCache
|
||||
deriving (Read,Show,Typeable)
|
||||
instance Message ClearGapCache
|
||||
|
||||
-- | SetStruts is a message constructor used to set or unset specific struts,
|
||||
-- regardless of whether or not the struts were originally set. Here are some
|
||||
-- example bindings:
|
||||
@@ -219,26 +244,18 @@ data SetStruts = SetStruts { addedStruts :: [Direction2D]
|
||||
instance Message SetStruts
|
||||
|
||||
instance LayoutModifier AvoidStruts a where
|
||||
modifyLayoutWithUpdate as@(AvoidStruts ss cache) w r = do
|
||||
nr <- case cache of
|
||||
Just (ss', r', nr) | ss' == ss, r' == r -> return nr
|
||||
_ -> do
|
||||
nr <- fmap ($ r) (calcGap ss)
|
||||
setWorkarea nr
|
||||
return nr
|
||||
arranged <- runLayout w nr
|
||||
let newCache = Just (ss, r, nr)
|
||||
return (arranged, if newCache == cache
|
||||
then Nothing
|
||||
else Just as{ avoidStrutsRectCache = newCache } )
|
||||
modifyLayout (AvoidStruts ss) w r = do
|
||||
srect <- fmap ($ r) (calcGap ss)
|
||||
setWorkarea srect
|
||||
runLayout w srect
|
||||
|
||||
pureMess as@(AvoidStruts { avoidStrutsDirection = ss }) m
|
||||
| Just ToggleStruts <- fromMessage m = Just $ as { avoidStrutsDirection = toggleAll ss }
|
||||
| Just (ToggleStrut s) <- fromMessage m = Just $ as { avoidStrutsDirection = toggleOne s ss }
|
||||
pureMess as@(AvoidStruts ss) m
|
||||
| Just ToggleStruts <- fromMessage m = Just $ AvoidStruts (toggleAll ss)
|
||||
| Just (ToggleStrut s) <- fromMessage m = Just $ AvoidStruts (toggleOne s ss)
|
||||
| Just (SetStruts n k) <- fromMessage m
|
||||
, let newSS = S.fromList n `S.union` (ss S.\\ S.fromList k)
|
||||
, newSS /= ss = Just $ as { avoidStrutsDirection = newSS }
|
||||
| Just ClearGapCache <- fromMessage m = Just $ as { avoidStrutsRectCache = Nothing }
|
||||
, newSS /= ss = Just $ AvoidStruts newSS
|
||||
| Just UpdateDocks <- fromMessage m = Just as
|
||||
| otherwise = Nothing
|
||||
where toggleAll x | S.null x = S.fromList [minBound .. maxBound]
|
||||
| otherwise = S.empty
|
||||
|
@@ -88,9 +88,9 @@ positionStoreInit mDecoTheme w = withDisplay $ \d -> do
|
||||
else do
|
||||
sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
|
||||
let sr = screenRect . W.screenDetail $ sc
|
||||
sr' <- fmap ($ sr) (calcGap $ S.fromList [minBound .. maxBound]) -- take docks into account, accepting
|
||||
-- a somewhat unfortunate inter-dependency
|
||||
-- with 'XMonad.Hooks.ManageDocks'
|
||||
sr' <- fmap ($ sr) (calcGap $ S.fromList [minBound .. maxBound]) -- take docks into account, accepting
|
||||
-- a somewhat unfortunate inter-dependency
|
||||
-- with 'XMonad.Hooks.ManageDocks'
|
||||
modifyPosStore (\ps -> posStoreInsert ps w
|
||||
(Rectangle (fi $ wa_x wa) (fi (wa_y wa) - fi decoH)
|
||||
(fi $ wa_width wa) (decoH + fi (wa_height wa))) sr' )
|
||||
|
@@ -185,6 +185,7 @@ screenCornerLayoutHook = ModifiedLayout ScreenCornerLayout
|
||||
-- > ]
|
||||
--
|
||||
-- Then add layout hook:
|
||||
--
|
||||
-- > myLayout = screenCornerLayoutHook $ tiled ||| Mirror tiled ||| Full where
|
||||
-- > tiled = Tall nmaster delta ratio
|
||||
-- > nmaster = 1
|
||||
|
@@ -425,6 +425,7 @@ optimizeOrientation rct (t, cs) = Just (opt t rct, cs)
|
||||
|
||||
-- initially focused leaf, path from root to selected node, window ids of borders highlighting the selection
|
||||
data NodeRef = NodeRef { refLeaf :: Int, refPath :: [Direction2D], refWins :: [Window] } deriving (Show,Read,Eq)
|
||||
noRef :: NodeRef
|
||||
noRef = NodeRef (-1) [] []
|
||||
|
||||
goToNode :: NodeRef -> Zipper a -> Maybe (Zipper a)
|
||||
|
@@ -274,7 +274,7 @@ instance (LayoutClass l Window, LayoutClass l2 (Group l Window))
|
||||
results <- forM areas $ \(g, r') -> runLayout ws { W.layout = gLayout g
|
||||
, W.stack = gZipper g } r'
|
||||
|
||||
let hidden = map gLayout (W.integrate $ groups l) \\ map (gLayout . fst) areas
|
||||
let hidden = map gLayout (W.integrate $ groups _l) \\ map (gLayout . fst) areas
|
||||
hidden' <- mapM (flip handleMessage $ SomeMessage Hide) hidden
|
||||
|
||||
let placements = concatMap fst results
|
||||
|
@@ -114,7 +114,7 @@ zoomGroupReset = zoomColumnReset
|
||||
-- | Toggle whether the currently focused group should be maximized
|
||||
-- whenever it has focus.
|
||||
toggleGroupFull :: X ()
|
||||
toggleGroupFull = toggleGroupFull
|
||||
toggleGroupFull = toggleColumnFull
|
||||
|
||||
-- | Rotate the layouts in the focused group.
|
||||
groupToNextLayout :: X ()
|
||||
|
@@ -14,7 +14,7 @@
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, FlexibleContexts, PatternGuards #-}
|
||||
|
||||
module XMonad.Layout.IfMax
|
||||
( -- * Usage
|
||||
@@ -23,6 +23,10 @@ module XMonad.Layout.IfMax
|
||||
, ifMax
|
||||
) where
|
||||
|
||||
import Control.Applicative((<$>))
|
||||
import Control.Arrow
|
||||
import qualified Data.List as L
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe
|
||||
|
||||
import XMonad
|
||||
@@ -47,21 +51,36 @@ import qualified XMonad.StackSet as W
|
||||
data IfMax l1 l2 w = IfMax Int (l1 w) (l2 w)
|
||||
deriving (Read, Show)
|
||||
|
||||
instance (LayoutClass l1 a, LayoutClass l2 a) => LayoutClass (IfMax l1 l2) a where
|
||||
instance (LayoutClass l1 Window, LayoutClass l2 Window) => LayoutClass (IfMax l1 l2) Window where
|
||||
|
||||
runLayout (W.Workspace _ (IfMax n l1 l2) s) rect = arrange (W.integrate' s)
|
||||
runLayout (W.Workspace wname (IfMax n l1 l2) s) rect = withWindowSet $ \ws -> arrange (W.integrate' s) (M.keys . W.floating $ ws)
|
||||
where
|
||||
arrange [] = do l1' <- maybe l1 id `fmap` handleMessage l1 (SomeMessage ReleaseResources)
|
||||
l2' <- maybe l2 id `fmap` handleMessage l2 (SomeMessage ReleaseResources)
|
||||
return ([], Just $ IfMax n l1' l2')
|
||||
arrange ws | length ws <= n = do
|
||||
(wrs, ml1') <- runLayout (W.Workspace "" l1 s) rect
|
||||
arrange ws fw | length (ws L.\\ fw) <= n = do
|
||||
(wrs, ml1') <- runLayout (W.Workspace wname l1 s) rect
|
||||
let l1' = fromMaybe l1 ml1'
|
||||
return (wrs, Just $ IfMax n l1' l2)
|
||||
| otherwise = do
|
||||
(wrs, ml2') <- runLayout (W.Workspace "" l2 s) rect
|
||||
l2' <- fromMaybe l2 <$> handleMessage l2 (SomeMessage Hide)
|
||||
return (wrs, Just $ IfMax n l1' l2')
|
||||
| otherwise = do
|
||||
(wrs, ml2') <- runLayout (W.Workspace wname l2 s) rect
|
||||
l1' <- fromMaybe l1 <$> handleMessage l1 (SomeMessage Hide)
|
||||
let l2' = fromMaybe l2 ml2'
|
||||
return (wrs, Just $ IfMax n l1 l2')
|
||||
return (wrs, Just $ IfMax n l1' l2')
|
||||
|
||||
handleMessage (IfMax n l1 l2) m | Just ReleaseResources <- fromMessage m = do
|
||||
l1' <- handleMessage l1 (SomeMessage ReleaseResources)
|
||||
l2' <- handleMessage l2 (SomeMessage ReleaseResources)
|
||||
if isNothing l1' && isNothing l2'
|
||||
then return Nothing
|
||||
else return $ Just $ IfMax n (fromMaybe l1 l1') (fromMaybe l2 l2')
|
||||
handleMessage (IfMax n l1 l2) m = do
|
||||
(allWindows, floatingWindows) <- gets ((W.integrate' . W.stack . W.workspace . W.current &&& M.keys . W.floating) . windowset)
|
||||
if length (allWindows L.\\ floatingWindows) <= n
|
||||
then do
|
||||
l1' <- handleMessage l1 m
|
||||
return $ flip (IfMax n) l2 <$> l1'
|
||||
else do
|
||||
l2' <- handleMessage l2 m
|
||||
return $ IfMax n l1 <$> l2'
|
||||
|
||||
description (IfMax n l1 l2) = "If number of windows is <= " ++ show n ++ ", then " ++
|
||||
description l1 ++ ", else " ++ description l2
|
||||
|
@@ -1,11 +1,26 @@
|
||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances, PatternGuards, DeriveDataTypeable #-}
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE PatternGuards #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
{-# LANGUAGE UndecidableInstances #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.LayoutBuilder
|
||||
-- Copyright : (c) 2009 Anders Engstrom <ankaan@gmail.com>
|
||||
--
|
||||
-- Copyright : (c) 2009 Anders Engstrom <ankaan@gmail.com>,
|
||||
-- 2011 Ilya Portnov <portnov84@rambler.ru>,
|
||||
-- 2015 Peter Jones <pjones@devalot.com>
|
||||
--
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Anders Engstrom <ankaan@gmail.com>
|
||||
-- Maintainer : Anders Engstrom <ankaan@gmail.com>,
|
||||
-- Ilya Portnov <portnov84@rambler.ru>,
|
||||
-- Peter Jones <pjones@devalot.com>
|
||||
--
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
@@ -17,25 +32,40 @@
|
||||
-- ("XMonad.Layout.LayoutHints", "XMonad.Layout.HintedGrid" etc.)
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.LayoutBuilder (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
layoutN,
|
||||
layoutR,
|
||||
layoutP,
|
||||
layoutAll,
|
||||
|
||||
-- * Selecting Windows
|
||||
-- $selectWin
|
||||
Predicate (..),
|
||||
Proxy(..),
|
||||
|
||||
-- * Messages
|
||||
IncLayoutN (..),
|
||||
|
||||
-- * Utilities
|
||||
SubMeasure (..),
|
||||
SubBox (..),
|
||||
absBox,
|
||||
relBox,
|
||||
LayoutB,
|
||||
LayoutN,
|
||||
) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
import Control.Applicative ((<|>))
|
||||
import Control.Monad (foldM)
|
||||
import Data.Maybe
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import Data.Maybe (isJust,isNothing,listToMaybe)
|
||||
import XMonad.Util.WindowProperties
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
@@ -89,56 +119,126 @@ import Data.Maybe (isJust,isNothing,listToMaybe)
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
type WindowNum = Either Int (Rational,Rational)
|
||||
--------------------------------------------------------------------------------
|
||||
-- $selectWin
|
||||
--
|
||||
-- 'Predicate' exists because layouts are required to be serializable, and
|
||||
-- "XMonad.Util.WindowProperties" is not sufficient (for example it does not
|
||||
-- allow using regular expressions).
|
||||
--
|
||||
-- compare "XMonad.Util.Invisible"
|
||||
|
||||
-- | Use one layout in the specified area for a number of windows and possibly let another layout handle the rest.
|
||||
data LayoutN l1 l2 a =
|
||||
LayoutN (Maybe a) (Maybe a) WindowNum SubBox (Maybe SubBox) (l1 a) (Maybe (l2 a))
|
||||
deriving (Show,Read)
|
||||
-- | Type class for predicates. This enables us to manage not only Windows,
|
||||
-- but any objects, for which instance Predicate is defined.
|
||||
--
|
||||
-- Another instance exists in XMonad.Util.WindowPropertiesRE in xmonad-extras
|
||||
class Predicate p w where
|
||||
alwaysTrue :: Proxy w -> p -- ^ A predicate that is always True.
|
||||
checkPredicate :: p -> w -> X Bool -- ^ Check if given object (window or smth else) matches that predicate
|
||||
|
||||
-- | Use the specified layout in the described area for N windows and send the rest of the windows to the next layout in the chain.
|
||||
-- It is possible to supply an alternative area that will then be used instead, if there are no windows to send to the next layout.
|
||||
instance Predicate () a where
|
||||
alwaysTrue _ = ()
|
||||
checkPredicate _ _ = return True
|
||||
|
||||
instance Predicate Property Window where
|
||||
alwaysTrue _ = Const True
|
||||
checkPredicate = hasProperty
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Contains no actual data, but is needed to help select the correct instance
|
||||
-- of 'Predicate'
|
||||
data Proxy a = Proxy
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Information about how to split windows between layouts.
|
||||
data Limit p = LimitN Int -- ^ See: 'layoutN'.
|
||||
| LimitR (Rational, Rational) -- ^ See: 'layoutR'.
|
||||
| LimitP p -- ^ See: 'layoutP'.
|
||||
deriving (Show, Read)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Use one layout in the specified area for a number of windows and
|
||||
-- possibly let another layout handle the rest.
|
||||
data LayoutB l1 l2 p a = LayoutB
|
||||
{ subFocus :: Maybe a -- ^ The focused window in this layout.
|
||||
, nextFocus :: Maybe a -- ^ The focused window in the next layout.
|
||||
, limit :: Limit p -- ^ How to split windows between layouts.
|
||||
, box :: SubBox -- ^ Normal size of layout.
|
||||
, mbox :: Maybe SubBox -- ^ Size of layout when handling all windows.
|
||||
, sub :: l1 a -- ^ The layout to use in this box.
|
||||
, next :: Maybe (l2 a) -- ^ The next layout in the chain.
|
||||
} deriving (Show, Read)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | A variant of 'LayoutB' that can't use 'layoutP'. For backwards
|
||||
-- compatibility with previous versions of LayoutBuilder.
|
||||
type LayoutN l1 l2 a = LayoutB l1 l2 () a
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Use the specified layout in the described area for N windows and
|
||||
-- send the rest of the windows to the next layout in the chain. It
|
||||
-- is possible to supply an alternative area that will then be used
|
||||
-- instead, if there are no windows to send to the next layout.
|
||||
layoutN :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a) =>
|
||||
Int -- ^ The number of windows to handle
|
||||
-> SubBox -- ^ The box to place the windows in
|
||||
-> Maybe SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
-> LayoutN l2 l3 a -- ^ Where to send the remaining windows
|
||||
-> LayoutN l1 (LayoutN l2 l3) a -- ^ The resulting layout
|
||||
layoutN num box mbox sub next = LayoutN Nothing Nothing (Left num) box mbox sub (Just next)
|
||||
Int -- ^ The number of windows to handle
|
||||
-> SubBox -- ^ The box to place the windows in
|
||||
-> Maybe SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
-> LayoutB l2 l3 p a -- ^ Where to send the remaining windows
|
||||
-> LayoutB l1 (LayoutB l2 l3 p) () a -- ^ The resulting layout
|
||||
layoutN num box mbox sub next = LayoutB Nothing Nothing (LimitN num) box mbox sub (Just next)
|
||||
|
||||
-- | As layoutN, but the number of windows is given relative to the total number of windows remaining to be handled. The first
|
||||
-- argument is how much to change the ratio when using IncLayoutN, and the second is the initial ratio.
|
||||
layoutR :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a) =>
|
||||
Rational -- ^ How much to change the ratio with each IncLayoutN
|
||||
-> Rational -- ^ The ratio of the remaining windows to handle
|
||||
-> SubBox -- ^ The box to place the windows in
|
||||
-> Maybe SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
-> LayoutN l2 l3 a -- ^ Where to send the remaining windows
|
||||
-> LayoutN l1 (LayoutN l2 l3) a -- ^ The resulting layout
|
||||
layoutR numdiff num box mbox sub next = LayoutN Nothing Nothing (Right (numdiff,num)) box mbox sub (Just next)
|
||||
Rational -- ^ How much to change the ratio with each IncLayoutN
|
||||
-> Rational -- ^ The ratio of the remaining windows to handle
|
||||
-> SubBox -- ^ The box to place the windows in
|
||||
-> Maybe SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
-> LayoutB l2 l3 p a -- ^ Where to send the remaining windows
|
||||
-> LayoutB l1 (LayoutB l2 l3 p) p a -- ^ The resulting layout
|
||||
layoutR numdiff num box mbox sub next = LayoutB Nothing Nothing (LimitR (numdiff,num)) box mbox sub (Just next)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Use the specified layout in the described area windows that match
|
||||
-- given predicate and send the rest of the windows to the next layout
|
||||
-- in the chain. It is possible to supply an alternative area that
|
||||
-- will then be used instead, if there are no windows to send to the
|
||||
-- next layout.
|
||||
layoutP :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a, Predicate p a, Predicate p' a) =>
|
||||
p -- ^ The predicate to use
|
||||
-> SubBox -- ^ The box to place the windows in
|
||||
-> Maybe SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
-> LayoutB l2 l3 p' a -- ^ Where to send the remaining windows
|
||||
-> LayoutB l1 (LayoutB l2 l3 p') p a -- ^ The resulting layout
|
||||
layoutP prop box mbox sub next = LayoutB Nothing Nothing (LimitP prop) box mbox sub (Just next)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Use the specified layout in the described area for all remaining windows.
|
||||
layoutAll :: (Read a, Eq a, LayoutClass l1 a) =>
|
||||
SubBox -- ^ The box to place the windows in
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
-> LayoutN l1 Full a -- ^ The resulting layout
|
||||
layoutAll box sub = LayoutN Nothing Nothing (Right (0,1)) box Nothing sub Nothing
|
||||
SubBox -- ^ The box to place the windows in
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
-> LayoutB l1 Full () a -- ^ The resulting layout
|
||||
layoutAll box sub = LayoutB Nothing Nothing (LimitR (0,1)) box Nothing sub Nothing
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Change the number of windows handled by the focused layout.
|
||||
data IncLayoutN = IncLayoutN Int deriving Typeable
|
||||
instance Message IncLayoutN
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | The absolute or relative measures used to describe the area a layout should be placed in. For negative absolute values
|
||||
-- the total remaining space will be added. For sizes, the remaining space will also be added for zeroes. Relative values
|
||||
-- are applied on the remaining space after the top-left corner of the box have been removed.
|
||||
data SubMeasure = Abs Int | Rel Rational deriving (Show,Read)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | A box to place a layout in. The stored values are xpos, ypos, width and height.
|
||||
data SubBox = SubBox SubMeasure SubMeasure SubMeasure SubMeasure deriving (Show,Read)
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Create a box with only absolute measurements. If the values are negative, the total remaining space will be added. For
|
||||
-- sizes it will also be added for zeroes.
|
||||
absBox :: Int -- ^ Absolute X-Position
|
||||
@@ -148,7 +248,7 @@ absBox :: Int -- ^ Absolute X-Position
|
||||
-> SubBox -- ^ The resulting 'SubBox' describing the area
|
||||
absBox x y w h = SubBox (Abs x) (Abs y) (Abs w) (Abs h)
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Create a box with only relative measurements.
|
||||
relBox :: Rational -- ^ Relative X-Position with respect to the surrounding area
|
||||
-> Rational -- ^ Relative Y-Position with respect to the surrounding area
|
||||
@@ -157,138 +257,209 @@ relBox :: Rational -- ^ Relative X-Position with respect to the surrounding are
|
||||
-> SubBox -- ^ The resulting 'SubBox' describing the area
|
||||
relBox x y w h = SubBox (Rel x) (Rel y) (Rel w) (Rel h)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
instance ( LayoutClass l1 a, LayoutClass l2 a
|
||||
, Read a, Show a, Show p, Eq a, Typeable a, Predicate p a
|
||||
) => LayoutClass (LayoutB l1 l2 p) a where
|
||||
|
||||
instance (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) =>
|
||||
LayoutClass (LayoutN l1 l2) a where
|
||||
-- | Update window locations.
|
||||
runLayout (W.Workspace _ LayoutB {..} s) rect = do
|
||||
(subs, nexts, subFocus', nextFocus') <- splitStack s limit subFocus nextFocus
|
||||
|
||||
-- | Update window locations.
|
||||
runLayout (W.Workspace _ (LayoutN subf nextf num box mbox sub next) s) rect
|
||||
= do let (subs,nexts,subf',nextf') = splitStack s num subf nextf
|
||||
selBox = if isJust nextf'
|
||||
then box
|
||||
else maybe box id mbox
|
||||
let selBox = if isJust nextFocus' then box else fromMaybe box mbox
|
||||
|
||||
(sublist,sub',schange) <- handle sub subs $ calcArea selBox rect
|
||||
(sublist, sub', schange) <- handle sub subs (calcArea selBox rect)
|
||||
|
||||
(nextlist,next',nchange) <- case next of Nothing -> return ([], Nothing, False)
|
||||
Just n -> do (res, l, ch) <- handle n nexts rect
|
||||
return (res, Just l, ch)
|
||||
(nextlist, next', nchange) <- case next of
|
||||
Nothing -> return ([], Nothing, False)
|
||||
Just n -> do (res, l, ch) <- handle n nexts rect
|
||||
return (res, Just l, ch)
|
||||
|
||||
let newlist = if (length $ maybe [] W.up s) < (length $ W.integrate' subs)
|
||||
then sublist++nextlist
|
||||
else nextlist++sublist
|
||||
newstate = if subf' /= subf || nextf' /= nextf || schange || nchange
|
||||
then Just $ LayoutN subf' nextf' num box mbox sub' next'
|
||||
else Nothing
|
||||
let newlist = if length (maybe [] W.up s) < length (W.integrate' subs)
|
||||
then sublist++nextlist
|
||||
else nextlist++sublist
|
||||
|
||||
return (newlist, newstate)
|
||||
where
|
||||
handle l s' r = do (res,ml) <- runLayout (W.Workspace "" l s') r
|
||||
l' <- return $ maybe l id ml
|
||||
return (res, l', isNothing ml)
|
||||
newstate = if subFocus' /= subFocus || nextFocus' /= nextFocus || schange || nchange
|
||||
then Just $ LayoutB subFocus' nextFocus' limit box mbox sub' next'
|
||||
else Nothing
|
||||
|
||||
-- | Propagate messages.
|
||||
handleMessage l m
|
||||
| Just (IncLayoutN _) <- fromMessage m = windowNum l m
|
||||
| Just (IncMasterN _) <- fromMessage m = sendFocus l m
|
||||
| Just (Shrink) <- fromMessage m = sendFocus l m
|
||||
| Just (Expand) <- fromMessage m = sendFocus l m
|
||||
| otherwise = sendBoth l m
|
||||
return (newlist, newstate)
|
||||
where
|
||||
handle l s' r = do (res,ml) <- runLayout (W.Workspace "" l s') r
|
||||
return (res, fromMaybe l ml, isNothing ml)
|
||||
|
||||
-- | Descriptive name for layout.
|
||||
description (LayoutN _ _ _ _ _ sub Nothing) = "layoutAll "++ description sub
|
||||
description (LayoutN _ _ (Left _) _ _ sub (Just next)) = "layoutN "++ description sub ++" "++ description next
|
||||
description (LayoutN _ _ (Right _) _ _ sub (Just next)) = "layoutR "++ description sub ++" "++ description next
|
||||
-- | Propagate messages.
|
||||
handleMessage l m
|
||||
| Just (IncLayoutN n) <- fromMessage m = incLayoutN l m n
|
||||
| Just (IncMasterN _) <- fromMessage m = sendFocus l m
|
||||
| Just Shrink <- fromMessage m = sendFocus l m
|
||||
| Just Expand <- fromMessage m = sendFocus l m
|
||||
| otherwise = sendBoth l m
|
||||
|
||||
-- | Descriptive name for layout.
|
||||
description layout = case layout of
|
||||
(LayoutB _ _ _ _ _ sub Nothing) ->
|
||||
"layoutAll " ++ description sub
|
||||
|
||||
windowNum :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutN l1 l2 a -> SomeMessage -> X (Maybe (LayoutN l1 l2 a))
|
||||
windowNum l@(LayoutN subf nextf num box mbox subl nextl) m | (Just (IncLayoutN n)) <- fromMessage m =
|
||||
do foc <- isFocus subf
|
||||
if foc then do let newnum = case num of
|
||||
(Left oldnum) -> Left $ max 1 $ oldnum + n
|
||||
(Right (diff,oldnum)) -> Right (diff, min 1 $ max 0 $ oldnum + (fromIntegral n)*diff)
|
||||
return $ Just $ LayoutN subf nextf newnum box mbox subl nextl
|
||||
else sendNext l m
|
||||
windowNum l m = sendNext l m
|
||||
(LayoutB _ _ (LimitN _) _ _ sub (Just next)) ->
|
||||
"layoutN " ++ description sub ++ " " ++ description next
|
||||
|
||||
sendSub :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutN l1 l2 a -> SomeMessage -> X (Maybe (LayoutN l1 l2 a))
|
||||
sendSub (LayoutN subf nextf num box mbox sub next) m =
|
||||
(LayoutB _ _ (LimitR _) _ _ sub (Just next)) ->
|
||||
"layoutR " ++ description sub ++ " " ++ description next
|
||||
|
||||
(LayoutB _ _ (LimitP _) _ _ sub (Just next)) ->
|
||||
"layoutP " ++ description sub ++ " " ++ description next
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Increase the number of windows allowed in the focused layout.
|
||||
incLayoutN :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a)
|
||||
=> LayoutB l1 l2 p a
|
||||
-> SomeMessage
|
||||
-> Int
|
||||
-> X (Maybe (LayoutB l1 l2 p a))
|
||||
incLayoutN layout@LayoutB {..} message n = do
|
||||
incThis <- isFocus subFocus
|
||||
|
||||
if incThis
|
||||
then return $ Just layout { limit = newLimit }
|
||||
else sendNext layout message
|
||||
|
||||
where
|
||||
newLimit = case limit of
|
||||
LimitN oldnum -> LimitN (max 1 $ oldnum + n)
|
||||
LimitR (diff, oldnum) -> LimitR (diff, min 1 $ max 0 $ oldnum + fromIntegral n * diff)
|
||||
LimitP _ -> limit
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
sendSub :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutB l1 l2 p a -> SomeMessage -> X (Maybe (LayoutB l1 l2 p a))
|
||||
sendSub (LayoutB subFocus nextFocus num box mbox sub next) m =
|
||||
do sub' <- handleMessage sub m
|
||||
return $ if isJust sub'
|
||||
then Just $ LayoutN subf nextf num box mbox (maybe sub id sub') next
|
||||
then Just $ LayoutB subFocus nextFocus num box mbox (fromMaybe sub sub') next
|
||||
else Nothing
|
||||
|
||||
sendBoth :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutN l1 l2 a -> SomeMessage -> X (Maybe (LayoutN l1 l2 a))
|
||||
sendBoth l@(LayoutN _ _ _ _ _ _ Nothing) m = sendSub l m
|
||||
sendBoth (LayoutN subf nextf num box mbox sub (Just next)) m =
|
||||
--------------------------------------------------------------------------------
|
||||
sendBoth :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutB l1 l2 p a -> SomeMessage -> X (Maybe (LayoutB l1 l2 p a))
|
||||
sendBoth l@(LayoutB _ _ _ _ _ _ Nothing) m = sendSub l m
|
||||
sendBoth (LayoutB subFocus nextFocus num box mbox sub (Just next)) m =
|
||||
do sub' <- handleMessage sub m
|
||||
next' <- handleMessage next m
|
||||
return $ if isJust sub' || isJust next'
|
||||
then Just $ LayoutN subf nextf num box mbox (maybe sub id sub') (Just $ maybe next id next')
|
||||
then Just $ LayoutB subFocus nextFocus num box mbox (fromMaybe sub sub') (next' <|> Just next)
|
||||
else Nothing
|
||||
|
||||
sendNext :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutN l1 l2 a -> SomeMessage -> X (Maybe (LayoutN l1 l2 a))
|
||||
sendNext (LayoutN _ _ _ _ _ _ Nothing) _ = return Nothing
|
||||
sendNext (LayoutN subf nextf num box mbox sub (Just next)) m =
|
||||
--------------------------------------------------------------------------------
|
||||
sendNext :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutB l1 l2 p a -> SomeMessage -> X (Maybe (LayoutB l1 l2 p a))
|
||||
sendNext (LayoutB _ _ _ _ _ _ Nothing) _ = return Nothing
|
||||
sendNext (LayoutB subFocus nextFocus num box mbox sub (Just next)) m =
|
||||
do next' <- handleMessage next m
|
||||
return $ if isJust next'
|
||||
then Just $ LayoutN subf nextf num box mbox sub next'
|
||||
then Just $ LayoutB subFocus nextFocus num box mbox sub next'
|
||||
else Nothing
|
||||
|
||||
sendFocus :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutN l1 l2 a -> SomeMessage -> X (Maybe (LayoutN l1 l2 a))
|
||||
sendFocus l@(LayoutN subf _ _ _ _ _ _) m = do foc <- isFocus subf
|
||||
if foc then sendSub l m
|
||||
else sendNext l m
|
||||
--------------------------------------------------------------------------------
|
||||
sendFocus :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutB l1 l2 p a -> SomeMessage -> X (Maybe (LayoutB l1 l2 p a))
|
||||
sendFocus l@(LayoutB subFocus _ _ _ _ _ _) m = do
|
||||
foc <- isFocus subFocus
|
||||
|
||||
if foc
|
||||
then sendSub l m
|
||||
else sendNext l m
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Check to see if the given window is currently focused.
|
||||
isFocus :: (Show a) => Maybe a -> X Bool
|
||||
isFocus Nothing = return False
|
||||
isFocus (Just w) = do ms <- (W.stack . W.workspace . W.current) `fmap` gets windowset
|
||||
return $ maybe False (\s -> show w == (show $ W.focus s)) ms
|
||||
return $ maybe False (\s -> show w == show (W.focus s)) ms
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
calcNum :: Int -> Limit p -> Int
|
||||
calcNum tot num = max 1 $ case num of LimitN i -> i
|
||||
LimitR (_,r) -> ceiling $ r * fromIntegral tot
|
||||
LimitP _ -> 1
|
||||
|
||||
calcNum :: Int -> WindowNum -> Int
|
||||
calcNum tot num = max 1 $ case num of Left i -> i
|
||||
Right (_,r) -> ceiling $ r * fromIntegral tot
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Split given list of objects (i.e. windows) using predicate.
|
||||
splitBy :: (Predicate p a) => p -> [a] -> X ([a], [a])
|
||||
splitBy prop = foldM step ([], [])
|
||||
where
|
||||
step (good, bad) w = do
|
||||
ok <- checkPredicate prop w
|
||||
return $ if ok
|
||||
then (w:good, bad)
|
||||
else (good, w:bad)
|
||||
|
||||
splitStack :: Eq a => Maybe (W.Stack a) -> WindowNum -> Maybe a -> Maybe a -> (Maybe (W.Stack a),Maybe (W.Stack a),Maybe a,Maybe a)
|
||||
splitStack Nothing _ _ _ = (Nothing,Nothing,Nothing,Nothing)
|
||||
splitStack (Just s) num subf nextf = ( differentiate' subf' subl
|
||||
, differentiate' nextf' nextl
|
||||
, subf'
|
||||
, nextf'
|
||||
)
|
||||
where
|
||||
ws = W.integrate s
|
||||
n = calcNum (length ws) num
|
||||
subl = take n ws
|
||||
nextl = drop n ws
|
||||
subf' = foc subl subf
|
||||
nextf' = foc nextl nextf
|
||||
foc [] _ = Nothing
|
||||
foc l f | W.focus s `elem` l = Just $ W.focus s
|
||||
| maybe False (`elem` l) f = f
|
||||
| otherwise = listToMaybe l
|
||||
--------------------------------------------------------------------------------
|
||||
splitStack :: forall a p. (Eq a, Predicate p a)
|
||||
=> Maybe (W.Stack a) -- ^ Window set.
|
||||
-> Limit p -- ^ How to split the stack.
|
||||
-> Maybe a -- ^ The window that was focused in this layout.
|
||||
-> Maybe a -- ^ The window that was focused in the next layout.
|
||||
-> X (Maybe (W.Stack a), Maybe (W.Stack a), Maybe a, Maybe a)
|
||||
splitStack Nothing _ _ _ = return (Nothing, Nothing, Nothing, Nothing)
|
||||
splitStack (Just s) limit subFocus nextFocus =
|
||||
case limit of
|
||||
LimitN _ -> splitN
|
||||
LimitR _ -> splitN
|
||||
LimitP prop -> splitP prop
|
||||
|
||||
where
|
||||
ws = W.integrate s
|
||||
n = calcNum (length ws) limit
|
||||
subl = take n ws
|
||||
nextl = drop n ws
|
||||
subFocus' xs = foc xs subFocus
|
||||
nextFocus' xs = foc xs nextFocus
|
||||
|
||||
-- Pick a new focused window if necessary.
|
||||
foc :: [a] -> Maybe a -> Maybe a
|
||||
foc [] _ = Nothing
|
||||
foc l f | W.focus s `elem` l = Just (W.focus s)
|
||||
| maybe False (`elem` l) f = f
|
||||
| otherwise = listToMaybe l
|
||||
|
||||
-- Split based on max number of windows.
|
||||
splitN = return ( differentiate' (subFocus' subl) subl
|
||||
, differentiate' (nextFocus' nextl) nextl
|
||||
, subFocus' subl
|
||||
, nextFocus' nextl
|
||||
)
|
||||
|
||||
-- Split based on a predicate.
|
||||
splitP prop = do
|
||||
(this, other) <- splitBy prop ws
|
||||
return ( differentiate' (subFocus' this) this
|
||||
, differentiate' (nextFocus' other) other
|
||||
, subFocus' this
|
||||
, nextFocus' other
|
||||
)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
calcArea :: SubBox -> Rectangle -> Rectangle
|
||||
calcArea (SubBox xpos ypos width height) rect = Rectangle (rect_x rect + fromIntegral xpos') (rect_y rect + fromIntegral ypos') width' height'
|
||||
where
|
||||
xpos' = calc False xpos $ rect_width rect
|
||||
ypos' = calc False ypos $ rect_height rect
|
||||
width' = calc True width $ rect_width rect - xpos'
|
||||
height' = calc True height $ rect_height rect - ypos'
|
||||
calcArea (SubBox xpos ypos width height) rect =
|
||||
Rectangle (rect_x rect + fromIntegral xpos')
|
||||
(rect_y rect + fromIntegral ypos')
|
||||
width' height'
|
||||
where
|
||||
xpos' = calc False xpos $ rect_width rect
|
||||
ypos' = calc False ypos $ rect_height rect
|
||||
width' = calc True width $ rect_width rect - xpos'
|
||||
height' = calc True height $ rect_height rect - ypos'
|
||||
|
||||
calc zneg val tot = fromIntegral $ min (fromIntegral tot) $ max 0 $
|
||||
case val of Rel v -> floor $ v * fromIntegral tot
|
||||
Abs v -> if v<0 || (zneg && v==0)
|
||||
then (fromIntegral tot)+v
|
||||
else v
|
||||
calc zneg val tot = fromIntegral $ min (fromIntegral tot) $ max 0 $
|
||||
case val of Rel v -> floor $ v * fromIntegral tot
|
||||
Abs v -> if v<0 || (zneg && v==0)
|
||||
then fromIntegral tot + v
|
||||
else v
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
differentiate' :: Eq q => Maybe q -> [q] -> Maybe (W.Stack q)
|
||||
differentiate' _ [] = Nothing
|
||||
differentiate' Nothing w = W.differentiate w
|
||||
differentiate' (Just f) w
|
||||
| f `elem` w = Just $ W.Stack { W.focus = f
|
||||
, W.up = reverse $ takeWhile (/=f) w
|
||||
, W.down = tail $ dropWhile (/=f) w
|
||||
}
|
||||
| f `elem` w = Just W.Stack { W.focus = f
|
||||
, W.up = reverse $ takeWhile (/=f) w
|
||||
, W.down = tail $ dropWhile (/=f) w
|
||||
}
|
||||
| otherwise = W.differentiate w
|
||||
|
@@ -9,12 +9,11 @@
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- A layout combinator that sends windows matching given predicate to one rectangle
|
||||
-- and the rest to another.
|
||||
-- DEPRECATED. Use 'XMonad.Layout.LayoutBuilder' instead.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.LayoutBuilderP (
|
||||
module XMonad.Layout.LayoutBuilderP {-# DEPRECATED "Use XMonad.Layout.LayoutBuilder instead" #-} (
|
||||
LayoutP (..),
|
||||
layoutP, layoutAll,
|
||||
B.relBox, B.absBox,
|
||||
@@ -59,6 +58,7 @@ data LayoutP p l1 l2 a =
|
||||
|
||||
-- | Use the specified layout in the described area windows that match given predicate and send the rest of the windows to the next layout in the chain.
|
||||
-- It is possible to supply an alternative area that will then be used instead, if there are no windows to send to the next layout.
|
||||
{-# DEPRECATED layoutP "Use XMonad.Layout.LayoutBuilder.layoutP instead." #-}
|
||||
layoutP :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a, Predicate p a) =>
|
||||
p
|
||||
-> B.SubBox -- ^ The box to place the windows in
|
||||
@@ -69,6 +69,7 @@ layoutP :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a,
|
||||
layoutP prop box mbox sub next = LayoutP Nothing Nothing prop box mbox sub (Just next)
|
||||
|
||||
-- | Use the specified layout in the described area for all remaining windows.
|
||||
{-# DEPRECATED layoutAll "Use XMonad.Layout.LayoutBuilder.layoutAll instead." #-}
|
||||
layoutAll :: forall l1 p a. (Read a, Eq a, LayoutClass l1 a, Predicate p a) =>
|
||||
B.SubBox -- ^ The box to place the windows in
|
||||
-> l1 a -- ^ The layout to use in the specified area
|
||||
@@ -207,4 +208,3 @@ differentiate' (Just f) w
|
||||
instance Predicate Property Window where
|
||||
alwaysTrue _ = Const True
|
||||
checkPredicate = hasProperty
|
||||
|
||||
|
@@ -50,7 +50,7 @@ data Rename a = CutLeft Int -- ^ Remove a number of characters from the left
|
||||
-- if necessary
|
||||
| PrependWords String -- ^ Add a string to the left, appending a space to it if
|
||||
-- necessary
|
||||
| Replace String -- ^ Repace with another wtring
|
||||
| Replace String -- ^ Replace with another string
|
||||
| Chain [Rename a] -- ^ Apply a list of modifications in left-to-right order
|
||||
deriving (Show, Read, Eq)
|
||||
|
||||
@@ -68,4 +68,4 @@ apply (PrependWords s') s = unwords $ s' : words s
|
||||
apply (Chain rs) s = ($s) $ foldr (flip (.)) id $ map apply rs
|
||||
|
||||
instance LayoutModifier Rename a where
|
||||
modifyDescription r l = apply r (description l)
|
||||
modifyDescription r l = apply r (description l)
|
||||
|
88
XMonad/Layout/SortedLayout.hs
Normal file
88
XMonad/Layout/SortedLayout.hs
Normal file
@@ -0,0 +1,88 @@
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE TypeSynonymInstances #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.SortedLayout
|
||||
-- Copyright : (c) 2016 Kurt Dietrich
|
||||
-- License : BSD-style (see xmonad/LICENSE)
|
||||
--
|
||||
-- Maintainer : kurto@mac.com
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- A 'LayoutModifier' that sorts the windows in another layout, given a
|
||||
-- list of properties. The order of properties in the list determines
|
||||
-- the order of windows in the final layout. Any unmatched windows
|
||||
-- go to the end of the order.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.SortedLayout
|
||||
( -- *Usage:
|
||||
-- $usage
|
||||
sorted
|
||||
, Property(..)
|
||||
) where
|
||||
|
||||
import Control.Monad
|
||||
import Data.Functor ((<$>))
|
||||
import Data.List
|
||||
|
||||
import XMonad
|
||||
import XMonad.Layout.LayoutModifier
|
||||
import XMonad.StackSet as W
|
||||
import XMonad.Util.WindowProperties
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your
|
||||
-- @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.SortedLayout
|
||||
--
|
||||
-- Then edit your @layoutHook@ to sort another layout (in this case, 'XMonad.Layout.Grid.Grid'):
|
||||
--
|
||||
-- > myLayout = sorted [ClassName "Firefox", ClassName "URxvt"] Grid
|
||||
-- > main = xmonad def { layoutHook = myLayout }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
|
||||
|
||||
-- | Modify a layout using a list of properties to sort its windows.
|
||||
sorted :: [Property]
|
||||
-> l a
|
||||
-> ModifiedLayout SortedLayout l a
|
||||
sorted props = ModifiedLayout . SortedLayout $ props ++ [Const True]
|
||||
|
||||
data WindowDescriptor = WindowDescriptor { wdSeqn :: !Integer
|
||||
, wdProp :: !Property
|
||||
, wdId :: !Window
|
||||
} deriving (Show, Read)
|
||||
|
||||
instance Eq WindowDescriptor where
|
||||
(==) a b = wdId a == wdId b
|
||||
|
||||
instance Ord WindowDescriptor where
|
||||
compare a b = compare (wdSeqn a) (wdSeqn b)
|
||||
|
||||
data SortedLayout a = SortedLayout [Property] deriving (Show, Read)
|
||||
|
||||
instance LayoutModifier SortedLayout Window where
|
||||
modifyLayout (SortedLayout props) = sortLayout props
|
||||
modifierDescription _ = "Sorted"
|
||||
|
||||
findMatchingWindows :: Integer -> Property -> [Window] -> X [WindowDescriptor]
|
||||
findMatchingWindows seqn prop wids = fmap (fmap (WindowDescriptor seqn prop)) matching where
|
||||
matching = filterM (hasProperty prop) wids
|
||||
|
||||
sortLayout :: (LayoutClass l Window)
|
||||
=> [Property]
|
||||
-> W.Workspace WorkspaceId (l Window) Window
|
||||
-> Rectangle
|
||||
-> X ([(Window, Rectangle)], Maybe (l Window))
|
||||
sortLayout props (W.Workspace w l r) rect = do
|
||||
let wids = W.integrate' r
|
||||
sortedWids <- map wdId . nub . sort . concat <$> zipWithM (\s p -> findMatchingWindows s p wids) [0..] props
|
||||
let sr = W.differentiate sortedWids
|
||||
runLayout (W.Workspace w l sr) rect
|
@@ -93,7 +93,7 @@ instance LayoutModifier SpacingWithEdge a where
|
||||
modifierDescription (SpacingWithEdge p) = "SpacingWithEdge " ++ show p
|
||||
|
||||
shrinkRect :: Int -> Rectangle -> Rectangle
|
||||
shrinkRect p (Rectangle x y w h) = Rectangle (x+fi p) (y+fi p) (w-2*fi p) (h-2*fi p)
|
||||
shrinkRect p (Rectangle x y w h) = Rectangle (x+fi p) (y+fi p) (fi $ max 1 $ fi w-2*p) (fi $ max 1 $ fi h-2*p)
|
||||
|
||||
-- | Surrounds all windows with blank space, except when the window is the only
|
||||
-- visible window on the current workspace.
|
||||
|
@@ -35,6 +35,9 @@
|
||||
-- (Linux-centric) approach may be used. See
|
||||
-- <https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt>
|
||||
--
|
||||
-- * Note
|
||||
-- This module doesn't work on programs that do fancy things with processes
|
||||
-- (such as Chromium) and programs that do not set _NET_WM_PID.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.Stoppable
|
||||
|
@@ -4,6 +4,7 @@
|
||||
-- |
|
||||
-- Module : XMonad.Prompt
|
||||
-- Copyright : (C) 2007 Andrea Rossato, 2015 Evgeny Kurnevsky
|
||||
-- 2015 Sibi Prabakaran
|
||||
-- License : BSD3
|
||||
--
|
||||
-- Maintainer : Spencer Janssen <spencerjanssen@gmail.com>
|
||||
@@ -126,14 +127,14 @@ data XPState =
|
||||
}
|
||||
|
||||
data XPConfig =
|
||||
XPC { font :: String -- ^ Font
|
||||
XPC { font :: String -- ^ Font; use the prefix @"xft:"@ for TrueType fonts
|
||||
, bgColor :: String -- ^ Background color
|
||||
, fgColor :: String -- ^ Font color
|
||||
, fgHLight :: String -- ^ Font color of a highlighted completion entry
|
||||
, bgHLight :: String -- ^ Background color of a highlighted completion entry
|
||||
, borderColor :: String -- ^ Border color
|
||||
, promptBorderWidth :: !Dimension -- ^ Border width
|
||||
, position :: XPPosition -- ^ Position: 'Top' or 'Bottom'
|
||||
, position :: XPPosition -- ^ Position: 'Top', 'Bottom', or 'CenteredAt'
|
||||
, alwaysHighlight :: !Bool -- ^ Always highlight an item, overriden to True with multiple modes. This implies having *one* column of autocompletions only.
|
||||
, height :: !Dimension -- ^ Window height
|
||||
, maxComplRows :: Maybe Dimension
|
||||
@@ -144,12 +145,12 @@ data XPConfig =
|
||||
-- history entries to remember
|
||||
, promptKeymap :: M.Map (KeyMask,KeySym) (XP ())
|
||||
-- ^ Mapping from key combinations to actions
|
||||
, completionKey :: KeySym -- ^ Key that should trigger completion
|
||||
, completionKey :: (KeyMask, KeySym) -- ^ Key that should trigger completion
|
||||
, changeModeKey :: KeySym -- ^ Key to change mode (when the prompt has multiple modes)
|
||||
, defaultText :: String -- ^ The text by default in the prompt line
|
||||
, autoComplete :: Maybe Int -- ^ Just x: if only one completion remains, auto-select it,
|
||||
, showCompletionOnTab :: Bool -- ^ Only show list of completions when Tab was pressed
|
||||
-- and delay by x microseconds
|
||||
, showCompletionOnTab :: Bool -- ^ Only show list of completions when Tab was pressed
|
||||
, searchPredicate :: String -> String -> Bool
|
||||
-- ^ Given the typed string and a possible
|
||||
-- completion, is the completion valid?
|
||||
@@ -228,6 +229,17 @@ class XPrompt t where
|
||||
|
||||
data XPPosition = Top
|
||||
| Bottom
|
||||
-- | Prompt will be placed in the center horizontally and
|
||||
-- in the certain place of screen vertically. If it's in the upper
|
||||
-- part of the screen, completion window will be placed below(like
|
||||
-- in 'Top') and otherwise above(like in 'Bottom')
|
||||
| CenteredAt { xpCenterY :: Rational
|
||||
-- ^ Rational between 0 and 1, giving
|
||||
-- y coordinate of center of the prompt relative to the screen height.
|
||||
, xpWidth :: Rational
|
||||
-- ^ Rational between 0 and 1, giving
|
||||
-- width of the prompt relatave to the screen width.
|
||||
}
|
||||
deriving (Show,Read)
|
||||
|
||||
amberXPConfig, defaultXPConfig, greenXPConfig :: XPConfig
|
||||
@@ -242,7 +254,7 @@ instance Default XPConfig where
|
||||
, borderColor = "white"
|
||||
, promptBorderWidth = 1
|
||||
, promptKeymap = defaultXPKeymap
|
||||
, completionKey = xK_Tab
|
||||
, completionKey = (0,xK_Tab)
|
||||
, changeModeKey = xK_grave
|
||||
, position = Bottom
|
||||
, height = 18
|
||||
@@ -488,14 +500,15 @@ handle ks@(sym,_) e@(KeyEvent {ev_event_type = t, ev_state = m}) = do
|
||||
complKey <- gets $ completionKey . config
|
||||
chgModeKey <- gets $ changeModeKey . config
|
||||
c <- getCompletions
|
||||
mCleaned <- cleanMask m
|
||||
when (length c > 1) $ modify (\s -> s { showComplWin = True })
|
||||
if complKey == sym
|
||||
if complKey == (mCleaned,sym)
|
||||
then completionHandle c ks e
|
||||
else if (sym == chgModeKey) then
|
||||
do
|
||||
modify setNextMode
|
||||
updateWindows
|
||||
else when (t == keyPress) $ keyPressHandle m ks
|
||||
else when (t == keyPress) $ keyPressHandle mCleaned ks
|
||||
handle _ (ExposeEvent {ev_window = w}) = do
|
||||
st <- get
|
||||
when (win st == w) updateWindows
|
||||
@@ -506,8 +519,9 @@ completionHandle :: [String] -> KeyStroke -> Event -> XP ()
|
||||
completionHandle c ks@(sym,_) (KeyEvent { ev_event_type = t, ev_state = m }) = do
|
||||
complKey <- gets $ completionKey . config
|
||||
alwaysHlight <- gets $ alwaysHighlight . config
|
||||
mCleaned <- cleanMask m
|
||||
case () of
|
||||
() | t == keyPress && sym == complKey ->
|
||||
() | t == keyPress && (mCleaned,sym) == complKey ->
|
||||
do
|
||||
st <- get
|
||||
let updateState l = case alwaysHlight of
|
||||
@@ -523,8 +537,8 @@ completionHandle c ks@(sym,_) (KeyEvent { ev_event_type = t, ev_state = m }) = d
|
||||
[] -> updateWindows >> eventLoop handle
|
||||
[x] -> updateState [x] >> getCompletions >>= updateWins
|
||||
l -> updateState l >> updateWins l
|
||||
| t == keyRelease && sym == complKey -> eventLoop (completionHandle c)
|
||||
| otherwise -> keyPressHandle m ks -- some other key, handle it normally
|
||||
| t == keyRelease && (mCleaned,sym) == complKey -> eventLoop (completionHandle c)
|
||||
| otherwise -> keyPressHandle mCleaned ks -- some other key, handle it normally
|
||||
-- some other event: go back to main loop
|
||||
completionHandle _ k e = handle k e
|
||||
|
||||
@@ -662,12 +676,11 @@ emacsLikeXPKeymap' p = M.fromList $
|
||||
keyPressHandle :: KeyMask -> KeyStroke -> XP ()
|
||||
keyPressHandle m (ks,str) = do
|
||||
km <- gets (promptKeymap . config)
|
||||
kmask <- cleanMask m -- mask is defined in ghc7
|
||||
case M.lookup (kmask,ks) km of
|
||||
case M.lookup (m,ks) km of
|
||||
Just action -> action >> updateWindows
|
||||
Nothing -> case str of
|
||||
"" -> eventLoop handle
|
||||
_ -> when (kmask .&. controlMask == 0) $ do
|
||||
_ -> when (m .&. controlMask == 0) $ do
|
||||
let str' = if isUTF8Encoded str
|
||||
then decodeString str
|
||||
else str
|
||||
@@ -842,8 +855,12 @@ createWin d rw c s = do
|
||||
let (x,y) = case position c of
|
||||
Top -> (0,0)
|
||||
Bottom -> (0, rect_height s - height c)
|
||||
CenteredAt py w -> (floor $ (fi $ rect_width s) * ((1 - w) / 2), floor $ py * fi (rect_height s) - (fi (height c) / 2))
|
||||
width = case position c of
|
||||
CenteredAt _ w -> floor $ fi (rect_width s) * w
|
||||
_ -> rect_width s
|
||||
w <- mkUnmanagedWindow d (defaultScreenOfDisplay d) rw
|
||||
(rect_x s + x) (rect_y s + fi y) (rect_width s) (height c)
|
||||
(rect_x s + x) (rect_y s + fi y) width (height c)
|
||||
mapWindow d w
|
||||
return w
|
||||
|
||||
@@ -852,7 +869,9 @@ drawWin = do
|
||||
st <- get
|
||||
let (c,(d,(w,gc))) = (config &&& dpy &&& win &&& gcon) st
|
||||
scr = defaultScreenOfDisplay d
|
||||
wh = widthOfScreen scr
|
||||
wh = case position c of
|
||||
CenteredAt _ wd -> floor $ wd * fi (widthOfScreen scr)
|
||||
_ -> widthOfScreen scr
|
||||
ht = height c
|
||||
bw = promptBorderWidth c
|
||||
Just bgcolor <- io $ initColor d (bgColor c)
|
||||
@@ -935,8 +954,11 @@ getComplWinDim :: [String] -> XP ComplWindowDim
|
||||
getComplWinDim compl = do
|
||||
st <- get
|
||||
let (c,(scr,fs)) = (config &&& screen &&& fontS) st
|
||||
wh = rect_width scr
|
||||
wh = case position c of
|
||||
CenteredAt _ w -> floor $ fi (rect_width scr) * w
|
||||
_ -> rect_width scr
|
||||
ht = height c
|
||||
bw = promptBorderWidth c
|
||||
|
||||
tws <- mapM (textWidthXMF (dpy st) fs) compl
|
||||
let max_compl_len = fromIntegral ((fi ht `div` 2) + maximum tws)
|
||||
@@ -951,8 +973,11 @@ getComplWinDim compl = do
|
||||
actual_rows = min actual_max_number_of_rows (fi needed_rows)
|
||||
actual_height = actual_rows * ht
|
||||
(x,y) = case position c of
|
||||
Top -> (0,ht)
|
||||
Bottom -> (0, (0 + rem_height - actual_height))
|
||||
Top -> (0,ht - bw)
|
||||
Bottom -> (0, (0 + rem_height - actual_height + bw))
|
||||
CenteredAt py w
|
||||
| py <= 1/2 -> (floor $ fi (rect_width scr) * ((1 - w) / 2), floor (py * fi (rect_height scr) + (fi ht)/2) - bw)
|
||||
| otherwise -> (floor $ fi (rect_width scr) * ((1 - w) / 2), floor (py * fi (rect_height scr) - (fi ht)/2) - actual_height + bw)
|
||||
(asc,desc) <- io $ textExtentsXMF fs $ head compl
|
||||
let yp = fi $ (ht + fi (asc - desc)) `div` 2
|
||||
xp = (asc + desc) `div` 2
|
||||
|
@@ -48,7 +48,7 @@ import XMonad.Prompt ( XPrompt
|
||||
, getNextCompletion
|
||||
, XPConfig
|
||||
, mkXPrompt
|
||||
, mkComplFunFromList)
|
||||
, searchPredicate)
|
||||
import System.Directory (getHomeDirectory)
|
||||
import System.FilePath (takeExtension, dropExtension, combine)
|
||||
import System.Posix.Env (getEnv)
|
||||
@@ -72,6 +72,11 @@ import XMonad.Util.Run (runProcessWithInput)
|
||||
-- - how to setup the password storage, see <http://git.zx2c4.com/password-store/about/>
|
||||
--
|
||||
|
||||
type Predicate = String -> String -> Bool
|
||||
|
||||
getPassCompl :: [String] -> Predicate -> String -> IO [String]
|
||||
getPassCompl compls p s = do return $ filter (p s) compls
|
||||
|
||||
type PromptLabel = String
|
||||
|
||||
data Pass = Pass PromptLabel
|
||||
@@ -101,7 +106,7 @@ passwordStoreFolder =
|
||||
mkPassPrompt :: PromptLabel -> (String -> X ()) -> XPConfig -> X ()
|
||||
mkPassPrompt promptLabel passwordFunction xpconfig = do
|
||||
passwords <- io (passwordStoreFolder >>= getPasswords)
|
||||
mkXPrompt (Pass promptLabel) xpconfig (mkComplFunFromList passwords) passwordFunction
|
||||
mkXPrompt (Pass promptLabel) xpconfig (getPassCompl passwords $ searchPredicate xpconfig) passwordFunction
|
||||
|
||||
-- | A prompt to retrieve a password from a given entry.
|
||||
--
|
||||
|
99
XMonad/Prompt/Unicode.hs
Normal file
99
XMonad/Prompt/Unicode.hs
Normal file
@@ -0,0 +1,99 @@
|
||||
{- |
|
||||
Module : XMonad.Prompt.Unicode
|
||||
Copyright : (c) 2016 Joachim Breitner
|
||||
License : BSD-style (see LICENSE)
|
||||
|
||||
Maintainer : <mail@joachim-breitner.de>
|
||||
Stability : stable
|
||||
|
||||
A prompt for searching unicode characters by name and inserting them into
|
||||
the clipboard.
|
||||
|
||||
Requires the file @\/usr\/share\/unicode\/UnicodeData.txt@ (shipped in the package
|
||||
@unicode-data@ on Debian) and the @xsel@ tool.
|
||||
-}
|
||||
|
||||
module XMonad.Prompt.Unicode (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
unicodePrompt
|
||||
) where
|
||||
|
||||
import qualified Data.ByteString.Char8 as BS
|
||||
import Data.Char
|
||||
import Data.Maybe
|
||||
import Data.Ord
|
||||
import Numeric
|
||||
import System.Environment
|
||||
import System.IO
|
||||
import System.IO.Unsafe
|
||||
import System.IO.Error
|
||||
import Control.Arrow
|
||||
import Data.List
|
||||
import Text.Printf
|
||||
|
||||
import XMonad
|
||||
import XMonad.Util.Run
|
||||
import XMonad.Prompt
|
||||
|
||||
{- $usage
|
||||
|
||||
You can use this module by importing it, along with
|
||||
"XMonad.Prompt", into your ~\/.xmonad\/xmonad.hs file:
|
||||
|
||||
> import XMonad.Prompt
|
||||
> import XMonad.Prompt.Unicode
|
||||
|
||||
and adding an appropriate keybinding, for example:
|
||||
|
||||
> , ((modm .|. controlMask, xK_u), unicodePrompt def)
|
||||
|
||||
-}
|
||||
|
||||
unicodeDataFilename :: String
|
||||
unicodeDataFilename = "/usr/share/unicode/UnicodeData.txt"
|
||||
|
||||
entries :: [(Char, BS.ByteString)]
|
||||
entries = unsafePerformIO $ do
|
||||
datE <- tryIOError $ BS.readFile unicodeDataFilename
|
||||
case datE of
|
||||
Left e -> do
|
||||
hPutStrLn stderr $ "Could not read file \"" ++ unicodeDataFilename ++ "\""
|
||||
hPutStrLn stderr $ show e
|
||||
hPutStrLn stderr $ "Do you have unicode-data installed?"
|
||||
return []
|
||||
Right dat -> return $ sortBy (comparing (BS.length . snd)) $ parseUnicodeData dat
|
||||
{-# NOINLINE entries #-}
|
||||
|
||||
parseUnicodeData :: BS.ByteString -> [(Char, BS.ByteString)]
|
||||
parseUnicodeData = mapMaybe parseLine . BS.lines
|
||||
where
|
||||
parseLine l = do
|
||||
field1 : field2 : _ <- return $ BS.split ';' l
|
||||
[(c,"")] <- return $ readHex (BS.unpack field1)
|
||||
return (chr c, field2)
|
||||
|
||||
searchUnicode :: String -> [(Char, String)]
|
||||
searchUnicode s = map (second BS.unpack) $ filter go entries
|
||||
where w = map BS.pack $ filter (all isAscii) $ filter ((> 1) . length) $ words $ map toUpper s
|
||||
go (c,d) = all (`BS.isInfixOf` d) w
|
||||
|
||||
-- | Prompt the user for a unicode character to be inserted into the paste buffer of the X server.
|
||||
unicodePrompt :: XPConfig -> X ()
|
||||
unicodePrompt config = mkXPrompt Unicode config unicodeCompl paste
|
||||
where
|
||||
unicodeCompl [] = return []
|
||||
unicodeCompl s = do
|
||||
return $ map (\(c,d) -> printf "%s %s" [c] d) $ take 20 $ searchUnicode s
|
||||
|
||||
paste [] = return ()
|
||||
paste (c:_) = do
|
||||
runProcessWithInput "xsel" ["-i"] [c]
|
||||
return ()
|
||||
|
||||
data Unicode = Unicode
|
||||
instance XPrompt Unicode where
|
||||
showXPrompt Unicode = "Unicode: "
|
||||
commandToComplete Unicode s = s
|
||||
nextCompletion Unicode = getNextCompletion
|
||||
|
@@ -18,10 +18,16 @@ module XMonad.Prompt.Window
|
||||
(
|
||||
-- * Usage
|
||||
-- $usage
|
||||
WindowPrompt(..),
|
||||
windowPrompt,
|
||||
allWindows,
|
||||
wsWindows,
|
||||
XWindowMap,
|
||||
|
||||
-- * Deprecated
|
||||
windowPromptGoto,
|
||||
windowPromptBring,
|
||||
windowPromptBringCopy,
|
||||
WindowPrompt,
|
||||
) where
|
||||
|
||||
import qualified Data.Map as M
|
||||
@@ -31,11 +37,13 @@ import XMonad
|
||||
import XMonad.Prompt
|
||||
import XMonad.Actions.CopyWindow
|
||||
import XMonad.Actions.WindowBringer
|
||||
import XMonad.Util.NamedWindows
|
||||
|
||||
-- $usage
|
||||
-- WindowPrompt brings windows to you and you to windows.
|
||||
-- That is to say, it pops up a prompt with window names, in case you forgot
|
||||
-- where you left your XChat.
|
||||
-- WindowPrompt brings windows to you and you to windows. That is to
|
||||
-- say, it pops up a prompt with window names, in case you forgot
|
||||
-- where you left your XChat. It also offers helpers to build the
|
||||
-- subset of windows which is used for the prompt completion.
|
||||
--
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
@@ -44,13 +52,14 @@ import XMonad.Actions.WindowBringer
|
||||
--
|
||||
-- and in the keys definition:
|
||||
--
|
||||
-- > , ((modm .|. shiftMask, xK_g ), windowPromptGoto def)
|
||||
-- > , ((modm .|. shiftMask, xK_b ), windowPromptBring def)
|
||||
-- > , ((modm .|. shiftMask, xK_g ), windowPrompt def Goto wsWindows)
|
||||
-- > , ((modm .|. shiftMask, xK_b ), windowPrompt def Bring allWindows)
|
||||
--
|
||||
-- The autoComplete option is a handy complement here:
|
||||
--
|
||||
-- > , ((modm .|. shiftMask, xK_g ), windowPromptGoto
|
||||
-- > def { autoComplete = Just 500000 } )
|
||||
-- > , ((modm .|. shiftMask, xK_g ), windowPrompt
|
||||
-- > def { autoComplete = Just 500000 }
|
||||
-- > Goto allWindows)
|
||||
--
|
||||
-- The \'500000\' is the number of microseconds to pause before sending you to
|
||||
-- your new window. This is useful so that you don't accidentally send some
|
||||
@@ -59,28 +68,50 @@ import XMonad.Actions.WindowBringer
|
||||
-- For detailed instruction on editing the key binding see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
data WindowPrompt = Goto | Bring | BringCopy
|
||||
-- Describe actions that can applied on the selected window
|
||||
data WindowPrompt = Goto | Bring | BringCopy | BringToMaster
|
||||
instance XPrompt WindowPrompt where
|
||||
showXPrompt Goto = "Go to window: "
|
||||
showXPrompt Bring = "Bring window: "
|
||||
showXPrompt BringToMaster
|
||||
= "Bring window to master: "
|
||||
showXPrompt BringCopy = "Bring a copy: "
|
||||
commandToComplete _ c = c
|
||||
nextCompletion _ = getNextCompletion
|
||||
|
||||
-- | Deprecated. Use windowPrompt instead.
|
||||
windowPromptGoto, windowPromptBring, windowPromptBringCopy :: XPConfig -> X ()
|
||||
windowPromptGoto = doPrompt Goto
|
||||
windowPromptBring = doPrompt Bring
|
||||
windowPromptBringCopy = doPrompt BringCopy
|
||||
windowPromptGoto c = windowPrompt c Goto windowMap
|
||||
windowPromptBring c = windowPrompt c Bring windowMap
|
||||
windowPromptBringCopy c = windowPrompt c BringCopy windowMap
|
||||
|
||||
-- | Pops open a prompt with window titles. Choose one, and you will be
|
||||
-- taken to the corresponding workspace.
|
||||
doPrompt :: WindowPrompt -> XPConfig -> X ()
|
||||
doPrompt t c = do
|
||||
-- | A helper to get the map of all windows.
|
||||
allWindows :: XWindowMap
|
||||
allWindows = windowMap
|
||||
|
||||
-- | A helper to get the map of windows of the current workspace.
|
||||
wsWindows :: XWindowMap
|
||||
wsWindows = withWindowSet (return . W.index) >>= winmap
|
||||
where
|
||||
winmap = fmap M.fromList . mapM pair
|
||||
pair w = do name <- fmap show $ getName w
|
||||
return (name, w)
|
||||
|
||||
-- | A Map where keys are pretty printable window names and values are
|
||||
-- Xmonad windows identifier.
|
||||
type XWindowMap = X (M.Map String Window)
|
||||
|
||||
-- | Pops open a prompt with window titles belonging to
|
||||
-- winmap. Choose one, and an action is applied on the
|
||||
-- selected window, according to WindowPrompt.
|
||||
windowPrompt :: XPConfig -> WindowPrompt -> XWindowMap -> X ()
|
||||
windowPrompt c t winmap = do
|
||||
a <- case t of
|
||||
Goto -> fmap gotoAction windowMap
|
||||
Bring -> fmap bringAction windowMap
|
||||
BringCopy -> fmap bringCopyAction windowMap
|
||||
wm <- windowMap
|
||||
Goto -> fmap gotoAction winmap
|
||||
Bring -> fmap bringAction winmap
|
||||
BringCopy -> fmap bringCopyAction winmap
|
||||
BringToMaster -> fmap bringToMaster winmap
|
||||
wm <- winmap
|
||||
mkXPrompt t c (compList wm) a
|
||||
|
||||
where
|
||||
@@ -88,10 +119,10 @@ doPrompt t c = do
|
||||
gotoAction = winAction W.focusWindow
|
||||
bringAction = winAction bringWindow
|
||||
bringCopyAction = winAction bringCopyWindow
|
||||
bringToMaster = winAction (\w s -> W.shiftMaster . W.focusWindow w $ bringWindow w s)
|
||||
|
||||
compList m s = return . filter (searchPredicate c s) . map fst . M.toList $ m
|
||||
|
||||
|
||||
-- | Brings a copy of the specified window into the current workspace.
|
||||
bringCopyWindow :: Window -> WindowSet -> WindowSet
|
||||
bringCopyWindow w ws = copyWindow w (W.currentTag ws) ws
|
||||
|
@@ -41,7 +41,15 @@ debugWindow w = do
|
||||
case w' of
|
||||
Nothing ->
|
||||
return $ "(deleted window " ++ wx ++ ")"
|
||||
Just (WindowAttributes x y wid ht bw m o) -> do
|
||||
Just (WindowAttributes
|
||||
{ wa_x = x
|
||||
, wa_y = y
|
||||
, wa_width = wid
|
||||
, wa_height = ht
|
||||
, wa_border_width = bw
|
||||
, wa_map_state = m
|
||||
, wa_override_redirect = o
|
||||
}) -> do
|
||||
c' <- withDisplay $ \d ->
|
||||
io (getWindowProperty8 d wM_CLASS w)
|
||||
let c = case c' of
|
||||
|
@@ -80,7 +80,7 @@ import Text.ParserCombinators.ReadP
|
||||
-- Note that, unlike in xmonad 0.4 and previous, you can't use modMask to refer
|
||||
-- to the modMask you configured earlier. You must specify mod1Mask (or
|
||||
-- whichever), or add your own @myModMask = mod1Mask@ line.
|
||||
additionalKeys :: XConfig a -> [((ButtonMask, KeySym), X ())] -> XConfig a
|
||||
additionalKeys :: XConfig a -> [((KeyMask, KeySym), X ())] -> XConfig a
|
||||
additionalKeys conf keyList =
|
||||
conf { keys = \cnf -> M.union (M.fromList keyList) (keys conf cnf) }
|
||||
|
||||
@@ -103,7 +103,7 @@ additionalKeysP conf keyList =
|
||||
--
|
||||
-- > main = xmonad $ def { terminal = "urxvt" }
|
||||
-- > `removeKeys` [(mod1Mask .|. shiftMask, n) | n <- [xK_1 .. xK_9]]
|
||||
removeKeys :: XConfig a -> [(ButtonMask, KeySym)] -> XConfig a
|
||||
removeKeys :: XConfig a -> [(KeyMask, KeySym)] -> XConfig a
|
||||
removeKeys conf keyList =
|
||||
conf { keys = \cnf -> keys conf cnf `M.difference` M.fromList (zip keyList $ repeat ()) }
|
||||
|
||||
|
@@ -21,6 +21,7 @@ module XMonad.Util.ExtensibleState (
|
||||
, remove
|
||||
, get
|
||||
, gets
|
||||
, modified
|
||||
) where
|
||||
|
||||
import Data.Typeable (typeOf,cast)
|
||||
@@ -115,3 +116,10 @@ gets = flip fmap get
|
||||
-- | Remove the value from the extensible state field that has the same type as the supplied argument
|
||||
remove :: ExtensionClass a => a -> X ()
|
||||
remove wit = modifyStateExts $ M.delete (show . typeOf $ wit)
|
||||
|
||||
modified :: (ExtensionClass a, Eq a) => (a -> a) -> X Bool
|
||||
modified f = do
|
||||
v <- get
|
||||
case f v of
|
||||
v' | v' == v -> return False
|
||||
| otherwise -> put v' >> return True
|
||||
|
138
XMonad/Util/Loggers/NamedScratchpad.hs
Normal file
138
XMonad/Util/Loggers/NamedScratchpad.hs
Normal file
@@ -0,0 +1,138 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Util.Loggers.NamedScratchpad
|
||||
-- Copyright : (c) Brandon S Allbery <allbery.b@gmail.com>
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Brandon S Allbery <allbery.b@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- 'XMonad.Util.Loggers' for 'XMonad.Util.NamedScratchpad'
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
module XMonad.Util.Loggers.NamedScratchpad (-- * Usage
|
||||
-- $usage
|
||||
nspTrackStartup
|
||||
,nspTrackHook
|
||||
,nspActiveIcon
|
||||
,nspActive
|
||||
,nspActive') where
|
||||
|
||||
import XMonad.Core
|
||||
import Graphics.X11.Xlib (Window)
|
||||
import Graphics.X11.Xlib.Extras (Event(..))
|
||||
import XMonad.Util.Loggers (Logger)
|
||||
import XMonad.Util.NamedScratchpad (NamedScratchpad(..))
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
import Data.Monoid (All(..))
|
||||
import Data.Char (chr)
|
||||
import Control.Monad (forM, foldM)
|
||||
import qualified Data.IntMap as M
|
||||
import qualified XMonad.StackSet as W (allWindows)
|
||||
|
||||
-- $usage
|
||||
-- This is a set of 'Logger's for 'NamedScratchpad's.
|
||||
-- It provides a 'startupHook' and 'handleEventHook' to keep track of
|
||||
-- 'NamedScratchpad's, and several possible 'Logger's for use in
|
||||
-- 'XMonad.Hooks.DynamicLog' 'ppExtras'.
|
||||
--
|
||||
-- You must add 'nspTrackStartup' to your 'startupHook' to initialize
|
||||
-- 'NamedScratchpad' tracking and to detect any currently running
|
||||
-- 'NamedScratchpad's on restart, and 'nspTrackHook' to your 'handleEventHook'
|
||||
-- to track the coming and going of 'NamedScratchpad's.
|
||||
--
|
||||
-- Why would you want to do this? If you aren't using 'EwmhDesktops', this
|
||||
-- gives you a way to see what 'NamedScratchpad's are running. If you are
|
||||
-- using 'EwmhDesktops' then you can get that from a taskbar... but you may
|
||||
-- have noticed that selecting the window from the taskbar moves you to
|
||||
-- the 'NSP' workspace instead of moving the window to the current workspace.
|
||||
-- (This is difficult to change; "minimizing" by moving it back to 'NSP'
|
||||
-- is even harder.)
|
||||
-- I hide the 'NamedScratchpad's from the taskbar and use this to track
|
||||
-- them instead (see 'XMonad.Util.NoTaskbar').
|
||||
|
||||
-- The extension data for tracking NSP windows
|
||||
data NSPTrack = NSPTrack [Maybe Window] deriving Typeable
|
||||
instance ExtensionClass NSPTrack where
|
||||
initialValue = NSPTrack []
|
||||
|
||||
-- | 'startupHook' to initialize scratchpad activation tracking
|
||||
--
|
||||
-- > , startupHook = ... <+> nspTrackStartup scratchpads
|
||||
--
|
||||
-- If you kickstart the 'logHook', do it /after/ 'nspTrackStartup'!
|
||||
nspTrackStartup :: [NamedScratchpad] -> X ()
|
||||
nspTrackStartup ns = do
|
||||
let ns'i = M.fromList $ zip [0..] $ map (const Nothing) ns
|
||||
ns' <- withWindowSet $ foldM (isSp ns) ns'i . W.allWindows
|
||||
XS.put (NSPTrack (map snd $ M.toAscList ns'))
|
||||
|
||||
isSp :: [NamedScratchpad] -> M.IntMap (Maybe Window) -> Window -> X (M.IntMap (Maybe Window))
|
||||
isSp ns ws w = do
|
||||
n <- runQuery (scratchpadWindow ns) w
|
||||
return $ case n of
|
||||
Nothing -> ws
|
||||
Just n' -> M.insert n' (Just w) ws
|
||||
|
||||
scratchpadWindow :: [NamedScratchpad] -> Query (Maybe Int)
|
||||
scratchpadWindow ns = foldM sp' Nothing (zip [0..] ns)
|
||||
where sp' :: Maybe Int -> (Int,NamedScratchpad) -> Query (Maybe Int)
|
||||
sp' r@(Just _) _ = return r
|
||||
sp' Nothing (n,NS _ _ q _) = q >>= \p -> return $ if p then Just n else Nothing
|
||||
|
||||
-- | 'handleEventHook' to track scratchpad activation/deactivation
|
||||
--
|
||||
-- > , handleEventHook = ... <+> nspTrackHook scratchpads
|
||||
nspTrackHook :: [NamedScratchpad] -> Event -> X All
|
||||
nspTrackHook _ (DestroyWindowEvent {ev_window = w}) = do
|
||||
XS.modify $ \(NSPTrack ws) -> NSPTrack $ map (\sw -> if sw == Just w then Nothing else sw) ws
|
||||
return (All True)
|
||||
nspTrackHook ns (ConfigureRequestEvent {ev_window = w}) = do
|
||||
NSPTrack ws <- XS.get
|
||||
ws' <- forM (zip3 [0..] ws ns) $ \(_,w',NS _ _ q _) -> do
|
||||
p <- runQuery q w
|
||||
return $ if p then Just w else w'
|
||||
XS.put $ NSPTrack ws'
|
||||
return (All True)
|
||||
nspTrackHook _ _ = return (All True)
|
||||
|
||||
-- | 'Logger' for scratchpads' state, using Unicode characters as "icons".
|
||||
--
|
||||
-- > , ppExtras = [..., nspActive' iconChars showActive showInactive, ...]
|
||||
nspActiveIcon :: [Char] -> (String -> String) -> (String -> String) -> Logger
|
||||
nspActiveIcon icns act inact = do
|
||||
NSPTrack ws <- XS.get
|
||||
return $ if null ws
|
||||
then Nothing
|
||||
else let icon' n = if n < length icns then icns !! n else '\NUL'
|
||||
icon n = let c = icon' n
|
||||
in [if c == '\NUL' then chr (0x2460 + n) else c]
|
||||
ckact n w = let icn = icon n
|
||||
in case w of
|
||||
Nothing -> inact icn
|
||||
Just _ -> act icn
|
||||
s = unwords $ zipWith ckact [0..] ws
|
||||
in Just s
|
||||
|
||||
-- | 'Logger' with String-s (and no defaults)
|
||||
--
|
||||
-- > , ppExtras = [..., nspActive iconStrs showActive showInactive, ...]
|
||||
nspActive :: [String] -> (String -> String) -> (String -> String) -> Logger
|
||||
nspActive icns act inact = do
|
||||
NSPTrack ws <- XS.get
|
||||
return $ if null ws
|
||||
then Nothing
|
||||
else let ckact n w = let icn = icns !! n
|
||||
in case w of
|
||||
Nothing -> inact icn
|
||||
Just _ -> act icn
|
||||
s = unwords $ zipWith ckact [0..] ws
|
||||
in Just s
|
||||
|
||||
-- | Variant of the above getting the String-s from the 'NamedScratchpad's
|
||||
nspActive' :: [NamedScratchpad] -> (String -> String) -> (String -> String) -> Logger
|
||||
nspActive' ns = nspActive (map name ns)
|
33
XMonad/Util/NoTaskbar.hs
Normal file
33
XMonad/Util/NoTaskbar.hs
Normal file
@@ -0,0 +1,33 @@
|
||||
module XMonad.Util.NoTaskbar (-- * Usage
|
||||
-- $usage
|
||||
noTaskbar
|
||||
,markNoTaskbar) where
|
||||
|
||||
import XMonad.Core
|
||||
import XMonad.ManageHook
|
||||
import Graphics.X11.Xlib (Window)
|
||||
import Graphics.X11.Xlib.Atom (aTOM)
|
||||
import Graphics.X11.Xlib.Extras (changeProperty32
|
||||
,propModePrepend)
|
||||
import Control.Monad.Reader (ask)
|
||||
|
||||
-- $usage
|
||||
-- Utility functions to hide windows from pagers and taskbars. Mostly useful
|
||||
-- when EWMH doesn't do what you intend (e.g. for 'NamedScratchpad' windows you
|
||||
-- probably don't want to be dumped into the 'NSP' workspace).
|
||||
|
||||
-- | A 'ManageHook' to mark a window to not be shown in pagers or taskbars.
|
||||
noTaskbar :: ManageHook
|
||||
noTaskbar = ask >>= (>> idHook) . liftX . markNoTaskbar
|
||||
|
||||
-- | An 'X' action to mark a window to not be shown in pagers or taskbars.
|
||||
markNoTaskbar :: Window -> X ()
|
||||
markNoTaskbar w = withDisplay $ \d -> do
|
||||
ws <- getAtom "_NET_WM_STATE"
|
||||
ntb <- getAtom "_NET_WM_STATE_SKIP_TASKBAR"
|
||||
npg <- getAtom "_NET_WM_STATE_SKIP_PAGER"
|
||||
io $ changeProperty32 d w ws aTOM propModePrepend [fi ntb,fi npg]
|
||||
|
||||
-- sigh
|
||||
fi :: (Integral i, Num n) => i -> n
|
||||
fi = fromIntegral
|
@@ -94,7 +94,7 @@ runProcessWithInputAndWait cmd args input timeout = io $ do
|
||||
--
|
||||
-- to the top of your file) or use seconds in prefix form:
|
||||
--
|
||||
-- > 5.5 seconds
|
||||
-- > seconds 5.5
|
||||
seconds :: Rational -> Int
|
||||
seconds = fromEnum . (* 1000000)
|
||||
|
||||
|
177
XMonad/Util/TreeZipper.hs
Normal file
177
XMonad/Util/TreeZipper.hs
Normal file
@@ -0,0 +1,177 @@
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.TreeSelect
|
||||
-- Copyright : (c) Tom Smeets <tom.tsmeets@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Tom Smeets <tom.tsmeets@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- <https://wiki.haskell.org/Zipper Zipper> over the "Data.Tree" data structure.
|
||||
-- This module is based on <http://hackage.haskell.org/package/rosezipper rosezipper>.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Util.TreeZipper(
|
||||
-- * Data structure
|
||||
TreeZipper(..)
|
||||
, cursor
|
||||
|
||||
-- * Conversion
|
||||
, fromForest
|
||||
, toForest
|
||||
, getSubForest
|
||||
|
||||
-- * Navigation
|
||||
, rootNode
|
||||
, parent
|
||||
, children
|
||||
, nextChild
|
||||
, previousChild
|
||||
|
||||
-- * Utils
|
||||
, nodeDepth
|
||||
, nodeIndex
|
||||
, followPath
|
||||
, findChild
|
||||
|
||||
, isLeaf
|
||||
, isRoot
|
||||
, isLast
|
||||
, isFirst
|
||||
) where
|
||||
|
||||
import Data.Tree
|
||||
|
||||
-- | A <https://wiki.haskell.org/Zipper Zipper> over the "Data.Tree" data structure.
|
||||
data TreeZipper a = TreeZipper { tz_current :: Tree a -- ^ the currently focused sub-tree under the cursor
|
||||
, tz_before :: Forest a -- ^ all sub-tree's to the /left/ of the cursor that have the same parent
|
||||
, tz_after :: Forest a -- ^ all sub-tree's to the /right/ of the cursor that have the same parent
|
||||
, tz_parents :: [(Forest a, a, Forest a)] -- ^ list zippers for each parent level, the first element is the current parent
|
||||
}
|
||||
-- ^ Very crappy visualization of the 'TreeZipper' data structure
|
||||
--
|
||||
-- @
|
||||
-- (tz_parents)
|
||||
-- ([*], *, [*])
|
||||
-- ([*, *], *, [])
|
||||
-- ([], * [*, *])
|
||||
-- | | |
|
||||
-- +-------+--------+-------+------+ +-*-+ *
|
||||
-- | | | | | | |
|
||||
-- (tz_before) (tz_current) (tz_after) * *
|
||||
-- | | | |
|
||||
-- +-*-+ * * *
|
||||
-- | |
|
||||
-- * *
|
||||
-- @
|
||||
|
||||
-- | Get the highlighted value
|
||||
cursor :: TreeZipper a -> a
|
||||
cursor = rootLabel . tz_current
|
||||
|
||||
-- | Create a 'TreeZipper' from a list of 'Data.Tree.Tree's focused on the first element
|
||||
fromForest :: Forest a -> TreeZipper a
|
||||
fromForest [] = error "XMonad.Util.TreeZipper.fromForest: can't create a TreeZipper from an empty list!"
|
||||
fromForest (x:xs) = TreeZipper { tz_current = x
|
||||
, tz_before = []
|
||||
, tz_after = xs
|
||||
, tz_parents = []
|
||||
}
|
||||
|
||||
-- | Convert the entire zipper back to a 'Data.Tree.Forest'
|
||||
toForest :: TreeZipper a -> Forest a
|
||||
toForest = getSubForest . rootNode
|
||||
|
||||
-- | Create a 'Data.Tree.Forest' from all the children of the current parent
|
||||
getSubForest :: TreeZipper a -> Forest a
|
||||
getSubForest TreeZipper{..} = reverse tz_before ++ tz_current : tz_after
|
||||
|
||||
-- | Go to the upper most node such that
|
||||
-- nothing is before nor above the cursor
|
||||
rootNode :: TreeZipper a -> TreeZipper a
|
||||
rootNode = f
|
||||
where
|
||||
f z = maybe (g z) f $ parent z
|
||||
g z = maybe z g $ previousChild z
|
||||
|
||||
-- | Move to the parent node
|
||||
parent :: TreeZipper a -> Maybe (TreeZipper a)
|
||||
parent t = case tz_parents t of
|
||||
(xs,a,ys) : ps -> Just
|
||||
TreeZipper { tz_current = Node a (reverse (tz_before t) ++ tz_current t : tz_after t)
|
||||
, tz_before = xs
|
||||
, tz_after = ys
|
||||
, tz_parents = ps
|
||||
}
|
||||
[] -> Nothing
|
||||
|
||||
-- | Move the cursor one level down to the first node
|
||||
children :: TreeZipper a -> Maybe (TreeZipper a)
|
||||
children z = case subForest $ tz_current z of
|
||||
(n:xs) -> Just
|
||||
TreeZipper { tz_current = n
|
||||
, tz_before = []
|
||||
, tz_after = xs
|
||||
, tz_parents = (tz_before z, cursor z, tz_after z) : tz_parents z
|
||||
}
|
||||
[] -> Nothing
|
||||
|
||||
-- | Go to the next child node
|
||||
nextChild :: TreeZipper a -> Maybe (TreeZipper a)
|
||||
nextChild z = case tz_after z of
|
||||
(n:xs) -> Just
|
||||
TreeZipper { tz_current = n
|
||||
, tz_before = tz_current z : tz_before z
|
||||
, tz_after = xs
|
||||
, tz_parents = tz_parents z
|
||||
}
|
||||
[] -> Nothing
|
||||
|
||||
-- | Go to the previous child node
|
||||
previousChild :: TreeZipper a -> Maybe (TreeZipper a)
|
||||
previousChild z = case tz_before z of
|
||||
(n:xs) -> Just
|
||||
TreeZipper { tz_current = n
|
||||
, tz_before = xs
|
||||
, tz_after = tz_current z : tz_after z
|
||||
, tz_parents = tz_parents z
|
||||
}
|
||||
[] -> Nothing
|
||||
|
||||
-- | How many nodes are above this one?
|
||||
nodeDepth :: TreeZipper a -> Int
|
||||
nodeDepth = length . tz_parents
|
||||
|
||||
-- | How many nodes are before the cursor? (on the current level)
|
||||
nodeIndex :: TreeZipper a -> Int
|
||||
nodeIndex = length . tz_before
|
||||
|
||||
-- | follow a Path specified by the list of nodes
|
||||
followPath :: Eq b => (a -> b) -> [b] -> TreeZipper a -> Maybe (TreeZipper a)
|
||||
followPath _ [] z = Just z
|
||||
followPath f [x] z = findChild (\y -> f y == x) z
|
||||
followPath f (x:xs) z = findChild (\y -> f y == x) z >>= children >>= followPath f xs
|
||||
|
||||
-- | go to the first node next to the cursor that matches
|
||||
findChild :: (a -> Bool) -> TreeZipper a -> Maybe (TreeZipper a)
|
||||
findChild f z | f (cursor z) = Just z
|
||||
| otherwise = nextChild z >>= findChild f
|
||||
|
||||
-- | Check whenther this is a leaf node
|
||||
isLeaf :: TreeZipper a -> Bool
|
||||
isLeaf = null . subForest . tz_current
|
||||
|
||||
-- | Check whenther this is a leaf node
|
||||
isRoot :: TreeZipper a -> Bool
|
||||
isRoot = null . tz_parents
|
||||
|
||||
-- | Check whenther this the last child
|
||||
isLast :: TreeZipper a -> Bool
|
||||
isLast = null . tz_after
|
||||
|
||||
-- | Check whenther this the first child
|
||||
isFirst :: TreeZipper a -> Bool
|
||||
isFirst = null . tz_before
|
43
XMonad/Util/Ungrab.hs
Normal file
43
XMonad/Util/Ungrab.hs
Normal file
@@ -0,0 +1,43 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Util.Ungrab
|
||||
-- Copyright : (c) 2016 Brandon S Allbery
|
||||
-- License : BSD-style (see xmonad/LICENSE)
|
||||
--
|
||||
-- Maintainer : allbery.b@gmail.com
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Allow releasing xmonad's keyboard grab
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Util.Ungrab
|
||||
( -- * Usage:
|
||||
-- $usage
|
||||
unGrab
|
||||
) where
|
||||
|
||||
import Graphics.X11.Xlib.Extras (currentTime)
|
||||
import Graphics.X11.Xlib.Misc (ungrabKeyboard, ungrabPointer)
|
||||
import XMonad.Core
|
||||
|
||||
-- $usage
|
||||
-- Start a keyboard action with this if it is going to run something
|
||||
-- that needs to do a keyboard, pointer, or server grab. For example,
|
||||
--
|
||||
-- > , ((modm .|. controlMask, xK_p), unGrab >> spawn "scrot")
|
||||
--
|
||||
-- (Other examples are screen lockers and "gksu".)
|
||||
-- This avoids needing to insert a pause/sleep before running the
|
||||
-- command.
|
||||
--
|
||||
-- xmonad retains the keyboard grab during key actions because if they
|
||||
-- use a Submap, they need the keyboard to be grabbed, and if they had
|
||||
-- to assert their own grab then the asynchronous nature of X11 allows
|
||||
-- race conditions between xmonad, other clients, and the X server that
|
||||
-- would cause keys to sometimes be "leaked" to the focused window.
|
||||
|
||||
-- | Release xmonad's keyboard grab, so other grabbers can do their thing.
|
||||
unGrab :: X ()
|
||||
unGrab = withDisplay $ \d -> io (ungrabKeyboard d currentTime >> ungrabPointer d currentTime)
|
@@ -50,23 +50,25 @@ flag use_xft
|
||||
|
||||
flag testing
|
||||
description: Testing mode
|
||||
manual: True
|
||||
default: False
|
||||
|
||||
library
|
||||
build-depends: base >= 3 && < 5,
|
||||
containers,
|
||||
directory,
|
||||
extensible-exceptions,
|
||||
filepath,
|
||||
old-locale,
|
||||
old-time,
|
||||
process,
|
||||
random,
|
||||
mtl >= 1 && < 3,
|
||||
unix,
|
||||
X11>=1.6.1 && < 1.7,
|
||||
xmonad>=0.12 && < 0.13,
|
||||
utf8-string
|
||||
build-depends: base >= 4.5 && < 5,
|
||||
bytestring >= 0.10 && < 0.11,
|
||||
containers >= 0.5 && < 0.6,
|
||||
directory,
|
||||
extensible-exceptions,
|
||||
filepath,
|
||||
old-locale,
|
||||
old-time,
|
||||
process,
|
||||
random,
|
||||
mtl >= 1 && < 3,
|
||||
unix,
|
||||
X11>=1.7 && < 1.8,
|
||||
xmonad>=0.12 && < 0.13,
|
||||
utf8-string
|
||||
|
||||
if flag(use_xft)
|
||||
build-depends: X11-xft >= 0.2
|
||||
@@ -81,9 +83,6 @@ library
|
||||
if impl(ghc >= 6.12.1)
|
||||
ghc-options: -fno-warn-unused-do-bind
|
||||
|
||||
if impl (ghc == 6.10.1) && arch (x86_64)
|
||||
ghc-options: -O0
|
||||
|
||||
exposed-modules: XMonad.Doc
|
||||
XMonad.Doc.Configuring
|
||||
XMonad.Doc.Extending
|
||||
@@ -135,6 +134,7 @@ library
|
||||
XMonad.Actions.SwapWorkspaces
|
||||
XMonad.Actions.TagWindows
|
||||
XMonad.Actions.TopicSpace
|
||||
XMonad.Actions.TreeSelect
|
||||
XMonad.Actions.UpdateFocus
|
||||
XMonad.Actions.UpdatePointer
|
||||
XMonad.Actions.Warp
|
||||
@@ -266,6 +266,7 @@ library
|
||||
XMonad.Layout.SimpleFloat
|
||||
XMonad.Layout.Simplest
|
||||
XMonad.Layout.SimplestFloat
|
||||
XMonad.Layout.SortedLayout
|
||||
XMonad.Layout.Spacing
|
||||
XMonad.Layout.Spiral
|
||||
XMonad.Layout.Square
|
||||
@@ -298,6 +299,7 @@ library
|
||||
XMonad.Prompt.Shell
|
||||
XMonad.Prompt.Ssh
|
||||
XMonad.Prompt.Theme
|
||||
XMonad.Prompt.Unicode
|
||||
XMonad.Prompt.Window
|
||||
XMonad.Prompt.Workspace
|
||||
XMonad.Prompt.XMonad
|
||||
@@ -312,9 +314,11 @@ library
|
||||
XMonad.Util.Image
|
||||
XMonad.Util.Invisible
|
||||
XMonad.Util.Loggers
|
||||
XMonad.Util.Loggers.NamedScratchpad
|
||||
XMonad.Util.NamedActions
|
||||
XMonad.Util.NamedScratchpad
|
||||
XMonad.Util.NamedWindows
|
||||
XMonad.Util.NoTaskbar
|
||||
XMonad.Util.Paste
|
||||
XMonad.Util.PositionStore
|
||||
XMonad.Util.RemoteWindows
|
||||
@@ -327,7 +331,9 @@ library
|
||||
XMonad.Util.StringProp
|
||||
XMonad.Util.Themes
|
||||
XMonad.Util.Timer
|
||||
XMonad.Util.TreeZipper
|
||||
XMonad.Util.Types
|
||||
XMonad.Util.Ungrab
|
||||
XMonad.Util.WindowProperties
|
||||
XMonad.Util.WindowState
|
||||
XMonad.Util.WorkspaceCompare
|
||||
|
Reference in New Issue
Block a user