mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-08-06 23:11:54 -07:00
Compare commits
92 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
60101b9a70 | ||
|
5f1fc602ab | ||
|
303f0c24bc | ||
|
8df80e7805 | ||
|
36dba39c44 | ||
|
8847b3b2f6 | ||
|
1b061245af | ||
|
94662bffc6 | ||
|
23102a6d5c | ||
|
728f9bc270 | ||
|
d9856b955a | ||
|
603fc4ccb7 | ||
|
65e7153874 | ||
|
6a0dc1685c | ||
|
41e8708eb5 | ||
|
18979de5f6 | ||
|
5ffb4e7f52 | ||
|
52a3800b96 | ||
|
dd89eae446 | ||
|
6b68ec5c00 | ||
|
db80842e65 | ||
|
4e4856c722 | ||
|
b5eb418fa4 | ||
|
04b32ae021 | ||
|
9c6d1e696f | ||
|
dad911913c | ||
|
b3f5a09673 | ||
|
4ad6ecf892 | ||
|
fcd296e87a | ||
|
fddd5ea1fe | ||
|
c3cee11ad6 | ||
|
22e6d4b017 | ||
|
fcced8991a | ||
|
bfb52a5025 | ||
|
c53436f3a6 | ||
|
66c1977c29 | ||
|
a18395a13c | ||
|
ca69574cfe | ||
|
faa252e309 | ||
|
71fec4f61a | ||
|
0301d6e21b | ||
|
0cfe3a288e | ||
|
84c63abda6 | ||
|
e12511c658 | ||
|
52890f6007 | ||
|
51bc32ea75 | ||
|
cce7f50372 | ||
|
ba9b108a68 | ||
|
e12c047870 | ||
|
2ce876c330 | ||
|
3faa9b2c38 | ||
|
6720eefce7 | ||
|
cf789504e8 | ||
|
deaaf6b177 | ||
|
60b35ff431 | ||
|
cd404eb644 | ||
|
d9f43c78d6 | ||
|
b9603a0e10 | ||
|
09a5fa111c | ||
|
41a2db5563 | ||
|
1706160b14 | ||
|
207d5962e2 | ||
|
c7e02726bf | ||
|
b0d6e0f942 | ||
|
a774168415 | ||
|
17da8bc8ee | ||
|
c2c0585d7e | ||
|
7a6d24712f | ||
|
13260cae58 | ||
|
07f4ce8029 | ||
|
298cdd6114 | ||
|
36356dd8f5 | ||
|
dd905d2603 | ||
|
dda242a459 | ||
|
204524328d | ||
|
6dcc36c904 | ||
|
705494eb4c | ||
|
6b9fb096d6 | ||
|
913183463a | ||
|
92fe5f34ff | ||
|
8c309f87b8 | ||
|
f6f925c823 | ||
|
203e63b055 | ||
|
e814f748b5 | ||
|
ed32ccd080 | ||
|
2314dd628a | ||
|
95cd118c9a | ||
|
53481b2269 | ||
|
c1c7c30532 | ||
|
617099badd | ||
|
b05f5f12cf | ||
|
270ca2da23 |
224
.travis.yml
224
.travis.yml
@@ -1,99 +1,149 @@
|
||||
# This file has been generated -- see https://github.com/hvr/multi-ghc-travis
|
||||
# This Travis job script has been generated by a script via
|
||||
#
|
||||
# haskell-ci '-o' '.travis.yml' 'xmonad-contrib.cabal' '--apt' 'libxrandr-dev'
|
||||
#
|
||||
# For more information, see https://github.com/haskell-CI/haskell-ci
|
||||
#
|
||||
# version: 0.5.20190916
|
||||
#
|
||||
language: c
|
||||
sudo: false
|
||||
|
||||
dist: xenial
|
||||
git:
|
||||
# whether to recursively clone submodules
|
||||
submodules: false
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cabsnap
|
||||
- $HOME/.cabal/packages
|
||||
|
||||
- $HOME/.cabal/store
|
||||
before_cache:
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
|
||||
|
||||
- rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
|
||||
# remove files that are regenerated by 'cabal update'
|
||||
- rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
|
||||
- rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
|
||||
- rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
|
||||
- rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
|
||||
- rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
|
||||
- rm -rfv $CABALHOME/packages/head.hackage
|
||||
matrix:
|
||||
include:
|
||||
- env: GHCVER=8.6.1 CABALVER=2.4
|
||||
compiler: ": #GHC 8.6.1"
|
||||
addons: { apt: { packages: [cabal-install-2.4, ghc-8.6.1, libxrandr-dev]
|
||||
, sources: [hvr-ghc]
|
||||
} }
|
||||
- env: GHCVER=8.4.3 CABALVER=2.2
|
||||
compiler: ": #GHC 8.4.3"
|
||||
addons: { apt: { packages: [cabal-install-2.2, ghc-8.4.3, libxrandr-dev]
|
||||
, sources: [hvr-ghc]
|
||||
} }
|
||||
- env: GHCVER=8.2.2 CABALVER=2.0
|
||||
compiler: ": #GHC 8.2.2"
|
||||
addons: { apt: { packages: [cabal-install-2.0, ghc-8.2.2, libxrandr-dev]
|
||||
, sources: [hvr-ghc]
|
||||
} }
|
||||
- env: GHCVER=8.0.1 CABALVER=1.24
|
||||
compiler: ": #GHC 8.0.1"
|
||||
addons: { apt: { packages: [cabal-install-1.24, ghc-8.0.1, libxrandr-dev]
|
||||
, sources: [hvr-ghc]
|
||||
} }
|
||||
|
||||
- compiler: ghc-8.8.1
|
||||
addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0","libxrandr-dev"]}}
|
||||
- compiler: ghc-8.6.5
|
||||
addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-3.0","libxrandr-dev"]}}
|
||||
- compiler: ghc-8.4.4
|
||||
addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-3.0","libxrandr-dev"]}}
|
||||
- compiler: ghc-8.2.2
|
||||
addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-3.0","libxrandr-dev"]}}
|
||||
- compiler: ghc-8.0.2
|
||||
addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-3.0","libxrandr-dev"]}}
|
||||
before_install:
|
||||
- unset CC
|
||||
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
||||
|
||||
- HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
|
||||
- WITHCOMPILER="-w $HC"
|
||||
- HADDOCK=$(echo "/opt/$CC/bin/haddock" | sed 's/-/\//')
|
||||
- HCPKG="$HC-pkg"
|
||||
- unset CC
|
||||
- CABAL=/opt/ghc/bin/cabal
|
||||
- CABALHOME=$HOME/.cabal
|
||||
- export PATH="$CABALHOME/bin:$PATH"
|
||||
- TOP=$(pwd)
|
||||
- "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
|
||||
- echo $HCNUMVER
|
||||
- CABAL="$CABAL -vnormal+nowrap+markoutput"
|
||||
- set -o pipefail
|
||||
- |
|
||||
echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk
|
||||
echo 'BEGIN { state = "output"; }' >> .colorful.awk
|
||||
echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk
|
||||
echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk
|
||||
echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk
|
||||
echo ' if (state == "cabal") {' >> .colorful.awk
|
||||
echo ' print blue($0)' >> .colorful.awk
|
||||
echo ' } else {' >> .colorful.awk
|
||||
echo ' print $0' >> .colorful.awk
|
||||
echo ' }' >> .colorful.awk
|
||||
echo '}' >> .colorful.awk
|
||||
- cat .colorful.awk
|
||||
- |
|
||||
color_cabal_output () {
|
||||
awk -f $TOP/.colorful.awk
|
||||
}
|
||||
- echo text | color_cabal_output
|
||||
install:
|
||||
- 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 ];
|
||||
then
|
||||
zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >
|
||||
$HOME/.cabal/packages/hackage.haskell.org/00-index.tar;
|
||||
fi
|
||||
- travis_retry cabal update -v
|
||||
|
||||
# build xmonad from HEAD
|
||||
- git clone https://github.com/xmonad/xmonad.git
|
||||
- cabal install xmonad/
|
||||
|
||||
- sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
|
||||
- cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt
|
||||
- sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt
|
||||
|
||||
# check whether current requested install-plan matches cached package-db snapshot
|
||||
- if diff -u $HOME/.cabsnap/installplan.txt installplan.txt;
|
||||
then
|
||||
echo "cabal build-cache HIT";
|
||||
rm -rfv .ghc;
|
||||
cp -a $HOME/.cabsnap/ghc $HOME/.ghc;
|
||||
cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;
|
||||
else
|
||||
echo "cabal build-cache MISS";
|
||||
rm -rf $HOME/.cabsnap;
|
||||
mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;
|
||||
fi
|
||||
- cabal install --only-dependencies --enable-tests --enable-benchmarks;
|
||||
|
||||
# snapshot package-db on cache miss
|
||||
- if [ ! -d $HOME/.cabsnap ];
|
||||
then
|
||||
echo "snapshotting package-db to build-cache";
|
||||
mkdir $HOME/.cabsnap;
|
||||
cp -a $HOME/.ghc $HOME/.cabsnap/ghc;
|
||||
cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
|
||||
fi
|
||||
|
||||
# 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.
|
||||
- ${CABAL} --version
|
||||
- echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
|
||||
- TEST=--enable-tests
|
||||
- BENCH=--enable-benchmarks
|
||||
- HEADHACKAGE=false
|
||||
- rm -f $CABALHOME/config
|
||||
- |
|
||||
echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
|
||||
echo "remote-build-reporting: anonymous" >> $CABALHOME/config
|
||||
echo "write-ghc-environment-files: always" >> $CABALHOME/config
|
||||
echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
|
||||
echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
|
||||
echo "world-file: $CABALHOME/world" >> $CABALHOME/config
|
||||
echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
|
||||
echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
|
||||
echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
|
||||
echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
|
||||
echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
|
||||
echo "install-dirs user" >> $CABALHOME/config
|
||||
echo " prefix: $CABALHOME" >> $CABALHOME/config
|
||||
echo "repository hackage.haskell.org" >> $CABALHOME/config
|
||||
echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
|
||||
- |
|
||||
echo "program-default-options" >> $CABALHOME/config
|
||||
echo " ghc-options: $GHCJOBS +RTS -M6G -RTS" >> $CABALHOME/config
|
||||
- cat $CABALHOME/config
|
||||
- rm -fv cabal.project cabal.project.local cabal.project.freeze
|
||||
- travis_retry ${CABAL} v2-update -v
|
||||
# Generate cabal.project
|
||||
- rm -rf cabal.project cabal.project.local cabal.project.freeze
|
||||
- touch cabal.project
|
||||
- |
|
||||
echo "packages: ." >> cabal.project
|
||||
- |
|
||||
- "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(xmonad-contrib)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
|
||||
- cat cabal.project || true
|
||||
- cat cabal.project.local || true
|
||||
- if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
|
||||
- ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output
|
||||
- "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
|
||||
- rm cabal.project.freeze
|
||||
- ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} --dep -j2 all | color_cabal_output
|
||||
- ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --dep -j2 all | color_cabal_output
|
||||
script:
|
||||
- if [ -f configure.ac ]; then autoreconf -i; fi
|
||||
- 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 # 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.
|
||||
# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
|
||||
# `cabal install --force-reinstalls dist/*-*.tar.gz`
|
||||
- SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
|
||||
(cd dist && cabal install --force-reinstalls "$SRC_TGZ")
|
||||
- DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
|
||||
# Packaging...
|
||||
- ${CABAL} v2-sdist all | color_cabal_output
|
||||
# Unpacking...
|
||||
- mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
|
||||
- cd ${DISTDIR} || false
|
||||
- find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
|
||||
- find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
|
||||
- PKGDIR_xmonad_contrib="$(find . -maxdepth 1 -type d -regex '.*/xmonad-contrib-[0-9.]*')"
|
||||
# Generate cabal.project
|
||||
- rm -rf cabal.project cabal.project.local cabal.project.freeze
|
||||
- touch cabal.project
|
||||
- |
|
||||
echo "packages: ${PKGDIR_xmonad_contrib}" >> cabal.project
|
||||
- |
|
||||
- "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(xmonad-contrib)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
|
||||
- cat cabal.project || true
|
||||
- cat cabal.project.local || true
|
||||
# Building...
|
||||
# this builds all libraries and executables (without tests/benchmarks)
|
||||
- ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
|
||||
# Building with tests and benchmarks...
|
||||
# build & run tests, build benchmarks
|
||||
- ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output
|
||||
# cabal check...
|
||||
- (cd ${PKGDIR_xmonad_contrib} && ${CABAL} -vnormal check)
|
||||
# haddock...
|
||||
- ${CABAL} v2-haddock $WITHCOMPILER --with-haddock $HADDOCK ${TEST} ${BENCH} all | color_cabal_output
|
||||
# Building without installed constraints for packages in global-db...
|
||||
- rm -f cabal.project.local
|
||||
- ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
|
||||
|
||||
# REGENDATA ["-o",".travis.yml","xmonad-contrib.cabal","--apt","libxrandr-dev"]
|
||||
# EOF
|
||||
|
121
CHANGES.md
121
CHANGES.md
@@ -1,6 +1,117 @@
|
||||
# Change Log / Release Notes
|
||||
|
||||
## unknown
|
||||
## 0.16
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* `XMonad.Layout.Decoration`
|
||||
- Added `Theme` record fields for controlling decoration border width for active/inactive/urgent windows.
|
||||
* `XMonad.Prompt`
|
||||
|
||||
- Prompt ships a vim-like keymap, see `vimLikeXPKeymap` and
|
||||
`vimLikeXPKeymap'`. A reworked event loop supports new vim-like prompt
|
||||
actions.
|
||||
- Prompt supports dynamic colors. Colors are now specified by the `XPColor`
|
||||
type in `XPState` while `XPConfig` colors remain unchanged for backwards
|
||||
compatibility.
|
||||
- Fixes `showCompletionOnTab`.
|
||||
- The behavior of `moveWord` and `moveWord'` has changed; brought in line
|
||||
with the documentation and now internally consistent. The old keymaps
|
||||
retain the original behavior; see the documentation to do the same your
|
||||
XMonad configuration.
|
||||
* `XMonad.Util.Invisble`
|
||||
- Requires `MonadFail` for `Read` instance
|
||||
|
||||
### New Modules
|
||||
|
||||
* `XMonad.Layout.TwoPanePersistent`
|
||||
|
||||
A layout that is like TwoPane but keeps track of the slave window that is
|
||||
currently beside the master. In TwoPane, the default behavior when the master
|
||||
is focused is to display the next window in the stack on the slave pane. This
|
||||
is a problem when a different slave window is selected without changing the stack
|
||||
order.
|
||||
|
||||
* `XMonad.Util.ExclusiveScratchpads`
|
||||
|
||||
Named scratchpads that can be mutually exclusive: This new module extends the
|
||||
idea of named scratchpads such that you can define "families of scratchpads"
|
||||
that are exclusive on the same screen. It also allows to remove this
|
||||
constraint of being mutually exclusive with another scratchpad.
|
||||
|
||||
### Bug Fixes and Minor Changes
|
||||
|
||||
* `XMonad.Layout.Tabbed`
|
||||
|
||||
tabbedLeft and tabbedRight will set their tabs' height and width according to decoHeight/decoWidth
|
||||
|
||||
* `XMonad.Prompt`
|
||||
|
||||
Added `sorter` to `XPConfig` used to sort the possible completions by how
|
||||
well they match the search string (example: `XMonad.Prompt.FuzzyMatch`).
|
||||
|
||||
Fixes a potential bug where an error during prompt execution would
|
||||
leave the window open and keep the keyboard grabbed. See issue
|
||||
[#180](https://github.com/xmonad/xmonad-contrib/issues/180).
|
||||
|
||||
Fixes [issue #217](https://github.com/xmonad/xmonad-contrib/issues/217), where
|
||||
using tab to wrap around the completion rows would fail when maxComplRows is
|
||||
restricting the number of rows of output.
|
||||
|
||||
* `XMonad.Prompt.Pass`
|
||||
|
||||
Added 'passOTPPrompt' to support getting OTP type password. This require
|
||||
pass-otp (https://github.com/tadfisher/pass-otp) has been setup in the running
|
||||
machine.
|
||||
|
||||
Added 'passGenerateAndCopyPrompt', which both generates a new password and
|
||||
copies it to the clipboard. These two actions are commonly desirable to
|
||||
take together, e.g. when establishing a new account.
|
||||
|
||||
Made password prompts traverse symlinks when gathering password names for
|
||||
autocomplete.
|
||||
|
||||
* `XMonad.Actions.DynamicProjects`
|
||||
|
||||
Make the input directory read from the prompt in `DynamicProjects`
|
||||
absolute wrt the current directory.
|
||||
|
||||
Before this, the directory set by the prompt was treated like a relative
|
||||
directory. This means that when you switch from a project with directory
|
||||
`foo` into a project with directory `bar`, xmonad actually tries to `cd`
|
||||
into `foo/bar`, instead of `~/bar` as expected.
|
||||
|
||||
* `XMonad.Actions.DynamicWorkspaceOrder`
|
||||
|
||||
Add a version of `withNthWorkspace` that takes a `[WorkspaceId] ->
|
||||
[WorkspaceId]` transformation to apply over the list of workspace tags
|
||||
resulting from the dynamic order.
|
||||
|
||||
* `XMonad.Actions.GroupNavigation`
|
||||
|
||||
Add a utility function `isOnAnyVisibleWS :: Query Bool` to allow easy
|
||||
cycling between all windows on all visible workspaces.
|
||||
|
||||
|
||||
* `XMonad.Hooks.WallpaperSetter`
|
||||
|
||||
Preserve the aspect ratio of wallpapers that xmonad sets. When previous
|
||||
versions would distort images to fit the screen size, it will now find a
|
||||
best fit by cropping instead.
|
||||
|
||||
* `XMonad.Util.Themes`
|
||||
|
||||
Add adwaitaTheme and adwaitaDarkTheme to match their respective
|
||||
GTK themes.
|
||||
|
||||
* 'XMonad.Layout.BinarySpacePartition'
|
||||
|
||||
Add a new `SplitShiftDirectional` message that allows moving windows by
|
||||
splitting its neighbours.
|
||||
|
||||
* `XMonad.Prompt.FuzzyMatch`
|
||||
|
||||
Make fuzzy sort show shorter strings first.
|
||||
|
||||
## 0.15
|
||||
|
||||
@@ -164,6 +275,12 @@
|
||||
Provides a simple transformer for use with `XMonad.Layout.MultiToggle` to
|
||||
dynamically toggle `XMonad.Layout.TabBarDecoration`.
|
||||
|
||||
* `XMonad.Hooks.RefocusLast`
|
||||
|
||||
Provides hooks and actions that keep track of recently focused windows on a
|
||||
per workspace basis and automatically refocus the last window on loss of the
|
||||
current (if appropriate as determined by user specified criteria).
|
||||
|
||||
* `XMonad.Layout.StateFull`
|
||||
|
||||
Provides `StateFull`: a stateful form of `Full` that does not misbehave when
|
||||
@@ -349,6 +466,8 @@
|
||||
|
||||
- New function `passTypePrompt` which uses `xdotool` to type in a password
|
||||
from the store, bypassing the clipboard.
|
||||
- New function `passEditPrompt` for editing a password from the
|
||||
store.
|
||||
- Now handles password labels with spaces and special characters inside
|
||||
them.
|
||||
|
||||
|
@@ -50,7 +50,7 @@ import Data.Map.Strict (Map)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import Data.Maybe (fromMaybe, isNothing)
|
||||
import Data.Monoid ((<>))
|
||||
import System.Directory (setCurrentDirectory, getHomeDirectory)
|
||||
import System.Directory (setCurrentDirectory, getHomeDirectory, makeAbsolute)
|
||||
import XMonad
|
||||
import XMonad.Actions.DynamicWorkspaces
|
||||
import XMonad.Prompt
|
||||
@@ -182,7 +182,8 @@ instance XPrompt ProjectPrompt where
|
||||
modifyProject (\p -> p { projectName = name })
|
||||
|
||||
modeAction (ProjectPrompt DirMode _) buf auto = do
|
||||
let dir = if null auto then buf else auto
|
||||
let dir' = if null auto then buf else auto
|
||||
dir <- io $ makeAbsolute dir'
|
||||
modifyProject (\p -> p { projectDirectory = dir })
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@@ -30,6 +30,7 @@ module XMonad.Actions.DynamicWorkspaceOrder
|
||||
, moveToGreedy
|
||||
, shiftTo
|
||||
|
||||
, withNthWorkspace'
|
||||
, withNthWorkspace
|
||||
|
||||
) where
|
||||
@@ -183,13 +184,19 @@ moveToGreedy dir t = doTo dir t getSortByOrder (windows . W.greedyView)
|
||||
shiftTo :: Direction1D -> WSType -> X ()
|
||||
shiftTo dir t = doTo dir t getSortByOrder (windows . W.shift)
|
||||
|
||||
-- | Do something with the nth workspace in the dynamic order after
|
||||
-- transforming it. The callback is given the workspace's tag as well
|
||||
-- as the 'WindowSet' of the workspace itself.
|
||||
withNthWorkspace' :: ([WorkspaceId] -> [WorkspaceId]) -> (String -> WindowSet -> WindowSet) -> Int -> X ()
|
||||
withNthWorkspace' tr job wnum = do
|
||||
sort <- getSortByOrder
|
||||
ws <- gets (tr . map W.tag . sort . W.workspaces . windowset)
|
||||
case drop wnum ws of
|
||||
(w:_) -> windows $ job w
|
||||
[] -> return ()
|
||||
|
||||
-- | Do something with the nth workspace in the dynamic order. The
|
||||
-- callback is given the workspace's tag as well as the 'WindowSet'
|
||||
-- of the workspace itself.
|
||||
withNthWorkspace :: (String -> WindowSet -> WindowSet) -> Int -> X ()
|
||||
withNthWorkspace job wnum = do
|
||||
sort <- getSortByOrder
|
||||
ws <- gets (map W.tag . sort . W.workspaces . windowset)
|
||||
case drop wnum ws of
|
||||
(w:_) -> windows $ job w
|
||||
[] -> return ()
|
||||
withNthWorkspace = withNthWorkspace' id
|
||||
|
@@ -16,7 +16,7 @@
|
||||
-- query.
|
||||
--
|
||||
-- Also provides a method for jumping back to the most recently used
|
||||
-- window in any given group.
|
||||
-- window in any given group, and predefined groups.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
@@ -27,9 +27,14 @@ module XMonad.Actions.GroupNavigation ( -- * Usage
|
||||
, nextMatchOrDo
|
||||
, nextMatchWithThis
|
||||
, historyHook
|
||||
|
||||
-- * Utilities
|
||||
-- $utilities
|
||||
, isOnAnyVisibleWS
|
||||
) where
|
||||
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.State
|
||||
import Data.Foldable as Fold
|
||||
import Data.Map as Map
|
||||
import Data.Sequence as Seq
|
||||
@@ -142,7 +147,7 @@ orderedWorkspaceList ss wsids = rotateTo isCurWS wspcs'
|
||||
where
|
||||
wspcs = SS.workspaces ss
|
||||
wspcsMap = Fold.foldl' (\m ws -> Map.insert (SS.tag ws) ws m) Map.empty wspcs
|
||||
wspcs' = fmap (\wsid -> wspcsMap ! wsid) wsids
|
||||
wspcs' = fmap (wspcsMap !) wsids
|
||||
isCurWS ws = SS.tag ws == SS.tag (SS.workspace $ SS.current ss)
|
||||
|
||||
--- History navigation, requires a layout modifier -------------------
|
||||
@@ -167,7 +172,7 @@ updateHistory :: HistoryDB -> X HistoryDB
|
||||
updateHistory (HistoryDB oldcur oldhist) = withWindowSet $ \ss -> do
|
||||
let newcur = SS.peek ss
|
||||
wins = Set.fromList $ SS.allWindows ss
|
||||
newhist = flt (flip Set.member wins) (ins oldcur oldhist)
|
||||
newhist = flt (`Set.member` wins) (ins oldcur oldhist)
|
||||
return $ HistoryDB newcur (del newcur newhist)
|
||||
where
|
||||
ins x xs = maybe xs (<| xs) x
|
||||
@@ -216,3 +221,22 @@ findM cond xs = findM' cond (viewl xs)
|
||||
if isMatch
|
||||
then return (Just x')
|
||||
else findM qry xs'
|
||||
|
||||
|
||||
-- $utilities
|
||||
-- #utilities#
|
||||
-- Below are handy queries for use with 'nextMatch', 'nextMatchOrDo',
|
||||
-- and 'nextMatchWithThis'.
|
||||
|
||||
-- | A query that matches all windows on visible workspaces. This is
|
||||
-- useful for configurations with multiple screens, and matches even
|
||||
-- invisible windows.
|
||||
isOnAnyVisibleWS :: Query Bool
|
||||
isOnAnyVisibleWS = do
|
||||
w <- ask
|
||||
ws <- liftX $ gets windowset
|
||||
let allVisible = concat $ maybe [] SS.integrate . SS.stack . SS.workspace <$> SS.current ws:SS.visible ws
|
||||
visibleWs = w `elem` allVisible
|
||||
unfocused = maybe True (w /=) $ SS.peek ws
|
||||
return $ visibleWs && unfocused
|
||||
|
||||
|
@@ -467,7 +467,7 @@ Here is a list of the modules found in @XMonad.Hooks@:
|
||||
* "XMonad.Hooks.DebugStack":
|
||||
Dump the state of the StackSet. A logHook and handleEventHook are also provided.
|
||||
|
||||
* "Xmonad.Hooks.DynamicBars":
|
||||
* "XMonad.Hooks.DynamicBars":
|
||||
Manage per-screen status bars.
|
||||
|
||||
* "XMonad.Hooks.DynamicHooks":
|
||||
|
@@ -29,12 +29,15 @@ import Control.Applicative((<$>))
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.Monoid
|
||||
import qualified Data.Map.Strict as M
|
||||
import System.IO.Unsafe
|
||||
|
||||
import XMonad
|
||||
import Control.Monad
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Hooks.SetWMName
|
||||
import qualified XMonad.Util.ExtensibleState as E
|
||||
import XMonad.Util.XUtils (fi)
|
||||
import XMonad.Util.WorkspaceCompare
|
||||
import XMonad.Util.WindowProperties (getProp32)
|
||||
@@ -70,6 +73,58 @@ ewmhDesktopsStartup = setSupported
|
||||
-- of the current state of workspaces and windows.
|
||||
ewmhDesktopsLogHook :: X ()
|
||||
ewmhDesktopsLogHook = ewmhDesktopsLogHookCustom id
|
||||
|
||||
-- |
|
||||
-- Cached desktop names (e.g. @_NET_NUMBER_OF_DESKTOPS@ and
|
||||
-- @_NET_DESKTOP_NAMES@).
|
||||
newtype DesktopNames = DesktopNames [String]
|
||||
deriving (Eq)
|
||||
|
||||
instance ExtensionClass DesktopNames where
|
||||
initialValue = DesktopNames []
|
||||
|
||||
-- |
|
||||
-- Cached client list (e.g. @_NET_CLIENT_LIST@).
|
||||
newtype ClientList = ClientList [Window]
|
||||
deriving (Eq)
|
||||
|
||||
instance ExtensionClass ClientList where
|
||||
initialValue = ClientList []
|
||||
|
||||
-- |
|
||||
-- Cached current desktop (e.g. @_NET_CURRENT_DESKTOP@).
|
||||
newtype CurrentDesktop = CurrentDesktop Int
|
||||
deriving (Eq)
|
||||
|
||||
instance ExtensionClass CurrentDesktop where
|
||||
initialValue = CurrentDesktop 0
|
||||
|
||||
-- |
|
||||
-- Cached window-desktop assignments (e.g. @_NET_CLIENT_LIST_STACKING@).
|
||||
newtype WindowDesktops = WindowDesktops (M.Map Window Int)
|
||||
deriving (Eq)
|
||||
|
||||
instance ExtensionClass WindowDesktops where
|
||||
initialValue = WindowDesktops M.empty
|
||||
|
||||
-- |
|
||||
-- The value of @_NET_ACTIVE_WINDOW@, cached to avoid unnecessary property
|
||||
-- updates.
|
||||
newtype ActiveWindow = ActiveWindow Window
|
||||
deriving (Eq)
|
||||
|
||||
instance ExtensionClass ActiveWindow where
|
||||
initialValue = ActiveWindow none
|
||||
|
||||
-- | Compare the given value against the value in the extensible state. Run the
|
||||
-- action if it has changed.
|
||||
whenChanged :: (Eq a, ExtensionClass a) => a -> X () -> X ()
|
||||
whenChanged v action = do
|
||||
v0 <- E.get
|
||||
unless (v == v0) $ do
|
||||
action
|
||||
E.put v
|
||||
|
||||
-- |
|
||||
-- Generalized version of ewmhDesktopsLogHook that allows an arbitrary
|
||||
-- user-specified function to transform the workspace list (post-sorting)
|
||||
@@ -78,28 +133,32 @@ ewmhDesktopsLogHookCustom f = withWindowSet $ \s -> do
|
||||
sort' <- getSortByIndex
|
||||
let ws = f $ sort' $ W.workspaces s
|
||||
|
||||
-- Number of Workspaces
|
||||
setNumberOfDesktops (length ws)
|
||||
-- Set number of workspaces and names thereof
|
||||
let desktopNames = map W.tag ws
|
||||
whenChanged (DesktopNames desktopNames) $ do
|
||||
setNumberOfDesktops (length desktopNames)
|
||||
setDesktopNames desktopNames
|
||||
|
||||
-- Names thereof
|
||||
setDesktopNames (map W.tag ws)
|
||||
|
||||
-- all windows, with focused windows last
|
||||
let wins = nub . concatMap (maybe [] (\(W.Stack x l r)-> reverse l ++ r ++ [x]) . W.stack) $ ws
|
||||
setClientList wins
|
||||
-- Set client list; all windows, with focused windows last
|
||||
let clientList = nub . concatMap (maybe [] (\(W.Stack x l r) -> reverse l ++ r ++ [x]) . W.stack) $ ws
|
||||
whenChanged (ClientList clientList) $ setClientList clientList
|
||||
|
||||
-- Remap the current workspace to handle any renames that f might be doing.
|
||||
let maybeCurrent' = W.tag <$> listToMaybe (f [W.workspace $ W.current s])
|
||||
maybeCurrent = join (flip elemIndex (map W.tag ws) <$> maybeCurrent')
|
||||
current = join (flip elemIndex (map W.tag ws) <$> maybeCurrent')
|
||||
whenChanged (CurrentDesktop $ fromMaybe 0 current) $ do
|
||||
mapM_ setCurrentDesktop current
|
||||
|
||||
fromMaybe (return ()) $ setCurrentDesktop <$> maybeCurrent
|
||||
|
||||
sequence_ $ zipWith setWorkspaceWindowDesktops [0..] ws
|
||||
|
||||
setActiveWindow
|
||||
|
||||
return ()
|
||||
-- Set window-desktop mapping
|
||||
let windowDesktops =
|
||||
let f wsId workspace = M.fromList [ (winId, wsId) | winId <- W.integrate' $ W.stack workspace ]
|
||||
in M.unions $ zipWith f [0..] ws
|
||||
whenChanged (WindowDesktops windowDesktops) $ do
|
||||
mapM_ (uncurry setWindowDesktop) (M.toList windowDesktops)
|
||||
|
||||
-- Set active window
|
||||
let activeWindow' = fromMaybe none (W.peek s)
|
||||
whenChanged (ActiveWindow activeWindow') $ setActiveWindow activeWindow'
|
||||
|
||||
-- |
|
||||
-- Intercepts messages from pagers and similar applications and reacts on them.
|
||||
@@ -221,10 +280,6 @@ setClientList wins = withDisplay $ \dpy -> do
|
||||
a' <- getAtom "_NET_CLIENT_LIST_STACKING"
|
||||
io $ changeProperty32 dpy r a' c propModeReplace (fmap fromIntegral wins)
|
||||
|
||||
setWorkspaceWindowDesktops :: (Integral a) => a -> WindowSpace -> X()
|
||||
setWorkspaceWindowDesktops index workspace =
|
||||
mapM_ (flip setWindowDesktop index) (W.integrate' $ W.stack workspace)
|
||||
|
||||
setWindowDesktop :: (Integral a) => Window -> a -> X ()
|
||||
setWindowDesktop win i = withDisplay $ \dpy -> do
|
||||
a <- getAtom "_NET_WM_DESKTOP"
|
||||
@@ -250,9 +305,8 @@ setSupported = withDisplay $ \dpy -> do
|
||||
|
||||
setWMName "xmonad"
|
||||
|
||||
setActiveWindow :: X ()
|
||||
setActiveWindow = withWindowSet $ \s -> withDisplay $ \dpy -> do
|
||||
let w = fromMaybe none (W.peek s)
|
||||
setActiveWindow :: Window -> X ()
|
||||
setActiveWindow w = withDisplay $ \dpy -> do
|
||||
r <- asks theRoot
|
||||
a <- getAtom "_NET_ACTIVE_WINDOW"
|
||||
c <- getAtom "WINDOW"
|
||||
|
295
XMonad/Hooks/RefocusLast.hs
Normal file
295
XMonad/Hooks/RefocusLast.hs
Normal file
@@ -0,0 +1,295 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, MultiWayIf #-}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.RefocusLast
|
||||
-- Description : Hooks and actions to refocus the previous window.
|
||||
-- Copyright : (c) 2018 L. S. Leary
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : L. S. Leary
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides hooks and actions that keep track of recently focused windows on a
|
||||
-- per workspace basis and automatically refocus the last window on loss of the
|
||||
-- current (if appropriate as determined by user specified criteria).
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- --< Imports & Exports >-- {{{
|
||||
|
||||
module XMonad.Hooks.RefocusLast (
|
||||
-- * Usage
|
||||
-- $Usage
|
||||
-- * Hooks
|
||||
refocusLastLogHook,
|
||||
refocusLastLayoutHook,
|
||||
refocusLastWhen,
|
||||
-- ** Predicates
|
||||
-- $Predicates
|
||||
refocusingIsActive,
|
||||
isFloat,
|
||||
-- * Actions
|
||||
toggleRefocusing,
|
||||
toggleFocus,
|
||||
swapWithLast,
|
||||
refocusWhen,
|
||||
shiftRLWhen,
|
||||
updateRecentsOn,
|
||||
-- * Types
|
||||
-- $Types
|
||||
RecentWins(..),
|
||||
RecentsMap(..),
|
||||
RefocusLastLayoutHook(..),
|
||||
RefocusLastToggle(..)
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
import XMonad.Util.Stack (findS, mapZ_)
|
||||
import XMonad.Layout.LayoutModifier
|
||||
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Monoid (All(..))
|
||||
import Data.Foldable (asum)
|
||||
import qualified Data.Map.Strict as M
|
||||
import Control.Monad (when)
|
||||
|
||||
-- }}}
|
||||
|
||||
-- --< Usage >-- {{{
|
||||
|
||||
-- $Usage
|
||||
-- To use this module, you must either include 'refocusLastLogHook' in your log
|
||||
-- hook __or__ 'refocusLastLayoutHook' in your layout hook; don't use both.
|
||||
-- This suffices to make use of both 'toggleFocus' and 'shiftRLWhen' but will
|
||||
-- not refocus automatically upon loss of the current window; for that you must
|
||||
-- include in your event hook @'refocusLastWhen' pred@ for some valid @pred@.
|
||||
--
|
||||
-- The event hooks that trigger refocusing only fire when a window is lost
|
||||
-- completely, not when it's simply e.g. moved to another workspace. Hence you
|
||||
-- will need to use @'shiftRLWhen' pred@ or @'refocusWhen' pred@ as appropriate
|
||||
-- if you want the same behaviour in such cases.
|
||||
--
|
||||
-- Example configuration:
|
||||
--
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Hooks.RefocusLast
|
||||
-- > import qualified Data.Map.Strict as M
|
||||
-- >
|
||||
-- > main :: IO ()
|
||||
-- > main = xmonad def
|
||||
-- > { handleEventHook = refocusLastWhen myPred <+> handleEventHook def
|
||||
-- > , logHook = refocusLastLogHook <+> logHook def
|
||||
-- > -- , layoutHook = refocusLastLayoutHook $ layoutHook def
|
||||
-- > , keys = refocusLastKeys <+> keys def
|
||||
-- > } where
|
||||
-- > myPred = refocusingIsActive <||> isFloat
|
||||
-- > refocusLastKeys cnf
|
||||
-- > = M.fromList
|
||||
-- > $ ((modMask cnf , xK_a), toggleFocus)
|
||||
-- > : ((modMask cnf .|. shiftMask, xK_a), swapWithLast)
|
||||
-- > : ((modMask cnf , xK_b), toggleRefocusing)
|
||||
-- > : [ ( (modMask cnf .|. shiftMask, n)
|
||||
-- > , windows =<< shiftRLWhen myPred wksp
|
||||
-- > )
|
||||
-- > | (n, wksp) <- zip [xK_1..xK_9] (workspaces cnf)
|
||||
-- > ]
|
||||
--
|
||||
|
||||
-- }}}
|
||||
|
||||
-- --< Types >-- {{{
|
||||
|
||||
-- $Types
|
||||
-- The types and constructors used in this module are exported principally to
|
||||
-- aid extensibility; typical users will have nothing to gain from this section.
|
||||
|
||||
-- | Data type holding onto the previous and current @Window@.
|
||||
data RecentWins = Recent { previous :: !Window, current :: !Window }
|
||||
deriving (Show, Read, Eq)
|
||||
|
||||
-- | Newtype wrapper for a @Map@ holding the @RecentWins@ for each workspace.
|
||||
-- Is an instance of @ExtensionClass@ with persistence of state.
|
||||
newtype RecentsMap = RecentsMap (M.Map WorkspaceId RecentWins)
|
||||
deriving (Show, Read, Eq, Typeable)
|
||||
|
||||
instance ExtensionClass RecentsMap where
|
||||
initialValue = RecentsMap M.empty
|
||||
extensionType = PersistentExtension
|
||||
|
||||
-- | A 'LayoutModifier' that updates the 'RecentWins' for a workspace upon
|
||||
-- relayout.
|
||||
data RefocusLastLayoutHook a = RefocusLastLayoutHook
|
||||
deriving (Show, Read)
|
||||
|
||||
instance LayoutModifier RefocusLastLayoutHook a where
|
||||
modifyLayout _ w@(W.Workspace tg _ _) r = updateRecentsOn tg >> runLayout w r
|
||||
|
||||
-- | A newtype on @Bool@ to act as a universal toggle for refocusing.
|
||||
newtype RefocusLastToggle = RefocusLastToggle { refocusing :: Bool }
|
||||
deriving (Show, Read, Eq, Typeable)
|
||||
|
||||
instance ExtensionClass RefocusLastToggle where
|
||||
initialValue = RefocusLastToggle { refocusing = True }
|
||||
extensionType = PersistentExtension
|
||||
|
||||
-- }}}
|
||||
|
||||
-- --< Public Hooks >-- {{{
|
||||
|
||||
-- | A log hook recording the current workspace's most recently focused windows
|
||||
-- into extensible state.
|
||||
refocusLastLogHook :: X ()
|
||||
refocusLastLogHook = withWindowSet (updateRecentsOn . W.currentTag)
|
||||
|
||||
-- | Records a workspace's recently focused windows into extensible state upon
|
||||
-- relayout. Potentially a less wasteful alternative to @refocusLastLogHook@,
|
||||
-- as it does not run on @WM_NAME@ @propertyNotify@ events.
|
||||
refocusLastLayoutHook :: l a -> ModifiedLayout RefocusLastLayoutHook l a
|
||||
refocusLastLayoutHook = ModifiedLayout RefocusLastLayoutHook
|
||||
|
||||
-- | Given a predicate on the event window determining whether or not to act,
|
||||
-- construct an event hook that runs iff the core xmonad event handler will
|
||||
-- unmanage the window, and which shifts focus to the last focused window on
|
||||
-- the appropriate workspace if desired.
|
||||
refocusLastWhen :: Query Bool -> Event -> X All
|
||||
refocusLastWhen p event = All True <$ case event of
|
||||
UnmapEvent { ev_send_event = synth, ev_window = w } -> do
|
||||
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
|
||||
when (synth || e == 0) (refocusLast w)
|
||||
DestroyWindowEvent { ev_window = w } -> refocusLast w
|
||||
_ -> return ()
|
||||
where
|
||||
refocusLast w = whenX (runQuery p w) . withWindowSet $ \ws ->
|
||||
whenJust (W.findTag w ws) $ \tag ->
|
||||
withRecentsIn tag () $ \lw cw ->
|
||||
when (w == cw) . modify $ \xs ->
|
||||
xs { windowset = tryFocusIn tag [lw] ws }
|
||||
|
||||
-- }}}
|
||||
|
||||
-- --< Predicates >-- {{{
|
||||
|
||||
-- $Predicates
|
||||
-- Impure @Query Bool@ predicates on event windows for use as arguments to
|
||||
-- 'refocusLastWhen', 'shiftRLWhen' and 'refocusWhen'. Can be combined with
|
||||
-- '<||>' or '<&&>'. Use like e.g.
|
||||
--
|
||||
-- > , handleEventHook = refocusLastWhen refocusingIsActive
|
||||
--
|
||||
-- or in a keybinding:
|
||||
--
|
||||
-- > windows =<< shiftRLWhen (refocusingIsActive <&&> isFloat) "3"
|
||||
--
|
||||
-- It's also valid to use a property lookup like @className =? "someProgram"@ as
|
||||
-- a predicate, and it should function as expected with e.g. @shiftRLWhen@.
|
||||
-- In the event hook on the other hand, the window in question has already been
|
||||
-- unmapped or destroyed, so external lookups to X properties don't work:
|
||||
-- only the information fossilised in xmonad's state is available.
|
||||
|
||||
-- | Holds iff refocusing is toggled active.
|
||||
refocusingIsActive :: Query Bool
|
||||
refocusingIsActive = (liftX . XS.gets) refocusing
|
||||
|
||||
-- | Holds iff the event window is a float.
|
||||
isFloat :: Query Bool
|
||||
isFloat = ask >>= \w -> (liftX . gets) (M.member w . W.floating . windowset)
|
||||
|
||||
-- }}}
|
||||
|
||||
-- --< Public Actions >-- {{{
|
||||
|
||||
-- | Toggle automatic refocusing at runtime. Has no effect unless the
|
||||
-- @refocusingIsActive@ predicate has been used.
|
||||
toggleRefocusing :: X ()
|
||||
toggleRefocusing = XS.modify (RefocusLastToggle . not . refocusing)
|
||||
|
||||
-- | Refocuses the previously focused window; acts as a toggle.
|
||||
-- Is not affected by @toggleRefocusing@.
|
||||
toggleFocus :: X ()
|
||||
toggleFocus = withRecents $ \lw cw ->
|
||||
when (cw /= lw) . windows $ tryFocus [lw]
|
||||
|
||||
-- | Swaps the current and previous windows of the current workspace.
|
||||
-- Is not affected by @toggleRefocusing@.
|
||||
swapWithLast :: X ()
|
||||
swapWithLast = withRecents $ \lw cw ->
|
||||
when (cw /= lw) . windows . modify''. mapZ_ $ \w ->
|
||||
if | (w == lw) -> cw
|
||||
| (w == cw) -> lw
|
||||
| otherwise -> w
|
||||
where modify'' f = W.modify (f Nothing) (f . Just)
|
||||
|
||||
-- | Given a target workspace and a predicate on its current window, produce a
|
||||
-- 'windows' suitable function that will refocus that workspace appropriately.
|
||||
-- Allows you to hook refocusing into any action you can run through
|
||||
-- @windows@. See the implementation of @shiftRLWhen@ for a straight-forward
|
||||
-- usage example.
|
||||
refocusWhen :: Query Bool -> WorkspaceId -> X (WindowSet -> WindowSet)
|
||||
refocusWhen p tag = withRecentsIn tag id $ \lw cw -> do
|
||||
b <- runQuery p cw
|
||||
return (if b then tryFocusIn tag [cw, lw] else id)
|
||||
|
||||
-- | Sends the focused window to the specified workspace, refocusing the last
|
||||
-- focused window if the predicate holds on the current window. Note that the
|
||||
-- native version of this, @windows . W.shift@, has a nice property that this
|
||||
-- does not: shifting a window to another workspace then shifting it back
|
||||
-- preserves its place in the stack. Can be used in a keybinding like e.g.
|
||||
--
|
||||
-- > windows =<< shiftRLWhen refocusingIsActive "3"
|
||||
--
|
||||
-- or
|
||||
--
|
||||
-- > (windows <=< shiftRLWhen refocusingIsActive) "3"
|
||||
--
|
||||
-- where '<=<' is imported from "Control.Monad".
|
||||
shiftRLWhen :: Query Bool -> WorkspaceId -> X (WindowSet -> WindowSet)
|
||||
shiftRLWhen p to = withWindowSet $ \ws -> do
|
||||
refocus <- refocusWhen p (W.currentTag ws)
|
||||
let shift = maybe id (W.shiftWin to) (W.peek ws)
|
||||
return (refocus . shift)
|
||||
|
||||
-- | Perform an update to the 'RecentWins' for the specified workspace.
|
||||
-- The RefocusLast log and layout hooks are both implemented trivially in
|
||||
-- terms of this function. Only exported to aid extensibility.
|
||||
updateRecentsOn :: WorkspaceId -> X ()
|
||||
updateRecentsOn tag = withWindowSet $ \ws ->
|
||||
whenJust (W.peek $ W.view tag ws) $ \fw -> do
|
||||
m <- getRecentsMap
|
||||
let insertRecent l c = XS.put . RecentsMap $ M.insert tag (Recent l c) m
|
||||
case M.lookup tag m of
|
||||
Just (Recent _ cw) -> when (cw /= fw) (insertRecent cw fw)
|
||||
Nothing -> insertRecent fw fw
|
||||
|
||||
-- }}}
|
||||
|
||||
-- --< Private Utilities >-- {{{
|
||||
|
||||
-- | Focuses the first window in the list it can find on the current workspace.
|
||||
tryFocus :: [Window] -> WindowSet -> WindowSet
|
||||
tryFocus wins = W.modify' $ \s ->
|
||||
fromMaybe s . asum $ (\w -> findS (== w) s) <$> wins
|
||||
|
||||
-- | Operate the above on a specified workspace.
|
||||
tryFocusIn :: WorkspaceId -> [Window] -> WindowSet -> WindowSet
|
||||
tryFocusIn tag wins ws =
|
||||
W.view (W.currentTag ws) . tryFocus wins . W.view tag $ ws
|
||||
|
||||
-- | Get the RecentsMap out of extensible state and remove its newtype wrapper.
|
||||
getRecentsMap :: X (M.Map WorkspaceId RecentWins)
|
||||
getRecentsMap = XS.get >>= \(RecentsMap m) -> return m
|
||||
|
||||
-- | Perform an X action dependent on successful lookup of the RecentWins for
|
||||
-- the specified workspace, or return a default value.
|
||||
withRecentsIn :: WorkspaceId -> a -> (Window -> Window -> X a) -> X a
|
||||
withRecentsIn tag dflt f = M.lookup tag <$> getRecentsMap
|
||||
>>= maybe (return dflt) (\(Recent lw cw) -> f lw cw)
|
||||
|
||||
-- | The above specialised to the current workspace and unit.
|
||||
withRecents :: (Window -> Window -> X ()) -> X ()
|
||||
withRecents f = withWindowSet $ \ws -> withRecentsIn (W.currentTag ws) () f
|
||||
|
||||
-- }}}
|
||||
|
@@ -221,7 +221,7 @@ layerCommand (rect, path) = do
|
||||
res <- getPicRes path
|
||||
return $ case needsRotation rect <$> res of
|
||||
Nothing -> ""
|
||||
Just rotate ->
|
||||
Just rotate -> let size = show (rect_width rect) ++ "x" ++ show (rect_height rect) in
|
||||
" \\( '"++path++"' "++(if rotate then "-rotate 90 " else "")
|
||||
++ " -scale "++(show$rect_width rect)++"x"++(show$rect_height rect)++"! \\)"
|
||||
++ " -scale "++size++"^ -gravity center -extent "++size++" +gravity \\)"
|
||||
++ " -geometry +"++(show$rect_x rect)++"+"++(show$rect_y rect)++" -composite "
|
||||
|
@@ -6,6 +6,7 @@
|
||||
-- Module : XMonad.Layout.BinarySpacePartition
|
||||
-- Copyright : (c) 2013 Ben Weitzman <benweitzman@gmail.com>
|
||||
-- 2015 Anton Pirogov <anton.pirogov@gmail.com>
|
||||
-- 2019 Mateusz Karbowy <obszczymucha@gmail.com
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Ben Weitzman <benweitzman@gmail.com>
|
||||
@@ -29,6 +30,7 @@ module XMonad.Layout.BinarySpacePartition (
|
||||
, FocusParent(..)
|
||||
, SelectMoveNode(..)
|
||||
, Direction2D(..)
|
||||
, SplitShiftDirectional(..)
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
@@ -66,19 +68,21 @@ import Data.Ratio ((%))
|
||||
--
|
||||
-- If you don't want to use the mouse, add the following key bindings to resize the splits with the keyboard:
|
||||
--
|
||||
-- > , ((modm .|. altMask, xK_l ), sendMessage $ ExpandTowards R)
|
||||
-- > , ((modm .|. altMask, xK_h ), sendMessage $ ExpandTowards L)
|
||||
-- > , ((modm .|. altMask, xK_j ), sendMessage $ ExpandTowards D)
|
||||
-- > , ((modm .|. altMask, xK_k ), sendMessage $ ExpandTowards U)
|
||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_l ), sendMessage $ ShrinkFrom R)
|
||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_h ), sendMessage $ ShrinkFrom L)
|
||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_j ), sendMessage $ ShrinkFrom D)
|
||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_k ), sendMessage $ ShrinkFrom U)
|
||||
-- > , ((modm, xK_r ), sendMessage Rotate)
|
||||
-- > , ((modm, xK_s ), sendMessage Swap)
|
||||
-- > , ((modm, xK_n ), sendMessage FocusParent)
|
||||
-- > , ((modm .|. ctrlMask, xK_n ), sendMessage SelectNode)
|
||||
-- > , ((modm .|. shiftMask, xK_n ), sendMessage MoveNode)
|
||||
-- > , ((modm .|. altMask, xK_l ), sendMessage $ ExpandTowards R)
|
||||
-- > , ((modm .|. altMask, xK_h ), sendMessage $ ExpandTowards L)
|
||||
-- > , ((modm .|. altMask, xK_j ), sendMessage $ ExpandTowards D)
|
||||
-- > , ((modm .|. altMask, xK_k ), sendMessage $ ExpandTowards U)
|
||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_l ), sendMessage $ ShrinkFrom R)
|
||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_h ), sendMessage $ ShrinkFrom L)
|
||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_j ), sendMessage $ ShrinkFrom D)
|
||||
-- > , ((modm .|. altMask .|. ctrlMask , xK_k ), sendMessage $ ShrinkFrom U)
|
||||
-- > , ((modm, xK_r ), sendMessage Rotate)
|
||||
-- > , ((modm, xK_s ), sendMessage Swap)
|
||||
-- > , ((modm, xK_n ), sendMessage FocusParent)
|
||||
-- > , ((modm .|. ctrlMask, xK_n ), sendMessage SelectNode)
|
||||
-- > , ((modm .|. shiftMask, xK_n ), sendMessage MoveNode)
|
||||
-- > , ((modm .|. shiftMask .|. ctrlMask , xK_j ), sendMessage $ SplitShift Prev)
|
||||
-- > , ((modm .|. shiftMask .|. ctrlMask , xK_k ), sendMessage $ SplitShift Next)
|
||||
--
|
||||
-- Here's an alternative key mapping, this time using additionalKeysP,
|
||||
-- arrow keys, and slightly different behavior when resizing windows
|
||||
@@ -92,7 +96,9 @@ import Data.Ratio ((%))
|
||||
-- > , ("M-M1-C-<Up>", sendMessage $ ShrinkFrom D)
|
||||
-- > , ("M-M1-C-<Down>", sendMessage $ ExpandTowards D)
|
||||
-- > , ("M-s", sendMessage $ BSP.Swap)
|
||||
-- > , ("M-M1-s", sendMessage $ Rotate) ]
|
||||
-- > , ("M-M1-s", sendMessage $ Rotate)
|
||||
-- > , ("M-S-C-j", sendMessage $ SplitShift Prev)
|
||||
-- > , ("M-S-C-k", sendMessage $ SplitShift Next)
|
||||
--
|
||||
-- If you have many windows open and the layout begins to look too hard to manage, you can 'Balance'
|
||||
-- the layout, so that the current splittings are discarded and windows are tiled freshly in a way that
|
||||
@@ -133,6 +139,10 @@ instance Message SelectMoveNode
|
||||
|
||||
data Axis = Horizontal | Vertical deriving (Show, Read, Eq)
|
||||
|
||||
-- |Message for shifting window by splitting its neighbour
|
||||
data SplitShiftDirectional = SplitShift Direction1D deriving Typeable
|
||||
instance Message SplitShiftDirectional
|
||||
|
||||
oppositeDirection :: Direction2D -> Direction2D
|
||||
oppositeDirection U = D
|
||||
oppositeDirection D = U
|
||||
@@ -273,6 +283,42 @@ swapCurrent :: Zipper a -> Maybe (Zipper a)
|
||||
swapCurrent l@(_, []) = Just l
|
||||
swapCurrent (n, c:cs) = Just (n, swapCrumb c:cs)
|
||||
|
||||
insertLeftLeaf :: Tree Split -> Zipper Split -> Maybe (Zipper Split)
|
||||
insertLeftLeaf (Leaf n) ((Node x l r), crumb:cs) = Just (Node (Split (oppositeAxis . axis . parentVal $ crumb) 0.5) (Leaf n) (Node x l r), crumb:cs)
|
||||
insertLeftLeaf (Leaf n) (Leaf x, crumb:cs) = Just (Node (Split (oppositeAxis . axis . parentVal $ crumb) 0.5) (Leaf n) (Leaf x), crumb:cs)
|
||||
insertLeftLeaf (Node _ _ _) z = Just z
|
||||
|
||||
insertRightLeaf :: Tree Split -> Zipper Split -> Maybe (Zipper Split)
|
||||
insertRightLeaf (Leaf n) ((Node x l r), crumb:cs) = Just (Node (Split (oppositeAxis . axis . parentVal $ crumb) 0.5) (Node x l r) (Leaf n), crumb:cs)
|
||||
insertRightLeaf (Leaf n) (Leaf x, crumb:cs) = Just (Node (Split (oppositeAxis . axis . parentVal $ crumb) 0.5) (Leaf x) (Leaf n), crumb:cs)
|
||||
insertRightLeaf (Node _ _ _) z = Just z
|
||||
|
||||
findRightLeaf :: Zipper Split -> Maybe (Zipper Split)
|
||||
findRightLeaf n@(Node _ _ _, _) = goRight n >>= findRightLeaf
|
||||
findRightLeaf l@(Leaf _, _) = Just l
|
||||
|
||||
findLeftLeaf :: Zipper Split -> Maybe (Zipper Split)
|
||||
findLeftLeaf n@(Node _ _ _, _) = goLeft n
|
||||
findLeftLeaf l@(Leaf _, _) = Just l
|
||||
|
||||
findTheClosestLeftmostLeaf :: Zipper Split -> Maybe (Zipper Split)
|
||||
findTheClosestLeftmostLeaf s@(_, (RightCrumb _ _):_) = goUp s >>= goLeft >>= findRightLeaf
|
||||
findTheClosestLeftmostLeaf s@(_, (LeftCrumb _ _):_) = goUp s >>= findTheClosestLeftmostLeaf
|
||||
|
||||
findTheClosestRightmostLeaf :: Zipper Split -> Maybe (Zipper Split)
|
||||
findTheClosestRightmostLeaf s@(_, (RightCrumb _ _):_) = goUp s >>= findTheClosestRightmostLeaf
|
||||
findTheClosestRightmostLeaf s@(_, (LeftCrumb _ _):_) = goUp s >>= goRight >>= findLeftLeaf
|
||||
|
||||
splitShiftLeftCurrent :: Zipper Split -> Maybe (Zipper Split)
|
||||
splitShiftLeftCurrent l@(_, []) = Just l
|
||||
splitShiftLeftCurrent l@(_, (RightCrumb _ _):_) = Just l -- Do nothing. We can swap windows instead.
|
||||
splitShiftLeftCurrent l@(n, c:cs) = removeCurrent l >>= findTheClosestLeftmostLeaf >>= insertRightLeaf n
|
||||
|
||||
splitShiftRightCurrent :: Zipper Split -> Maybe (Zipper Split)
|
||||
splitShiftRightCurrent l@(_, []) = Just l
|
||||
splitShiftRightCurrent l@(_, (LeftCrumb _ _):_) = Just l -- Do nothing. We can swap windows instead.
|
||||
splitShiftRightCurrent l@(n, c:cs) = removeCurrent l >>= findTheClosestRightmostLeaf >>= insertLeftLeaf n
|
||||
|
||||
isAllTheWay :: Direction2D -> Zipper Split -> Bool
|
||||
isAllTheWay _ (_, []) = True
|
||||
isAllTheWay R (_, LeftCrumb s _:_)
|
||||
@@ -513,6 +559,12 @@ swapNth (BinarySpacePartition _ _ _ Nothing) = emptyBSP
|
||||
swapNth b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
|
||||
swapNth b = doToNth swapCurrent b
|
||||
|
||||
splitShiftNth :: Direction1D -> BinarySpacePartition a -> BinarySpacePartition a
|
||||
splitShiftNth _ (BinarySpacePartition _ _ _ Nothing) = emptyBSP
|
||||
splitShiftNth _ b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
|
||||
splitShiftNth Prev b = doToNth splitShiftLeftCurrent b
|
||||
splitShiftNth Next b = doToNth splitShiftRightCurrent b
|
||||
|
||||
growNthTowards :: Direction2D -> BinarySpacePartition a -> BinarySpacePartition a
|
||||
growNthTowards _ (BinarySpacePartition _ _ _ Nothing) = emptyBSP
|
||||
growNthTowards _ b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
|
||||
@@ -687,6 +739,7 @@ instance LayoutClass BinarySpacePartition Window where
|
||||
, fmap rotateTr (fromMessage m)
|
||||
, fmap (balanceTr r) (fromMessage m)
|
||||
, fmap move (fromMessage m)
|
||||
, fmap splitShift (fromMessage m)
|
||||
]
|
||||
resize (ExpandTowards dir) = growNthTowards dir b
|
||||
resize (ShrinkFrom dir) = shrinkNthFrom dir b
|
||||
@@ -699,6 +752,7 @@ instance LayoutClass BinarySpacePartition Window where
|
||||
balanceTr r Balance = resetFoc $ rebalanceNth b r
|
||||
move MoveNode = resetFoc $ moveNode b
|
||||
move SelectNode = b --should not happen here, is done above, as we need X monad
|
||||
splitShift (SplitShift dir) = resetFoc $ splitShiftNth dir b
|
||||
|
||||
b = numerateLeaves b_orig
|
||||
resetFoc bsp = bsp{getFocusedNode=(getFocusedNode bsp){refLeaf=(-1)}
|
||||
|
@@ -68,18 +68,21 @@ decoration s t ds = ModifiedLayout (Decoration (I Nothing) s t ds)
|
||||
--
|
||||
-- For a collection of 'Theme's see "XMonad.Util.Themes"
|
||||
data Theme =
|
||||
Theme { activeColor :: String -- ^ Color of the active window
|
||||
, inactiveColor :: String -- ^ Color of the inactive window
|
||||
, urgentColor :: String -- ^ Color of the urgent window
|
||||
, activeBorderColor :: String -- ^ Color of the border of the active window
|
||||
, inactiveBorderColor :: String -- ^ Color of the border of the inactive window
|
||||
, urgentBorderColor :: String -- ^ Color of the border of the urgent window
|
||||
, activeTextColor :: String -- ^ Color of the text of the active window
|
||||
, inactiveTextColor :: String -- ^ Color of the text of the inactive window
|
||||
, urgentTextColor :: String -- ^ Color of the text of the urgent window
|
||||
, fontName :: String -- ^ Font name
|
||||
, decoWidth :: Dimension -- ^ Maximum width of the decorations (if supported by the 'DecorationStyle')
|
||||
, decoHeight :: Dimension -- ^ Height of the decorations
|
||||
Theme { activeColor :: String -- ^ Color of the active window
|
||||
, inactiveColor :: String -- ^ Color of the inactive window
|
||||
, urgentColor :: String -- ^ Color of the urgent window
|
||||
, activeBorderColor :: String -- ^ Color of the border of the active window
|
||||
, inactiveBorderColor :: String -- ^ Color of the border of the inactive window
|
||||
, urgentBorderColor :: String -- ^ Color of the border of the urgent window
|
||||
, activeBorderWidth :: Dimension -- ^ Width of the border of the active window
|
||||
, inactiveBorderWidth :: Dimension -- ^ Width of the border of the inactive window
|
||||
, urgentBorderWidth :: Dimension -- ^ Width of the border of the urgent window
|
||||
, activeTextColor :: String -- ^ Color of the text of the active window
|
||||
, inactiveTextColor :: String -- ^ Color of the text of the inactive window
|
||||
, urgentTextColor :: String -- ^ Color of the text of the urgent window
|
||||
, fontName :: String -- ^ Font name
|
||||
, decoWidth :: Dimension -- ^ Maximum width of the decorations (if supported by the 'DecorationStyle')
|
||||
, decoHeight :: Dimension -- ^ Height of the decorations
|
||||
, windowTitleAddons :: [(String, Align)] -- ^ Extra text to appear in a window's title bar.
|
||||
-- Refer to for a use "XMonad.Layout.ImageButtonDecoration"
|
||||
, windowTitleIcons :: [([[Bool]], Placement)] -- ^ Extra icons to appear in a window's title bar.
|
||||
@@ -94,6 +97,9 @@ instance Default Theme where
|
||||
, activeBorderColor = "#FFFFFF"
|
||||
, inactiveBorderColor = "#BBBBBB"
|
||||
, urgentBorderColor = "##00FF00"
|
||||
, activeBorderWidth = 1
|
||||
, inactiveBorderWidth = 1
|
||||
, urgentBorderWidth = 1
|
||||
, activeTextColor = "#FFFFFF"
|
||||
, inactiveTextColor = "#BFBFBF"
|
||||
, urgentTextColor = "#FF0000"
|
||||
@@ -395,9 +401,10 @@ updateDeco sh t fs ((w,_),(Just dw,Just (Rectangle _ _ wh ht))) = do
|
||||
| win `elem` ur -> uc
|
||||
| otherwise -> ic) . W.peek)
|
||||
`fmap` gets windowset
|
||||
(bc,borderc,tc) <- focusColor w (inactiveColor t, inactiveBorderColor t, inactiveTextColor t)
|
||||
(activeColor t, activeBorderColor t, activeTextColor t)
|
||||
(urgentColor t, urgentBorderColor t, urgentTextColor t)
|
||||
(bc,borderc,borderw,tc) <-
|
||||
focusColor w (inactiveColor t, inactiveBorderColor t, inactiveBorderWidth t, inactiveTextColor t)
|
||||
(activeColor t, activeBorderColor t, activeBorderWidth t, activeTextColor t)
|
||||
(urgentColor t, urgentBorderColor t, urgentBorderWidth t, urgentTextColor t)
|
||||
let s = shrinkIt sh
|
||||
name <- shrinkWhile s (\n -> do size <- io $ textWidthXMF dpy fs n
|
||||
return $ size > fromIntegral wh - fromIntegral (ht `div` 2)) (show nw)
|
||||
@@ -405,7 +412,7 @@ updateDeco sh t fs ((w,_),(Just dw,Just (Rectangle _ _ wh ht))) = do
|
||||
strs = name : map fst (windowTitleAddons t)
|
||||
i_als = map snd (windowTitleIcons t)
|
||||
icons = map fst (windowTitleIcons t)
|
||||
paintTextAndIcons dw fs wh ht 1 bc borderc tc bc als strs i_als icons
|
||||
paintTextAndIcons dw fs wh ht borderw bc borderc tc bc als strs i_als icons
|
||||
updateDeco _ _ _ (_,(Just w,Nothing)) = hideWindow w
|
||||
updateDeco _ _ _ _ = return ()
|
||||
|
||||
|
@@ -214,7 +214,7 @@ infixr 5 |||
|
||||
-- layouts, and use those.
|
||||
--
|
||||
-- For the ability to select a layout from a prompt, see
|
||||
-- "Xmonad.Prompt.Layout".
|
||||
-- "XMonad.Prompt.Layout".
|
||||
|
||||
-- | A reimplementation of the combinator of the same name from the
|
||||
-- xmonad core, providing layout choice, and the ability to support
|
||||
|
@@ -226,8 +226,12 @@ instance Eq a => DecorationStyle TabbedDecoration a where
|
||||
ny = n y hh
|
||||
upperTab = Rectangle nx y wid (fi ht)
|
||||
lowerTab = Rectangle nx (y + fi (hh - ht)) wid (fi ht)
|
||||
leftTab = Rectangle x ny (fi wt) hid
|
||||
rightTab = Rectangle (x + fi (wh - wt)) ny (fi wt) hid
|
||||
fixHeightLoc i = y + fi (((fi ht) * fi i))
|
||||
fixHeightTab k = Rectangle k
|
||||
(maybe y (fixHeightLoc)
|
||||
$ w `elemIndex` ws) (fi wt) (fi ht)
|
||||
rightTab = fixHeightTab (x + fi (wh - wt))
|
||||
leftTab = fixHeightTab x
|
||||
numWindows = length ws
|
||||
shrink (Tabbed loc _ ) (Rectangle _ _ dw dh) (Rectangle x y w h)
|
||||
= case loc of
|
||||
|
98
XMonad/Layout/TwoPanePersistent.hs
Normal file
98
XMonad/Layout/TwoPanePersistent.hs
Normal file
@@ -0,0 +1,98 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.TwoPanePersistent
|
||||
-- Copyright : (c) Chayanon Wichitrnithed
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Chayanon Wichitrnithed <namowi@gatech.edu>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This layout is the same as "XMonad.Layout.TwoPane" except that it keeps track of the slave window
|
||||
-- that is alongside the master pane. In other words, it prevents the slave pane
|
||||
-- from changing after the focus goes back to the master pane.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
module XMonad.Layout.TwoPanePersistent
|
||||
(
|
||||
-- * Usage
|
||||
-- $usage
|
||||
TwoPanePersistent(..)
|
||||
) where
|
||||
|
||||
import XMonad.StackSet (focus, up, down, Stack, Stack(..))
|
||||
import XMonad hiding (focus)
|
||||
|
||||
-- $usage
|
||||
-- Import the module in @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.TwoPanePersistent
|
||||
--
|
||||
-- Then add the layout to the @layoutHook@:
|
||||
--
|
||||
-- > myLayout = TwoPanePersistent Nothing (3/100) (1/2) ||| Full ||| etc..
|
||||
-- > main = xmonad def { layoutHook = myLayout }
|
||||
|
||||
|
||||
data TwoPanePersistent a = TwoPanePersistent
|
||||
{ slaveWin :: (Maybe a) -- ^ slave window; if 'Nothing' or not in the current workspace,
|
||||
-- the window below the master will go into the slave pane
|
||||
, dFrac :: Rational -- ^ shrink/expand size
|
||||
, mFrac :: Rational -- ^ initial master size
|
||||
} deriving (Show, Read)
|
||||
|
||||
|
||||
instance (Show a, Eq a) => LayoutClass TwoPanePersistent a where
|
||||
doLayout l r s =
|
||||
case reverse (up s) of
|
||||
-- master is focused
|
||||
[] -> return $ focusedMaster l s r
|
||||
|
||||
-- slave is focused
|
||||
(master:_) -> return $ focusedSlave l s r master
|
||||
|
||||
|
||||
pureMessage (TwoPanePersistent w delta split) x =
|
||||
case fromMessage x of
|
||||
Just Shrink -> Just (TwoPanePersistent w delta (split - delta))
|
||||
Just Expand -> Just (TwoPanePersistent w delta (split + delta))
|
||||
_ -> Nothing
|
||||
|
||||
description _ = "TwoPanePersistent"
|
||||
|
||||
|
||||
----------------------------------------------------------------------------------------
|
||||
|
||||
focusedMaster :: (Eq a) => TwoPanePersistent a -> Stack a -> Rectangle
|
||||
-> ( [(a, Rectangle)], Maybe (TwoPanePersistent a) )
|
||||
focusedMaster (TwoPanePersistent w delta split) s r =
|
||||
let (left, right) = splitHorizontallyBy split r in
|
||||
case down s of
|
||||
-- there exist windows below the master
|
||||
(next:_) -> let nextSlave = ( [(focus s, left), (next, right)]
|
||||
, Just $ TwoPanePersistent (Just next) delta split )
|
||||
in case w of
|
||||
-- if retains state, preserve the layout
|
||||
Just win -> if win `elem` (down s) && (focus s /= win)
|
||||
then ( [(focus s, left), (win, right)]
|
||||
, Just $ TwoPanePersistent w delta split )
|
||||
else nextSlave
|
||||
-- if no previous state, default to the next slave window
|
||||
Nothing -> nextSlave
|
||||
|
||||
|
||||
-- the master is the only window
|
||||
[] -> ( [(focus s, r)]
|
||||
, Just $ TwoPanePersistent Nothing delta split )
|
||||
|
||||
|
||||
|
||||
focusedSlave :: TwoPanePersistent a -> Stack a -> Rectangle -> a
|
||||
-> ( [(a, Rectangle)], Maybe (TwoPanePersistent a) )
|
||||
focusedSlave (TwoPanePersistent _ delta split) s r m =
|
||||
( [(m, left), (focus s, right)]
|
||||
, Just $ TwoPanePersistent (Just $ focus s) delta split )
|
||||
where (left, right) = splitHorizontallyBy split r
|
1100
XMonad/Prompt.hs
1100
XMonad/Prompt.hs
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@ import Data.Function
|
||||
import Data.List
|
||||
|
||||
-- $usage
|
||||
--
|
||||
--
|
||||
-- This module offers two aspects of fuzzy matching of completions offered by
|
||||
-- XMonad.Prompt.
|
||||
--
|
||||
@@ -48,22 +48,22 @@ import Data.List
|
||||
-- 11. "FastSPR" is ranked before "FasterSPR" because its match starts at
|
||||
-- position 5 while the match in "FasterSPR" starts at position 7.
|
||||
--
|
||||
-- To use these functions in an XPrompt, for example, for windowPromptGoto:
|
||||
-- To use these functions in an XPrompt, for example, for windowPrompt:
|
||||
--
|
||||
-- > import XMonad.Prompt
|
||||
-- > import XMonad.Prompt.Window ( windowPromptGoto )
|
||||
-- > import XMonad.Prompt.Window ( windowPrompt )
|
||||
-- > import XMonad.Prompt.FuzzyMatch
|
||||
-- >
|
||||
-- > myXPConfig = def { searchPredicate = fuzzyMatch
|
||||
-- , sorter = fuzzySort
|
||||
-- }
|
||||
--
|
||||
-- > , sorter = fuzzySort
|
||||
-- > }
|
||||
--
|
||||
-- then add this to your keys definition:
|
||||
--
|
||||
-- > , ((modm .|. shiftMask, xK_g), windowPromptGoto myXPConfig)
|
||||
-- > , ((modm .|. shiftMask, xK_g), windowPrompt myXPConfig Goto allWindows)
|
||||
--
|
||||
-- For detailed instructions on editing the key bindings, see
|
||||
-- "Xmonad.Doc.Extending#Editing_key_bindings".
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
-- | Returns True if the first argument is a subsequence of the second argument,
|
||||
-- that is, it can be obtained from the second sequence by deleting elements.
|
||||
@@ -77,7 +77,7 @@ fuzzyMatch xxs@(x:xs) (y:ys) | toLower x == toLower y = fuzzyMatch xs ys
|
||||
-- measured first by the length of the substring containing the match and second
|
||||
-- by the positions of the matching characters in the string.
|
||||
fuzzySort :: String -> [String] -> [String]
|
||||
fuzzySort q = map snd . sortBy (compare `on` fst) . map (rankMatch q)
|
||||
fuzzySort q = map snd . sort . map (rankMatch q)
|
||||
|
||||
rankMatch :: String -> String -> ((Int, Int), String)
|
||||
rankMatch q s = (minimum $ rankMatches q s, s)
|
||||
@@ -95,7 +95,7 @@ findOccurrences :: String -> Char -> [Int]
|
||||
findOccurrences s c = map snd $ filter ((toLower c ==) . toLower . fst) $ zip s [0..]
|
||||
|
||||
extendMatches :: [(Int, Int)] -> [Int] -> [(Int, Int)]
|
||||
extendMatches spans xs = map last $ groupBy ((==) `on` snd) $ extendMatches' spans xs
|
||||
extendMatches spans = map last . groupBy ((==) `on` snd) . extendMatches' spans
|
||||
|
||||
extendMatches' :: [(Int, Int)] -> [Int] -> [(Int, Int)]
|
||||
extendMatches' [] _ = []
|
||||
|
@@ -8,23 +8,33 @@
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module provides 4 <XMonad.Prompt> to ease password manipulation (generate, read, remove):
|
||||
-- This module provides 5 <XMonad.Prompt>s to ease password
|
||||
-- manipulation (generate, read, edit, remove):
|
||||
--
|
||||
-- - two to lookup passwords in the password-store; one of which copies to the
|
||||
-- clipboard, and the other uses @xdotool@ to type the password directly.
|
||||
-- - two to lookup passwords in the password-store; one of which
|
||||
-- copies to the clipboard, and the other uses @xdotool@ to type the
|
||||
-- password directly.
|
||||
--
|
||||
-- - one to generate a password for a given password label that the user inputs.
|
||||
-- - one to generate a password for a given password label that the
|
||||
-- user inputs.
|
||||
--
|
||||
-- - one to delete a stored password for a given password label that the user inputs.
|
||||
-- - one to edit a password for a given password label that the user
|
||||
-- inputs.
|
||||
--
|
||||
-- All those prompts benefit from the completion system provided by the module <XMonad.Prompt>.
|
||||
-- - one to delete a stored password for a given password label that
|
||||
-- the user inputs.
|
||||
--
|
||||
-- The password store is setup through an environment variable PASSWORD_STORE_DIR,
|
||||
-- or @$HOME\/.password-store@ if it is unset.
|
||||
-- All those prompts benefit from the completion system provided by
|
||||
-- the module <XMonad.Prompt>.
|
||||
--
|
||||
-- The password store is setup through an environment variable
|
||||
-- PASSWORD_STORE_DIR, or @$HOME\/.password-store@ if it is unset.
|
||||
-- The editor is determined from the environment variable EDITOR.
|
||||
--
|
||||
-- Source:
|
||||
--
|
||||
-- - The password store implementation is <http://git.zx2c4.com/password-store the password-store cli>.
|
||||
-- - The <https://www.passwordstore.org/ password store>
|
||||
-- implementation is <http://git.zx2c4.com/password-store here>.
|
||||
--
|
||||
-- - Inspired by <http://babushk.in/posts/combining-xmonad-and-pass.html>
|
||||
--
|
||||
@@ -34,8 +44,11 @@ module XMonad.Prompt.Pass (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
passPrompt
|
||||
, passOTPPrompt
|
||||
, passGeneratePrompt
|
||||
, passGenerateAndCopyPrompt
|
||||
, passRemovePrompt
|
||||
, passEditPrompt
|
||||
, passTypePrompt
|
||||
) where
|
||||
|
||||
@@ -58,10 +71,12 @@ import XMonad.Util.Run (runProcessWithInput)
|
||||
--
|
||||
-- > import XMonad.Prompt.Pass
|
||||
--
|
||||
-- Then add a keybinding for 'passPrompt', 'passGeneratePrompt' or 'passRemovePrompt':
|
||||
-- Then add a keybinding for 'passPrompt', 'passGeneratePrompt',
|
||||
-- 'passRemovePrompt', 'passEditPrompt' or 'passTypePrompt':
|
||||
--
|
||||
-- > , ((modMask , xK_p) , passPrompt xpconfig)
|
||||
-- > , ((modMask .|. controlMask, xK_p) , passGeneratePrompt xpconfig)
|
||||
-- > , ((modMask .|. shiftMask, xK_p) , passEditPrompt xpconfig)
|
||||
-- > , ((modMask .|. controlMask .|. shiftMask, xK_p), passRemovePrompt xpconfig)
|
||||
--
|
||||
-- For detailed instructions on:
|
||||
@@ -112,6 +127,11 @@ mkPassPrompt promptLabel passwordFunction xpconfig = do
|
||||
passPrompt :: XPConfig -> X ()
|
||||
passPrompt = mkPassPrompt "Select password" selectPassword
|
||||
|
||||
-- | A prompt to retrieve a OTP from a given entry.
|
||||
--
|
||||
passOTPPrompt :: XPConfig -> X ()
|
||||
passOTPPrompt = mkPassPrompt "Select OTP" selectOTP
|
||||
|
||||
-- | A prompt to generate a password for a given entry.
|
||||
-- This can be used to override an already stored entry.
|
||||
-- (Beware that no confirmation is asked)
|
||||
@@ -119,6 +139,13 @@ passPrompt = mkPassPrompt "Select password" selectPassword
|
||||
passGeneratePrompt :: XPConfig -> X ()
|
||||
passGeneratePrompt = mkPassPrompt "Generate password" generatePassword
|
||||
|
||||
-- | A prompt to generate a password for a given entry and immediately copy it
|
||||
-- to the clipboard. This can be used to override an already stored entry.
|
||||
-- (Beware that no confirmation is asked)
|
||||
--
|
||||
passGenerateAndCopyPrompt :: XPConfig -> X ()
|
||||
passGenerateAndCopyPrompt = mkPassPrompt "Generate and copy password" generateAndCopyPassword
|
||||
|
||||
-- | A prompt to remove a password for a given entry.
|
||||
-- (Beware that no confirmation is asked)
|
||||
--
|
||||
@@ -131,22 +158,45 @@ passRemovePrompt = mkPassPrompt "Remove password" removePassword
|
||||
passTypePrompt :: XPConfig -> X ()
|
||||
passTypePrompt = mkPassPrompt "Type password" typePassword
|
||||
|
||||
-- | A prompt to edit a given entry.
|
||||
-- This doesn't touch the clipboard.
|
||||
--
|
||||
passEditPrompt :: XPConfig -> X ()
|
||||
passEditPrompt = mkPassPrompt "Edit password" editPassword
|
||||
|
||||
-- | Select a password.
|
||||
--
|
||||
selectPassword :: String -> X ()
|
||||
selectPassword passLabel = spawn $ "pass --clip \"" ++ escapeQuote passLabel ++ "\""
|
||||
|
||||
-- | Select a OTP.
|
||||
--
|
||||
selectOTP :: String -> X ()
|
||||
selectOTP passLabel = spawn $ "pass otp --clip \"" ++ escapeQuote passLabel ++ "\""
|
||||
|
||||
-- | Generate a 30 characters password for a given entry.
|
||||
-- If the entry already exists, it is updated with a new password.
|
||||
--
|
||||
generatePassword :: String -> X ()
|
||||
generatePassword passLabel = spawn $ "pass generate --force \"" ++ escapeQuote passLabel ++ "\" 30"
|
||||
|
||||
-- | Generate a 30 characters password for a given entry.
|
||||
-- If the entry already exists, it is updated with a new password.
|
||||
-- After generating the password, it is copied to the clipboard.
|
||||
--
|
||||
generateAndCopyPassword :: String -> X ()
|
||||
generateAndCopyPassword passLabel = spawn $ "pass generate --force -c \"" ++ escapeQuote passLabel ++ "\" 30"
|
||||
|
||||
-- | Remove a password stored for a given entry.
|
||||
--
|
||||
removePassword :: String -> X ()
|
||||
removePassword passLabel = spawn $ "pass rm --force \"" ++ escapeQuote passLabel ++ "\""
|
||||
|
||||
-- | Edit a password stored for a given entry.
|
||||
--
|
||||
editPassword :: String -> X ()
|
||||
editPassword passLabel = spawn $ "pass edit \"" ++ escapeQuote passLabel ++ "\""
|
||||
|
||||
-- | Type a password stored for a given entry using xdotool.
|
||||
--
|
||||
typePassword :: String -> X ()
|
||||
@@ -163,6 +213,7 @@ escapeQuote = concatMap escape
|
||||
getPasswords :: FilePath -> IO [String]
|
||||
getPasswords passwordStoreDir = do
|
||||
files <- runProcessWithInput "find" [
|
||||
"-L", -- Traverse symlinks
|
||||
passwordStoreDir,
|
||||
"-type", "f",
|
||||
"-name", "*.gpg",
|
||||
|
263
XMonad/Util/ExclusiveScratchpads.hs
Normal file
263
XMonad/Util/ExclusiveScratchpads.hs
Normal file
@@ -0,0 +1,263 @@
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Util.ExclusiveScratchpads
|
||||
-- Copyright : Bruce Forte (2017)
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Bruce Forte
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Named scratchpads that can be mutually exclusive.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Util.ExclusiveScratchpads (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
mkXScratchpads,
|
||||
xScratchpadsManageHook,
|
||||
-- * Keyboard related
|
||||
scratchpadAction,
|
||||
hideAll,
|
||||
resetExclusiveSp,
|
||||
-- * Mouse related
|
||||
setNoexclusive,
|
||||
resizeNoexclusive,
|
||||
floatMoveNoexclusive,
|
||||
-- * Types
|
||||
ExclusiveScratchpad(..),
|
||||
ExclusiveScratchpads,
|
||||
-- * Hooks
|
||||
nonFloating,
|
||||
defaultFloating,
|
||||
customFloating
|
||||
) where
|
||||
|
||||
import Control.Applicative ((<$>))
|
||||
import Control.Monad ((<=<),filterM,liftM2)
|
||||
import Data.Monoid (appEndo)
|
||||
import XMonad
|
||||
import XMonad.Actions.Minimize
|
||||
import XMonad.Actions.TagWindows (addTag,delTag)
|
||||
import XMonad.Hooks.ManageHelpers (doRectFloat,isInProperty)
|
||||
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- For this module to work properly, you need to use "XMonad.Layout.BoringWindows" and
|
||||
-- "XMonad.Layout.Minimize", please refer to the documentation of these modules for more
|
||||
-- information on how to configure them.
|
||||
--
|
||||
-- To use this module, put the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Utils.ExclusiveScratchpads
|
||||
-- > import XMonad.ManageHook (title,appName)
|
||||
-- > import qualified XMonad.StackSet as W
|
||||
--
|
||||
-- Add exclusive scratchpads, for example:
|
||||
--
|
||||
-- > exclusiveSps = mkXScratchpads [ ("htop", "urxvt -name htop -e htop", title =? "htop")
|
||||
-- > , ("xclock", "xclock", appName =? "xclock")
|
||||
-- > ] $ customFloating $ W.RationalRect (1/4) (1/4) (1/2) (1/2)
|
||||
--
|
||||
-- The scratchpads don\'t have to be exclusive, you can create them like this (see 'ExclusiveScratchpad'):
|
||||
--
|
||||
-- > regularSps = [ XSP "term" "urxvt -name scratchpad" (appName =? "scratchpad") defaultFloating [] ]
|
||||
--
|
||||
-- Create a list that contains all your scratchpads like this:
|
||||
--
|
||||
-- > scratchpads = exclusiveSps ++ regularSps
|
||||
--
|
||||
-- Add the hooks to your managehook (see "XMonad.Doc.Extending#Editing_the_manage_hook"), eg.:
|
||||
--
|
||||
-- > manageHook = myManageHook <+> xScratchpadsManageHook scratchpads
|
||||
--
|
||||
-- And finally add some keybindings (see "XMonad.Doc.Extending#Editing_key_bindings"):
|
||||
--
|
||||
-- > , ((modMask, xK_h), scratchpadAction scratchpads "htop")
|
||||
-- > , ((modMask, xK_c), scratchpadAction scratchpads "xclock")
|
||||
-- > , ((modMask, xK_t), scratchpadAction scratchpads "term")
|
||||
-- > , ((modMask, xK_h), hideAll scratchpads)
|
||||
--
|
||||
-- Now you can get your scratchpads by pressing the corresponding keys, if you
|
||||
-- have the @htop@ scratchpad on your current screen and you fetch the @xclock@
|
||||
-- scratchpad then @htop@ gets hidden.
|
||||
--
|
||||
-- If you move a scratchpad it still gets hidden when you fetch a scratchpad of
|
||||
-- the same family, to change that behaviour and make windows not exclusive
|
||||
-- anymore when they get resized or moved add these mouse bindings
|
||||
-- (see "XMonad.Doc.Extending#Editing_mouse_bindings"):
|
||||
--
|
||||
-- > , ((mod4Mask, button1), floatMoveNoexclusive scratchpads)
|
||||
-- > , ((mod4Mask, button3), resizeNoexclusive scratchpads)
|
||||
--
|
||||
-- To reset a moved scratchpad to the original position that you set with its hook,
|
||||
-- call @resetExclusiveSp@ when it is in focus. For example if you want to extend
|
||||
-- Mod-Return to reset the placement when a scratchpad is in focus but keep the
|
||||
-- default behaviour for tiled windows, set these key bindings:
|
||||
--
|
||||
-- > , ((modMask, xK_Return), windows W.swapMaster >> resetExclusiveSp scratchpads)
|
||||
--
|
||||
-- __Note:__ This is just an example, in general you can add more than two
|
||||
-- exclusive scratchpads and multiple families of such.
|
||||
|
||||
data ExclusiveScratchpad = XSP { name :: String -- ^ Name of the scratchpad
|
||||
, cmd :: String -- ^ Command to spawn the scratchpad
|
||||
, query :: Query Bool -- ^ Query to match the scratchpad
|
||||
, hook :: ManageHook -- ^ Hook to specify the placement policy
|
||||
, exclusive :: [String] -- ^ Names of exclusive scratchpads
|
||||
}
|
||||
|
||||
type ExclusiveScratchpads = [ExclusiveScratchpad]
|
||||
|
||||
-- -----------------------------------------------------------------------------------
|
||||
|
||||
-- | Create 'ExclusiveScratchpads' from @[(name,cmd,query)]@ with a common @hook@
|
||||
mkXScratchpads :: [(String,String,Query Bool)] -- ^ List of @(name,cmd,query)@ of the
|
||||
-- exclusive scratchpads
|
||||
-> ManageHook -- ^ The common @hook@ that they use
|
||||
-> ExclusiveScratchpads
|
||||
mkXScratchpads xs h = foldl accumulate [] xs
|
||||
where
|
||||
accumulate a (n,c,q) = XSP n c q h (filter (n/=) names) : a
|
||||
names = map (\(n,_,_) -> n) xs
|
||||
|
||||
-- | Create 'ManageHook' from 'ExclusiveScratchpads'
|
||||
xScratchpadsManageHook :: ExclusiveScratchpads -- ^ List of exclusive scratchpads from
|
||||
-- which a 'ManageHook' should be generated
|
||||
-> ManageHook
|
||||
xScratchpadsManageHook = composeAll . fmap (\sp -> query sp --> hook sp)
|
||||
|
||||
-- | Pop up/hide the scratchpad by name and possibly hide its exclusive
|
||||
scratchpadAction :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||
-> String -- ^ Name of the scratchpad to toggle
|
||||
-> X ()
|
||||
scratchpadAction xs n =
|
||||
let ys = filter ((n==).name) xs in
|
||||
|
||||
case ys of [] -> return ()
|
||||
(sp:_) -> let q = query sp in withWindowSet $ \s -> do
|
||||
ws <- filterM (runQuery q) $ W.allWindows s
|
||||
|
||||
case ws of [] -> do spawn (cmd sp)
|
||||
hideOthers xs n
|
||||
windows W.shiftMaster
|
||||
|
||||
(w:_) -> do toggleWindow w
|
||||
whenX (runQuery isExclusive w) (hideOthers xs n)
|
||||
where
|
||||
toggleWindow w = liftM2 (&&) (runQuery isMaximized w) (onCurrentScreen w) >>= \case
|
||||
True -> whenX (onCurrentScreen w) (minimizeWindow w)
|
||||
False -> do windows (flip W.shiftWin w =<< W.currentTag)
|
||||
maximizeWindowAndFocus w
|
||||
windows W.shiftMaster
|
||||
|
||||
onCurrentScreen w = withWindowSet (return . elem w . currentWindows)
|
||||
|
||||
-- | Hide all 'ExclusiveScratchpads' on the current screen
|
||||
hideAll :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||
-> X ()
|
||||
hideAll xs = mapWithCurrentScreen q minimizeWindow
|
||||
where q = joinQueries (map query xs) <&&> isExclusive <&&> isMaximized
|
||||
|
||||
-- | If the focused window is a scratchpad, the scratchpad gets reset to the original
|
||||
-- placement specified with the hook and becomes exclusive again
|
||||
resetExclusiveSp :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||
-> X ()
|
||||
resetExclusiveSp xs = withFocused $ \w -> whenX (isScratchpad xs w) $ do
|
||||
let ys = filterM (flip runQuery w . query) xs
|
||||
|
||||
unlessX (null <$> ys) $ do
|
||||
mh <- (head . map hook) <$> ys -- ys /= [], so `head` is fine
|
||||
n <- (head . map name) <$> ys -- same
|
||||
|
||||
(windows . appEndo <=< runQuery mh) w
|
||||
hideOthers xs n
|
||||
delTag "_XSP_NOEXCLUSIVE" w
|
||||
|
||||
where unlessX = whenX . fmap not
|
||||
|
||||
-- -----------------------------------------------------------------------------------
|
||||
|
||||
-- | Hide the scratchpad of the same family by name if it's on the focused workspace
|
||||
hideOthers :: ExclusiveScratchpads -> String -> X ()
|
||||
hideOthers xs n =
|
||||
let ys = concatMap exclusive $ filter ((n==).name) xs
|
||||
qs = map query $ filter ((`elem` ys).name) xs
|
||||
q = joinQueries qs <&&> isExclusive <&&> isMaximized in
|
||||
|
||||
mapWithCurrentScreen q minimizeWindow
|
||||
|
||||
-- | Conditionally map a function on all windows of the current screen
|
||||
mapWithCurrentScreen :: Query Bool -> (Window -> X ()) -> X ()
|
||||
mapWithCurrentScreen q f = withWindowSet $ \s -> do
|
||||
ws <- filterM (runQuery q) $ currentWindows s
|
||||
mapM_ f ws
|
||||
|
||||
-- | Extract all windows on the current screen from a StackSet
|
||||
currentWindows :: W.StackSet i l a sid sd -> [a]
|
||||
currentWindows = W.integrate' . W.stack . W.workspace . W.current
|
||||
|
||||
-- | Check if given window is a scratchpad
|
||||
isScratchpad :: ExclusiveScratchpads -> Window -> X Bool
|
||||
isScratchpad xs w = withWindowSet $ \s -> do
|
||||
let q = joinQueries $ map query xs
|
||||
|
||||
ws <- filterM (runQuery q) $ W.allWindows s
|
||||
return $ elem w ws
|
||||
|
||||
-- | Build a disjunction from a list of clauses
|
||||
joinQueries :: [Query Bool] -> Query Bool
|
||||
joinQueries = foldl (<||>) (liftX $ return False)
|
||||
|
||||
-- | Useful queries
|
||||
isExclusive, isMaximized :: Query Bool
|
||||
isExclusive = (notElem "_XSP_NOEXCLUSIVE" . words) <$> stringProperty "_XMONAD_TAGS"
|
||||
isMaximized = not <$> isInProperty "_NET_WM_STATE" "_NET_WM_STATE_HIDDEN"
|
||||
|
||||
-- -----------------------------------------------------------------------------------
|
||||
|
||||
-- | Make a window not exclusive anymore
|
||||
setNoexclusive :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||
-> Window -- ^ Window which should be made not
|
||||
-- exclusive anymore
|
||||
-> X ()
|
||||
setNoexclusive xs w = whenX (isScratchpad xs w) $ addTag "_XSP_NOEXCLUSIVE" w
|
||||
|
||||
-- | Float and drag the window, make it not exclusive anymore
|
||||
floatMoveNoexclusive :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||
-> Window -- ^ Window which should be moved
|
||||
-> X ()
|
||||
floatMoveNoexclusive xs w = setNoexclusive xs w
|
||||
>> focus w
|
||||
>> mouseMoveWindow w
|
||||
>> windows W.shiftMaster
|
||||
|
||||
-- | Resize window, make it not exclusive anymore
|
||||
resizeNoexclusive :: ExclusiveScratchpads -- ^ List of exclusive scratchpads
|
||||
-> Window -- ^ Window which should be resized
|
||||
-> X ()
|
||||
resizeNoexclusive xs w = setNoexclusive xs w
|
||||
>> focus w
|
||||
>> mouseResizeWindow w
|
||||
>> windows W.shiftMaster
|
||||
|
||||
-- -----------------------------------------------------------------------------------
|
||||
|
||||
-- | Manage hook that makes the window non-floating
|
||||
nonFloating :: ManageHook
|
||||
nonFloating = idHook
|
||||
|
||||
-- | Manage hook that makes the window floating with the default placement
|
||||
defaultFloating :: ManageHook
|
||||
defaultFloating = doFloat
|
||||
|
||||
-- | Manage hook that makes the window floating with custom placement
|
||||
customFloating :: W.RationalRect -- ^ @RationalRect x y w h@ that specifies relative position,
|
||||
-- height and width (see "XMonad.StackSet#RationalRect")
|
||||
-> ManageHook
|
||||
customFloating = doRectFloat
|
@@ -37,7 +37,7 @@ data Placement = OffsetLeft Int Int -- ^ An exact amount of pixels from the up
|
||||
-- In the module we suppose that those matrices are represented as [[Bool]],
|
||||
-- so the lengths of the inner lists must be the same.
|
||||
--
|
||||
-- See "Xmonad.Layout.Decoration" for usage examples
|
||||
-- See "XMonad.Layout.Decoration" for usage examples
|
||||
|
||||
-- | Gets the ('width', 'height') of an image
|
||||
imageDims :: [[Bool]] -> (Int, Int)
|
||||
|
@@ -23,6 +23,7 @@ module XMonad.Util.Invisible (
|
||||
) where
|
||||
|
||||
import Control.Applicative
|
||||
import Control.Monad.Fail
|
||||
|
||||
-- $usage
|
||||
-- A wrapper data type to store layout state that shouldn't be persisted across
|
||||
@@ -30,10 +31,10 @@ import Control.Applicative
|
||||
-- Invisible derives trivial definitions for Read and Show, so the wrapped data
|
||||
-- type need not do so.
|
||||
|
||||
newtype Invisible m a = I (m a) deriving (Monad, Applicative, Functor)
|
||||
newtype Invisible m a = I (m a) deriving (Monad, MonadFail, Applicative, Functor)
|
||||
|
||||
instance (Functor m, Monad m) => Read (Invisible m a) where
|
||||
readsPrec _ s = [(fail "Read Invisible", s)]
|
||||
instance (Functor m, Monad m, MonadFail m) => Read (Invisible m a) where
|
||||
readsPrec _ s = [(Control.Monad.Fail.fail "Read Invisible", s)]
|
||||
|
||||
instance Monad m => Show (Invisible m a) where
|
||||
show _ = ""
|
||||
|
@@ -19,6 +19,8 @@ module XMonad.Util.Themes
|
||||
, ppThemeInfo
|
||||
, xmonadTheme
|
||||
, smallClean
|
||||
, adwaitaTheme
|
||||
, adwaitaDarkTheme
|
||||
, robertTheme
|
||||
, darkTheme
|
||||
, deiflTheme
|
||||
@@ -91,6 +93,8 @@ ppThemeInfo t = themeName t <> themeDescription t <> "by" <> themeAuthor t
|
||||
listOfThemes :: [ThemeInfo]
|
||||
listOfThemes = [ xmonadTheme
|
||||
, smallClean
|
||||
, adwaitaTheme
|
||||
, adwaitaDarkTheme
|
||||
, darkTheme
|
||||
, deiflTheme
|
||||
, oxymor00nTheme
|
||||
@@ -132,6 +136,48 @@ smallClean =
|
||||
}
|
||||
}
|
||||
|
||||
-- | Matching decorations for Adwaita GTK theme
|
||||
adwaitaTheme :: ThemeInfo
|
||||
adwaitaTheme =
|
||||
newTheme { themeName = "adwaitaTheme"
|
||||
, themeAuthor = "Alex Griffin"
|
||||
, themeDescription = "Matching decorations for Adwaita GTK theme"
|
||||
, theme = def { activeColor = "#dfdcd8"
|
||||
, inactiveColor = "#f6f5f4"
|
||||
, urgentColor = "#3584e4"
|
||||
, activeBorderColor = "#bfb8b1"
|
||||
, inactiveBorderColor = "#cdc7c2"
|
||||
, urgentBorderColor = "#1658a7"
|
||||
, activeTextColor = "#2e3436"
|
||||
, inactiveTextColor = "#929595"
|
||||
, urgentTextColor = "#ffffff"
|
||||
, fontName = "xft:Cantarell:bold:size=11"
|
||||
, decoWidth = 400
|
||||
, decoHeight = 35
|
||||
}
|
||||
}
|
||||
|
||||
-- | Matching decorations for Adwaita-dark GTK theme
|
||||
adwaitaDarkTheme :: ThemeInfo
|
||||
adwaitaDarkTheme =
|
||||
newTheme { themeName = "adwaitaDarkTheme"
|
||||
, themeAuthor = "Alex Griffin"
|
||||
, themeDescription = "Matching decorations for Adwaita-dark GTK theme"
|
||||
, theme = def { activeColor = "#2d2d2d"
|
||||
, inactiveColor = "#353535"
|
||||
, urgentColor = "#15539e"
|
||||
, activeBorderColor = "#070707"
|
||||
, inactiveBorderColor = "#1c1c1c"
|
||||
, urgentBorderColor = "#030c17"
|
||||
, activeTextColor = "#eeeeec"
|
||||
, inactiveTextColor = "#929291"
|
||||
, urgentTextColor = "#ffffff"
|
||||
, fontName = "xft:Cantarell:bold:size=11"
|
||||
, decoWidth = 400
|
||||
, decoHeight = 35
|
||||
}
|
||||
}
|
||||
|
||||
-- | Don's preferred colors - from DynamicLog...;)
|
||||
donaldTheme :: ThemeInfo
|
||||
donaldTheme =
|
||||
|
@@ -1,3 +1,2 @@
|
||||
packages: ./
|
||||
../xmonad/
|
||||
../x11/
|
||||
packages:
|
||||
xmonad-contrib.cabal
|
||||
|
@@ -1,5 +1,5 @@
|
||||
name: xmonad-contrib
|
||||
version: 0.15
|
||||
version: 0.16
|
||||
homepage: http://xmonad.org/
|
||||
synopsis: Third party extensions for xmonad
|
||||
description:
|
||||
@@ -36,7 +36,7 @@ cabal-version: >= 1.6
|
||||
build-type: Simple
|
||||
bug-reports: https://github.com/xmonad/xmonad-contrib/issues
|
||||
|
||||
tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.1, GHC==8.2.2, GHC==8.4.3, GHC==8.6.1
|
||||
tested-with: GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.1
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
@@ -52,7 +52,7 @@ flag testing
|
||||
default: False
|
||||
|
||||
library
|
||||
build-depends: base >= 4.5 && < 5,
|
||||
build-depends: base >= 4.9 && < 5,
|
||||
bytestring >= 0.10 && < 0.11,
|
||||
containers >= 0.5 && < 0.7,
|
||||
directory,
|
||||
@@ -181,6 +181,7 @@ library
|
||||
XMonad.Hooks.Minimize
|
||||
XMonad.Hooks.Place
|
||||
XMonad.Hooks.PositionStoreHooks
|
||||
XMonad.Hooks.RefocusLast
|
||||
XMonad.Hooks.RestoreMinimized
|
||||
XMonad.Hooks.ScreenCorners
|
||||
XMonad.Hooks.Script
|
||||
@@ -285,6 +286,7 @@ library
|
||||
XMonad.Layout.ToggleLayouts
|
||||
XMonad.Layout.TrackFloating
|
||||
XMonad.Layout.TwoPane
|
||||
XMonad.Layout.TwoPanePersistent
|
||||
XMonad.Layout.WindowArranger
|
||||
XMonad.Layout.WindowNavigation
|
||||
XMonad.Layout.WindowSwitcherDecoration
|
||||
@@ -316,6 +318,7 @@ library
|
||||
XMonad.Util.Dmenu
|
||||
XMonad.Util.Dzen
|
||||
XMonad.Util.EZConfig
|
||||
XMonad.Util.ExclusiveScratchpads
|
||||
XMonad.Util.ExtensibleState
|
||||
XMonad.Util.Font
|
||||
XMonad.Util.Image
|
||||
|
Reference in New Issue
Block a user