Compare commits
239 Commits
32827126cc
...
45c47f7d30
Author | SHA1 | Date |
---|---|---|
Emmanuel Lepage Vallée | 45c47f7d30 | |
Emmanuel Lepage Vallée | aa01c408ca | |
Emmanuel Lepage Vallee | 546ac6aec9 | |
Emmanuel Lepage Vallée | 01ae508899 | |
Emmanuel Lepage Vallée | 7b8426bd24 | |
Emmanuel Lepage Vallée | 7806763667 | |
Emmanuel Lepage Vallee | 70cd293839 | |
Emmanuel Lepage Vallee | 4a0645e942 | |
Emmanuel Lepage Vallee | 1ff860454e | |
Emmanuel Lepage Vallee | bbf1c9270f | |
Emmanuel Lepage Vallee | d8bc791818 | |
Emmanuel Lepage Vallee | 883cdb7f41 | |
Emmanuel Lepage Vallee | ddccddb6dc | |
Emmanuel Lepage Vallee | 3f2db184ad | |
Emmanuel Lepage Vallee | e55ea2b0d5 | |
Emmanuel Lepage Vallee | 6b7ecc51f8 | |
Emmanuel Lepage Vallee | 580f16b7ff | |
Emmanuel Lepage Vallee | dcdc6a1089 | |
Emmanuel Lepage Vallee | 8481af90c5 | |
Emmanuel Lepage Vallee | b9971a5acc | |
Emmanuel Lepage Vallee | b0a2d82d8f | |
Emmanuel Lepage Vallee | 82f54ab95c | |
Emmanuel Lepage Vallee | 98dd78b777 | |
Emmanuel Lepage Vallee | aa998db626 | |
Emmanuel Lepage Vallee | 5212f0634a | |
Emmanuel Lepage Vallee | cefd4f843e | |
Emmanuel Lepage Vallée | eddabebac4 | |
Emmanuel Lepage Vallee | 64d190546d | |
Emmanuel Lepage Vallee | b038463e22 | |
Emmanuel Lepage Vallee | 24fc1043ee | |
Emmanuel Lepage Vallee | ccbe116877 | |
Emmanuel Lepage Vallee | bd8f2f936b | |
Emmanuel Lepage Vallee | 552b2a22d1 | |
Emmanuel Lepage Vallee | 9336b62f80 | |
Emmanuel Lepage Vallee | e752943b98 | |
Emmanuel Lepage Vallee | 21a111d154 | |
Emmanuel Lepage Vallee | 89c84caee4 | |
poisson-aerohead | 87e09f81ab | |
Emmanuel Lepage Vallée | 5ca16ae8a0 | |
martin f. krafft | abac464384 | |
Emmanuel Lepage Vallée | d9bfc9c37e | |
Emmanuel Lepage Vallée | c3a508a886 | |
Lucas Schwiderski | 4b6d7fd241 | |
Lucas Schwiderski | c12715e11f | |
Emmanuel Lepage Vallée | edf21742b8 | |
Emmanuel Lepage Vallee | e27b908ca0 | |
Emmanuel Lepage Vallee | 17bd5fb036 | |
Emmanuel Lepage Vallée | 73e908ed95 | |
Emmanuel Lepage Vallée | 6ad693eff0 | |
Emmanuel Lepage Vallee | d5d74e44de | |
Emmanuel Lepage Vallee | b2368c54a8 | |
Emmanuel Lepage Vallee | dd6163ffe6 | |
Emmanuel Lepage Vallee | a94a4beb6f | |
Emmanuel Lepage Vallee | b883855272 | |
Emmanuel Lepage Vallee | 529a6c2a8d | |
Emmanuel Lepage Vallee | 0828c20a55 | |
Emmanuel Lepage Vallee | 67dc363437 | |
Emmanuel Lepage Vallee | dab767af3e | |
Emmanuel Lepage Vallee | b62f343409 | |
Emmanuel Lepage Vallee | ab977b2358 | |
Emmanuel Lepage Vallee | b4afd0206b | |
Emmanuel Lepage Vallee | d9f27bdf4b | |
Emmanuel Lepage Vallee | 4b606fb3d7 | |
Emmanuel Lepage Vallée | 2a8c17daea | |
Lucas Schwiderski | f39e8d261b | |
Lucas Schwiderski | 86ec4888b3 | |
Lucas Schwiderski | cc882585de | |
Emmanuel Lepage Vallée | ee3c42652e | |
mergify[bot] | 7ae6d26363 | |
mergify[bot] | c88dcf5405 | |
mattplm | 97726e8f38 | |
ViSaturn | 13cd20780e | |
Nguyễn Gia Phong | be45b40cdb | |
aflorea-2k | 8a81745d4d | |
aflorea-2k | 260f6dbe97 | |
Emmanuel Lepage Vallée | 4b494952da | |
mergify[bot] | fcd5918bd9 | |
aflorea-2k | ddb88ed945 | |
Andrei Florea | 5f5b1dcb2b | |
Andrei Florea | b73b885c1e | |
Lucas Schwiderski | 0a68341c12 | |
Lucas Schwiderski | c689af64d5 | |
Lucas Schwiderski | 05ca439ed5 | |
Andrei Florea | 66bb09718b | |
Lucas Schwiderski | 8fab5aa703 | |
Lucas Schwiderski | 4096c19223 | |
Lucas Schwiderski | f214ef16a7 | |
Lucas Schwiderski | 4b30158176 | |
Lucas Schwiderski | f2c66b006d | |
Lucas Schwiderski | 08c893fff9 | |
Lucas Schwiderski | a938a1b807 | |
Lucas Schwiderski | c1a3f02c88 | |
mergify[bot] | 832483dd60 | |
basaran | f473639c5d | |
mergify[bot] | d1c3394e40 | |
Uli Schlachter | d25ca02493 | |
Aire-One | 53a880454f | |
Lucas Schwiderski | 741efd4171 | |
Lucas Schwiderski | ab6df1280f | |
Emmanuel Lepage Vallée | 906dc543e4 | |
Lucas Schwiderski | 1a2037758b | |
Aire-One | 4188d1df1e | |
Aire-One | a3609146aa | |
Lucas Schwiderski | 4520f33309 | |
Lucas Schwiderski | fcae67cc03 | |
Lucas Schwiderski | dc0d5df4da | |
Lucas Schwiderski | e0e8f3fd72 | |
Lucas Schwiderski | bf4ad3310d | |
Lucas Schwiderski | fd59806392 | |
Lucas Schwiderski | 1cd89e7de5 | |
Lucas Schwiderski | a6864a3e59 | |
Lucas Schwiderski | 7838e89d7f | |
Lucas Schwiderski | 7591d5cde3 | |
Lucas Schwiderski | 3cefcfffe3 | |
Lucas Schwiderski | 33b2fdfbf6 | |
Lucas Schwiderski | 16df93370f | |
Lucas Schwiderski | b94cb51770 | |
Aire-One | 6b97ec3307 | |
mergify[bot] | f4fb055512 | |
Aire-One | 8085a508d1 | |
Aire-One | 948506cde1 | |
Lucas Schwiderski | 1182552783 | |
actionless | 87fb3d7553 | |
actionless | bbaccb05bc | |
mergify[bot] | b63399f656 | |
mergify[bot] | 149d18e0e7 | |
Emmanuel Lepage Vallee | 83c31f948b | |
Emmanuel Lepage Vallee | 82bd644ea1 | |
Emmanuel Lepage Vallee | 5e5f587bea | |
Emmanuel Lepage Vallée | fedc7dc69d | |
actionless | b91a033141 | |
actionless | fa7648e6d6 | |
actionless | 2249dc3c81 | |
Yauhen Kirylau | b65025ef62 | |
Yauhen Kirylau | 59789bc2cf | |
mergify[bot] | 4c8ac65d4c | |
Lucas Schwiderski | 8c422d4f68 | |
Lucas Schwiderski | 1b49a20e0d | |
Lucas Schwiderski | e0dea455c1 | |
Lucas Schwiderski | 4f1b308e2b | |
Lucas Schwiderski | 9a3ff8eb95 | |
Lucas Schwiderski | e0244e60da | |
mergify[bot] | 9c5149d4e3 | |
Alex Belykh | 0d0647848b | |
Lucas Schwiderski | 8d88f1fa52 | |
Lucas Schwiderski | cfe7da8526 | |
Lucas Schwiderski | 4d83228f00 | |
Lucas Schwiderski | 1867ab057c | |
Lucas Schwiderski | dc8108eff7 | |
Lucas Schwiderski | bccaac9b2f | |
Lucas Schwiderski | 6a3713a090 | |
Lucas Schwiderski | d9711c3f48 | |
Lucas Schwiderski | 857b7199d6 | |
Lucas Schwiderski | e7239840f4 | |
Lucas Schwiderski | 8334f9c1b1 | |
Lucas Schwiderski | 717d09aa94 | |
Lucas Schwiderski | 645156b3c0 | |
Lucas Schwiderski | 9bc8c28c90 | |
Lucas Schwiderski | 33ee8c09ba | |
Lucas Schwiderski | 87b717495f | |
Lucas Schwiderski | 4b37eb6b50 | |
Lucas Schwiderski | f74a838506 | |
Lucas Schwiderski | 674ee00437 | |
Lucas Schwiderski | 3fbcfc8553 | |
Lucas Schwiderski | 5c3b739c25 | |
Lucas Schwiderski | 8f39fb61bd | |
mergify[bot] | a4572b9b52 | |
mergify[bot] | 4c46f6dbf3 | |
Emmanuel Lepage Vallee | c355ce7d96 | |
Emmanuel Lepage Vallee | 5cfcbac959 | |
mergify[bot] | 42d241c707 | |
mergify[bot] | 35e0acbccb | |
Uli Schlachter | d2dc428e56 | |
actionless | 5bd0ff82a6 | |
Emmanuel Lepage Vallee | ee0d793efe | |
Emmanuel Lepage Vallee | dd26b8180d | |
Emmanuel Lepage Vallee | 738e2e0467 | |
Emmanuel Lepage Vallee | d373bf2d05 | |
Emmanuel Lepage Vallee | 78b8756068 | |
Emmanuel Lepage Vallee | 6a4e555ae1 | |
mergify[bot] | c8d3de3ff4 | |
actionless | 6b93661048 | |
Yauhen Kirylau | a35acea61a | |
Emmanuel Lepage Vallée | 47fd7797b4 | |
ShayAgros | ba923cb80d | |
Shay Agroskin | 1916677df1 | |
Shay Agroskin | 3f4b1bd0cc | |
mergify[bot] | fda950d186 | |
Emmanuel Lepage Vallee | e3ab76b872 | |
Aire-One | 6226742f72 | |
Aire-One | 9807ccd5e0 | |
Emmanuel Lepage Vallée | 022be39a3f | |
Emmanuel Lepage Vallée | 702db23ee0 | |
Emmanuel Lepage Vallée | 8e8525dae9 | |
Emmanuel Lepage Vallée | 747b14142f | |
Emmanuel Lepage Vallée | 3dddc2ba78 | |
Aire-One | b184f95195 | |
Emmanuel Lepage Vallee | 25f4cfcb05 | |
Emmanuel Lepage Vallee | 90c4c60d2a | |
Aire-One | bc8a5b0230 | |
Aire-One | 0ae0add45f | |
Aire-One | b706aa9bd0 | |
Aire-One | af3b194a31 | |
Aire-One | 20a8d902c5 | |
Aire-One | 3969ad8819 | |
Aire-One | 3b12a8d19a | |
Aire-One | 3ed2fc8500 | |
Aire-One | 5baa1c97cd | |
Aire-One | 93e9361280 | |
Aire-One | 12662d4cb1 | |
Aire-One | 3a6fa10754 | |
Aire-One | b82d2a690f | |
Aire-One | 4dd689f181 | |
mergify[bot] | 7a8fa9d27a | |
mergify[bot] | 972a194b01 | |
Lucas Schwiderski | 112dc8054e | |
Lucas Schwiderski | 8907f5bfbb | |
Lucas Schwiderski | f68939bfc1 | |
Lucas Schwiderski | ec3788bf73 | |
mergify[bot] | 95558ac919 | |
Emmanuel Lepage Vallée | 13e8408562 | |
Lucas Schwiderski | c0380e3080 | |
Emmanuel Lepage Vallée | 7050ba083f | |
Emmanuel Lepage Vallee | 743b327348 | |
Emmanuel Lepage Vallee | a5c4377901 | |
Emmanuel Lepage Vallee | c48e138b01 | |
Lucas Schwiderski | e7d55567a6 | |
Shay Agroskin | 62850476d2 | |
Shay Agroskin | a18e3508f6 | |
Shay Agroskin | 749422100e | |
actionless | e833da0303 | |
Lucas Schwiderski | afced71a9a | |
Lucas Schwiderski | 058190a3c0 | |
Lucas Schwiderski | 07df24f7d0 | |
Lucas Schwiderski | 504bf53b8c | |
Lucas Schwiderski | 7bc3ec4c35 | |
actionless | a2674c2d14 | |
actionless | 20a79ed448 | |
actionless | 901bb3d88e |
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# Process (API) docs after a successful build on Travis (via ../.travis.yml).
|
# Process (API) docs after a successful build on GitHub Actions.
|
||||||
#
|
#
|
||||||
# Updated/changed documentation for "master" is pushed to gh-pages.
|
# Updated/changed documentation for "master" is pushed to gh-pages.
|
||||||
# In case of pull requests or other branches, it will get added to a separate branch.
|
# In case of pull requests or other branches, it will get added to a separate branch.
|
||||||
|
@ -9,48 +9,49 @@
|
||||||
# NOTE: stdout/stderr might/should be discarded to not leak sensitive information.
|
# NOTE: stdout/stderr might/should be discarded to not leak sensitive information.
|
||||||
|
|
||||||
echo "Post-processing (API) documentation."
|
echo "Post-processing (API) documentation."
|
||||||
echo "TRAVIS_PULL_REQUEST: $TRAVIS_PULL_REQUEST"
|
echo "PR Number: $PR_NUMBER"
|
||||||
echo "TRAVIS_BRANCH: $TRAVIS_BRANCH"
|
echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF"
|
||||||
|
|
||||||
if [ -z "$GH_APIDOC_TOKEN" ]; then
|
if [ -z "$APIDOC_TOKEN" ]; then
|
||||||
echo "No GH_APIDOC_TOKEN available. Skipping."
|
echo "No APIDOC_TOKEN available. Skipping."
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# NOTE: DO NOT USE "set -x", or anything else that would reveal GH_APIDOC_TOKEN!
|
# NOTE: DO NOT USE "set -x", or anything else that would reveal APIDOC_TOKEN!
|
||||||
|
# GitHub Actions does filter secrets, but extra caution won't hurt
|
||||||
set -e
|
set -e
|
||||||
set +x
|
set +x
|
||||||
|
|
||||||
# Display exit code in term of failure (probably due to 'set -x').
|
# Display exit code in case of failure (probably due to 'set -x').
|
||||||
trap '[ "$?" = 0 ] || echo "EXIT CODE: $?"' EXIT
|
trap '[ "$?" = 0 ] || echo "EXIT CODE: $?"' EXIT
|
||||||
|
|
||||||
REPO_APIDOC="https://${GH_APIDOC_TOKEN}@github.com/awesomeWM/apidoc"
|
REPO_APIDOC="https://${APIDOC_TOKEN}@github.com/awesomeWM/apidoc"
|
||||||
REPO_DIR="$PWD"
|
REPO_DIR="$PWD"
|
||||||
|
|
||||||
export GIT_AUTHOR_NAME="awesome-robot on Travis CI"
|
export GIT_AUTHOR_NAME="awesome-robot on GitHub Actions"
|
||||||
export GIT_AUTHOR_EMAIL="awesome-robot@users.noreply.github.com"
|
export GIT_AUTHOR_EMAIL="awesome-robot@users.noreply.github.com"
|
||||||
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
|
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
|
||||||
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
|
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
|
||||||
|
|
||||||
git clone --depth 1 --branch gh-pages "$REPO_APIDOC" build/apidoc \
|
git clone --depth 1 --branch gh-pages "$REPO_APIDOC" build/apidoc \
|
||||||
2>&1 | sed "s/$GH_APIDOC_TOKEN/GH_APIDOC_TOKEN/g"
|
2>&1 | sed "s/$APIDOC_TOKEN/APIDOC_TOKEN/g"
|
||||||
cd build/apidoc
|
cd build/apidoc
|
||||||
|
|
||||||
# This will re-use already existing branches (updated PR).
|
# This will re-use already existing branches (updated PR).
|
||||||
if [ "$TRAVIS_PULL_REQUEST" != false ]; then
|
if [ "$PR_NUMBER" != false ]; then
|
||||||
BRANCH="pr-$TRAVIS_PULL_REQUEST"
|
BRANCH="pr-$PR_NUMBER"
|
||||||
elif [ "$TRAVIS_BRANCH" != master ]; then
|
elif [ "$GITHUB_HEAD_REF" != master ]; then
|
||||||
# Use merge-base of master in branch name, to keep different branches with
|
# Use merge-base of master in branch name, to keep different branches with
|
||||||
# the same name apart.
|
# the same name apart.
|
||||||
# shellcheck disable=SC2015
|
# shellcheck disable=SC2015
|
||||||
BRANCH="$TRAVIS_BRANCH-$(cd "$REPO_DIR" \
|
BRANCH="$GITHUB_HEAD_REF-$(cd "$REPO_DIR" \
|
||||||
&& git fetch --unshallow origin master \
|
&& git fetch --unshallow origin master \
|
||||||
&& git rev-parse --short "$(git merge-base HEAD FETCH_HEAD || true)" || true)"
|
&& git rev-parse --short "$(git merge-base HEAD FETCH_HEAD || true)" || true)"
|
||||||
else
|
else
|
||||||
BRANCH="gh-pages"
|
BRANCH="gh-pages"
|
||||||
fi
|
fi
|
||||||
if [ "$BRANCH" != "gh-pages" ]; then
|
if [ "$BRANCH" != "gh-pages" ]; then
|
||||||
git checkout -b "$BRANCH" "origin/${BRANCH}" || git checkout -b "$BRANCH"
|
git checkout -b "$BRANCH" "origin/${BRANCH}" 2> /dev/null || git checkout -b "$BRANCH"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use a temporary branch for the two commits, which allows for a better UI.
|
# Use a temporary branch for the two commits, which allows for a better UI.
|
||||||
|
@ -62,7 +63,7 @@ cat > ../doc/README.md <<END
|
||||||
|
|
||||||
This repository contains the built API documentation for the
|
This repository contains the built API documentation for the
|
||||||
[awesome](https://github.com/awesomeWM/awesome) window manager. It is
|
[awesome](https://github.com/awesomeWM/awesome) window manager. It is
|
||||||
automatically updated via Travis when the master branch changes. Hence:
|
automatically updated via GitHub Actions when the master branch changes. Hence:
|
||||||
|
|
||||||
## Do NOT send pull requests here
|
## Do NOT send pull requests here
|
||||||
|
|
||||||
|
@ -72,47 +73,48 @@ END
|
||||||
|
|
||||||
# Create a patch without irrelevant changes (version / timestamp).
|
# Create a patch without irrelevant changes (version / timestamp).
|
||||||
diff -Nur . ../doc -I "Last updated" -I "<strong>Release</strong>:" \
|
diff -Nur . ../doc -I "Last updated" -I "<strong>Release</strong>:" \
|
||||||
-I "<h2>API documentation for awesome, a highly configurable X window manager (version .*)\.</h2>" \
|
-I "<h2>API documentation for awesome, a highly configurable X window manager (version .*)\.</h2>" \
|
||||||
-x .git | patch -p1
|
-x .git | patch -p1
|
||||||
|
|
||||||
git add --all .
|
git add --all .
|
||||||
if git diff --cached --exit-code --quiet; then
|
if git diff --cached --exit-code --quiet; then
|
||||||
echo "Documentation has not changed."
|
echo "Documentation has not changed."
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LAST_COMMIT_MSG="$(cd "$REPO_DIR" && git log -1 --pretty=format:%s)"
|
LAST_COMMIT_MSG="$(cd "$REPO_DIR" && git log -1 --pretty=format:%s)"
|
||||||
LAST_COMMIT="$(cd "$REPO_DIR" && git rev-parse --short HEAD)"
|
LAST_COMMIT="$(cd "$REPO_DIR" && git rev-parse --short HEAD)"
|
||||||
|
|
||||||
# Commit the relevant changes.
|
# Commit the relevant changes.
|
||||||
COMMIT_MSG="Update docs for $AWESOME_VERSION via Travis
|
COMMIT_MSG="Update docs for $AWESOME_VERSION via Github Actions
|
||||||
|
|
||||||
Last commit message:
|
Last commit message:
|
||||||
$LAST_COMMIT_MSG
|
$LAST_COMMIT_MSG
|
||||||
|
|
||||||
Commits: https://github.com/awesomeWM/awesome/compare/${TRAVIS_COMMIT_RANGE/.../..}
|
Commits: https://github.com/awesomeWM/awesome/compare/${GITHUB_BASE_REF}..${GITHUB_HEAD_REF}
|
||||||
Build URL: https://travis-ci.com/awesomeWM/awesome/builds/${TRAVIS_BUILD_ID}"
|
Build URL: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
|
||||||
|
|
||||||
git commit -m "[relevant] $COMMIT_MSG"
|
git commit -m "[relevant] $COMMIT_MSG"
|
||||||
|
|
||||||
# Commit the irrelevant changes.
|
# Commit the irrelevant changes.
|
||||||
mv .git ../doc
|
mv .git ../doc
|
||||||
cd ../doc
|
cd ../doc
|
||||||
git add --all .
|
git add --all .
|
||||||
git commit -m "[boilerplate] $COMMIT_MSG"
|
if git commit -m "[boilerplate] $COMMIT_MSG"; then
|
||||||
|
# Reorder/swap commits, to have "relevant" after "boilerplate".
|
||||||
# Reorder/swap commits, to have "relevant" after "boilerplate".
|
# This makes it show up earlier in the Github interface etc.
|
||||||
# This makes it show up earlier in the Github interface etc.
|
git tag _old
|
||||||
git tag _old
|
git reset --hard HEAD~2
|
||||||
git reset --hard HEAD~2
|
git cherry-pick _old _old~1
|
||||||
git cherry-pick _old _old~1
|
RELEVANT_REV="$(git rev-parse --short HEAD)"
|
||||||
RELEVANT_REV="$(git rev-parse --short HEAD)"
|
git tag -d _old
|
||||||
git tag -d _old
|
fi
|
||||||
|
|
||||||
git checkout "$BRANCH"
|
git checkout "$BRANCH"
|
||||||
OLD_REV="$(git rev-parse --short HEAD)"
|
OLD_REV="$(git rev-parse --short HEAD)"
|
||||||
if [ "$TRAVIS_PULL_REQUEST" != false ]; then
|
if [ "$PR_NUMBER" != false ]; then
|
||||||
MERGE_COMMIT_MSG="$COMMIT_MSG
|
MERGE_COMMIT_MSG="$COMMIT_MSG
|
||||||
Pull request: https://github.com/awesomeWM/awesome/pull/${TRAVIS_PULL_REQUEST}"
|
Pull request: https://github.com/awesomeWM/awesome/pull/${PR_NUMBER}"
|
||||||
else
|
else
|
||||||
PR_OR_ISSUE="$(echo "$COMMIT_MSG" | head -n 1 | grep -o '#[0-9]\+' || true)"
|
PR_OR_ISSUE="$(echo "$COMMIT_MSG" | head -n 1 | grep -o '#[0-9]\+' || true)"
|
||||||
if [ -n "$PR_OR_ISSUE" ]; then
|
if [ -n "$PR_OR_ISSUE" ]; then
|
||||||
|
@ -134,24 +136,24 @@ fi
|
||||||
git merge --no-ff -m "$MERGE_COMMIT_MSG" merged-update
|
git merge --no-ff -m "$MERGE_COMMIT_MSG" merged-update
|
||||||
NEW_REV="$(git rev-parse --short HEAD)"
|
NEW_REV="$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
git push origin "$BRANCH" 2>&1 | sed "s/$GH_APIDOC_TOKEN/GH_APIDOC_TOKEN/g"
|
git push origin "$BRANCH" 2>&1 | sed "s/$APIDOC_TOKEN/APIDOC_TOKEN/g"
|
||||||
|
|
||||||
# Generate compare view links.
|
# Generate compare view links.
|
||||||
# NOTE: use "\n" for line endings, not real ones for valid json!
|
# NOTE: use "\n" for line endings, not real ones for valid json!
|
||||||
COMPARE_LINKS="Compare view: https://github.com/awesomeWM/apidoc/compare/${OLD_REV}...${NEW_REV}"
|
COMPARE_LINKS="Compare view: https://github.com/awesomeWM/apidoc/compare/${OLD_REV}...${NEW_REV}"
|
||||||
COMPARE_LINKS="$COMPARE_LINKS\nRelevant changes: https://github.com/awesomeWM/apidoc/commit/${RELEVANT_REV}"
|
COMPARE_LINKS="$COMPARE_LINKS\nRelevant changes: https://github.com/awesomeWM/apidoc/commit/${RELEVANT_REV}"
|
||||||
if [ "$BRANCH" != "gh-pages" ]; then
|
if [ "$BRANCH" != "gh-pages" ]; then
|
||||||
COMPARE_LINKS="$COMPARE_LINKS\nComparison against master (gh-pages): https://github.com/awesomeWM/apidoc/compare/gh-pages...${NEW_REV}"
|
COMPARE_LINKS="$COMPARE_LINKS\nComparison against master (gh-pages): https://github.com/awesomeWM/apidoc/compare/gh-pages...${NEW_REV}"
|
||||||
fi
|
fi
|
||||||
# shellcheck disable=SC2028
|
# shellcheck disable=SC2028
|
||||||
echo "Compare links:\n$COMPARE_LINKS"
|
printf %s "Compare links:\n$COMPARE_LINKS"
|
||||||
|
|
||||||
# Post a comment to the PR.
|
# Post a comment to the PR.
|
||||||
if [ "$TRAVIS_PULL_REQUEST" != false ]; then
|
if [ "$PR_NUMBER" != false ]; then
|
||||||
curl -H "Authorization: token $GH_APIDOC_TOKEN" \
|
curl -H "Authorization: token $APIDOC_TOKEN" \
|
||||||
-d "{\"body\": \"Documentation has been updated for this PR.\n\n$COMPARE_LINKS\"}" \
|
-d "{\"body\": \"Documentation has been updated for this PR.\n\n$COMPARE_LINKS\"}" \
|
||||||
"https://api.github.com/repos/awesomeWM/awesome/issues/${TRAVIS_PULL_REQUEST}/comments" \
|
"https://api.github.com/repos/awesomeWM/awesome/issues/${PR_NUMBER}/comments" \
|
||||||
2>&1 | sed "s/$GH_APIDOC_TOKEN/GH_APIDOC_TOKEN/g"
|
2>&1 | sed "s/$APIDOC_TOKEN/APIDOC_TOKEN/g"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# vim: filetype=sh:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
# vim: filetype=sh:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
|
@ -0,0 +1,156 @@
|
||||||
|
name: Update API docs
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Trigger on push to branches `master` and `3.5`.
|
||||||
|
push:
|
||||||
|
branches: [ master, 3.5 ]
|
||||||
|
# Trigger on pull request events for PRs that have `master` as their target branch
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
# Allow running the workflow manually
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
# GitHub Actions adds `errexit` and `pipefail` by default, but we add `xtrace`
|
||||||
|
# to improve debugging some of the longer scripts.
|
||||||
|
shell: /bin/bash -o errexit -o pipefail -o xtrace {0}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
main:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Used for stable dates in documentation examples. See #2070.
|
||||||
|
SOURCE_DATE_EPOCH: "1893456000"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# Create a cache invalidation key based on the current year + week.
|
||||||
|
# This way, packages will be checked for updates once every week.
|
||||||
|
- name: Get Date
|
||||||
|
id: get-date
|
||||||
|
run: echo "::set-output name=date::$(/bin/date -u "+%Y%W")"
|
||||||
|
|
||||||
|
- name: Cache apt packages
|
||||||
|
id: cache-apt
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: /var/cache/apt/archives
|
||||||
|
# The trailing number serves as a version flag that can be incremented
|
||||||
|
# to invalidate the cache after changing the list of packages.
|
||||||
|
key: ${{ github.workflow }}-${{ runner.os }}-${{ steps.get-date.outputs.date }}-apt-3
|
||||||
|
|
||||||
|
- name: Download apt packages
|
||||||
|
if: steps.cache-apt.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --download-only -y --no-install-recommends \
|
||||||
|
asciidoctor \
|
||||||
|
cmake \
|
||||||
|
dbus-x11 \
|
||||||
|
gettext \
|
||||||
|
gir1.2-gtk-3.0 \
|
||||||
|
gir1.2-pango-1.0 \
|
||||||
|
git \
|
||||||
|
libdbus-1-dev \
|
||||||
|
libgirepository1.0-dev \
|
||||||
|
libnotify-bin \
|
||||||
|
libpango1.0-dev \
|
||||||
|
libstartup-notification0-dev \
|
||||||
|
libx11-xcb-dev \
|
||||||
|
libxcb-cursor-dev \
|
||||||
|
libxcb-icccm4-dev \
|
||||||
|
libxcb-keysyms1-dev \
|
||||||
|
libxcb-randr0-dev \
|
||||||
|
libxcb-shape0-dev \
|
||||||
|
libxcb-util0-dev \
|
||||||
|
libxcb-xfixes0-dev \
|
||||||
|
libxcb-xinerama0-dev \
|
||||||
|
libxcb-xkb-dev \
|
||||||
|
libxcb-xrm-dev \
|
||||||
|
libxcb-xtest0-dev \
|
||||||
|
libxdg-basedir-dev \
|
||||||
|
libxkbcommon-dev \
|
||||||
|
libxkbcommon-x11-dev \
|
||||||
|
xutils-dev \
|
||||||
|
lua-discount \
|
||||||
|
liblua5.3-dev \
|
||||||
|
lua5.3
|
||||||
|
|
||||||
|
- name: Install downloaded packages
|
||||||
|
run: |
|
||||||
|
sudo dpkg -i /var/cache/apt/archives/*.deb
|
||||||
|
|
||||||
|
- name: Cache luarocks
|
||||||
|
id: cache-luarocks
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: /tmp/luarocks
|
||||||
|
key: ${{ github.workflow }}-${{ runner.os }}-luarocks-3.5.0
|
||||||
|
|
||||||
|
- name: Install fresh Luarocks
|
||||||
|
if: steps.cache-luarocks.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
wget -O /tmp/luarocks.tar.gz https://github.com/luarocks/luarocks/archive/v3.5.0.tar.gz
|
||||||
|
mkdir /tmp/luarocks
|
||||||
|
tar -xf /tmp/luarocks.tar.gz -C /tmp/luarocks --strip-components=1
|
||||||
|
cd /tmp/luarocks
|
||||||
|
./configure
|
||||||
|
make build
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
- name: Install cached Luarocks
|
||||||
|
if: steps.cache-luarocks.outputs.cache-hit == 'true'
|
||||||
|
run: |
|
||||||
|
cd /tmp/luarocks
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
- name: Install rocks
|
||||||
|
run: |
|
||||||
|
sudo -H luarocks install lgi
|
||||||
|
sudo -H luarocks install ldoc
|
||||||
|
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Build Awesome version string
|
||||||
|
run: |
|
||||||
|
# If this workflow is triggered by a pull request, we get a base branch.
|
||||||
|
# Otherwise, check if the current commit has a meaningful name.
|
||||||
|
if [ -n "${{ github.base_ref }}" ]; then
|
||||||
|
AWESOME_VERSION="${{ github.base_ref }}"
|
||||||
|
else
|
||||||
|
AWESOME_VERSION="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
AWESOME_VERSION="${AWESOME_VERSION}-g$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
|
if [ "${{ github.event_name }}" == "pull_request" && -n "${{ matrix.test_prev_commits }}" ]; then
|
||||||
|
AWESOME_VERSION="${AWESOME_VERSION}-PR${{ github.event.number }}"
|
||||||
|
elif [ "${{ github.event_name }}" == "pull_request" ]; then
|
||||||
|
AWESOME_VERSION="v9999-PR${{ github.event.number }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "AWESOME_VERSION=${AWESOME_VERSION}" >> ${GITHUB_ENV}
|
||||||
|
|
||||||
|
- name: Create Build Environment
|
||||||
|
run: cmake -E make_directory -B "${{ github.workspace }}/build"
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
run: |
|
||||||
|
cmake -S ${{ github.workspace }} -B "${{ github.workspace}}/build" \
|
||||||
|
-DAWESOME_VERSION=$AWESOME_VERSION \
|
||||||
|
-DGENERATE_DOC=ON \
|
||||||
|
-DGENERATE_MANPAGES=ON \
|
||||||
|
-DDO_COVERAGE=OFF
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cd "${{ github.workspace }}/build" && make
|
||||||
|
|
||||||
|
- name: Run apidoc script
|
||||||
|
env:
|
||||||
|
PR_NUMBER: ${{ github.event.number }}
|
||||||
|
APIDOC_TOKEN: ${{ secrets.APIDOC_TOKEN || github.token }}
|
||||||
|
run: .github/scripts/apidoc.sh
|
||||||
|
|
||||||
|
# vim: filetype=yaml:expandtab:shiftwidth=2:tabstop=2
|
|
@ -0,0 +1,394 @@
|
||||||
|
name: Build & Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Trigger on push to branches `master` and `3.5`.
|
||||||
|
push:
|
||||||
|
branches: [ master, 3.5 ]
|
||||||
|
# Trigger on pull request events for PRs that have `master` as their target branch
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
# Allow running the workflow manually
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
# GitHub Actions adds `errexit` and `pipefail` by default, but we add `xtrace`
|
||||||
|
# to improve debugging some of the longer scripts.
|
||||||
|
shell: /bin/bash -o errexit -o pipefail -o xtrace {0}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
main:
|
||||||
|
# The type of runner that the job will run on
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
name: ${{ matrix.test_name }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
# Let other jobs continue even if one of them fails
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- test_name: "codecov-lua5.3"
|
||||||
|
lua_version: "5.3"
|
||||||
|
lua_name: "lua5.3"
|
||||||
|
coverage: "codecov"
|
||||||
|
manual_screens: true
|
||||||
|
|
||||||
|
- test_name: "coveralls-lua5.2"
|
||||||
|
lua_version: "5.2"
|
||||||
|
lua_name: "lua5.2"
|
||||||
|
coverage: "coveralls"
|
||||||
|
manual_screens: true
|
||||||
|
|
||||||
|
- test_name: "lua5.1"
|
||||||
|
lua_version: "5.1"
|
||||||
|
lua_name: "lua5.1"
|
||||||
|
|
||||||
|
- test_name: "luajit"
|
||||||
|
lua_version: "5.1"
|
||||||
|
lua_name: "luajit"
|
||||||
|
lua_library: "/usr/lib/x86_64-linux-gnu/libluajit-5.1.so"
|
||||||
|
lua_include: "/usr/include/luajit-2.1"
|
||||||
|
luarocks_args: "--lua-suffix=jit-2.1.0-beta3"
|
||||||
|
|
||||||
|
# Lua 5.2 with fixed lgi version and screen size not divisible by 2.
|
||||||
|
- test_name: "fixed-lgi-lua5.2"
|
||||||
|
lua_version: "5.2"
|
||||||
|
lua_name: "lua5.2"
|
||||||
|
lgi_version: "0.9.2"
|
||||||
|
tests_screen_size: "1921x1079"
|
||||||
|
check_qa: true
|
||||||
|
empty_theme_while_loading: true
|
||||||
|
test_prev_commits: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
LUA: ${{ matrix.lua_version }}
|
||||||
|
LUAINCLUDE: ${{ matrix.lua_include || format('/usr/include/lua{0}', matrix.lua_version) }}
|
||||||
|
LUALIBRARY: ${{ matrix.lua_library || format('/usr/lib/x86_64-linux-gnu/liblua{0}.so', matrix.lua_version) }}
|
||||||
|
TESTS_SCREEN_SIZE: ${{ matrix.tests_screen_size }}
|
||||||
|
TEST_TIMEOUT: '100'
|
||||||
|
CI: 'true'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# Create a cache invalidation key based on the current year + week.
|
||||||
|
# This way, packages will be checked for updates once every week.
|
||||||
|
- name: Get Date
|
||||||
|
id: get-date
|
||||||
|
run: echo "::set-output name=date::$(/bin/date -u "+%Y%W")"
|
||||||
|
|
||||||
|
- name: Cache apt packages
|
||||||
|
id: cache-apt
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: /var/cache/apt/archives
|
||||||
|
# The trailing number serves as a version flag that can be incremented
|
||||||
|
# to invalidate the cache after changing the list of packages.
|
||||||
|
key: ${{ github.workflow }}-${{ runner.os }}-${{ steps.get-date.outputs.date }}-apt-4
|
||||||
|
|
||||||
|
- name: Download apt packages
|
||||||
|
if: steps.cache-apt.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --download-only -y --no-install-recommends \
|
||||||
|
asciidoctor \
|
||||||
|
cmake \
|
||||||
|
dbus-x11 \
|
||||||
|
gettext \
|
||||||
|
gir1.2-gtk-3.0 \
|
||||||
|
gir1.2-pango-1.0 \
|
||||||
|
git \
|
||||||
|
libdbus-1-dev \
|
||||||
|
libgirepository1.0-dev \
|
||||||
|
libnotify-bin \
|
||||||
|
libpango1.0-dev \
|
||||||
|
libstartup-notification0-dev \
|
||||||
|
libx11-xcb-dev \
|
||||||
|
libxcb-cursor-dev \
|
||||||
|
libxcb-icccm4-dev \
|
||||||
|
libxcb-keysyms1-dev \
|
||||||
|
libxcb-randr0-dev \
|
||||||
|
libxcb-shape0-dev \
|
||||||
|
libxcb-util0-dev \
|
||||||
|
libxcb-xfixes0-dev \
|
||||||
|
libxcb-xinerama0-dev \
|
||||||
|
libxcb-xkb-dev \
|
||||||
|
libxcb-xrm-dev \
|
||||||
|
libxcb-xtest0-dev \
|
||||||
|
libxdg-basedir-dev \
|
||||||
|
libxkbcommon-dev \
|
||||||
|
libxkbcommon-x11-dev \
|
||||||
|
wmctrl \
|
||||||
|
x11-apps \
|
||||||
|
xcb-proto \
|
||||||
|
xdotool \
|
||||||
|
xorg \
|
||||||
|
xserver-xephyr \
|
||||||
|
xterm \
|
||||||
|
xutils-dev \
|
||||||
|
xvfb \
|
||||||
|
zsh \
|
||||||
|
lua-discount
|
||||||
|
|
||||||
|
- name: Install downloaded packages
|
||||||
|
run: sudo dpkg -i /var/cache/apt/archives/*.deb
|
||||||
|
|
||||||
|
- name: Install Lua packages
|
||||||
|
run: |
|
||||||
|
if [ "${{ matrix.lua_name }}" = "luajit" ]; then
|
||||||
|
sudo apt-get install libluajit-5.1-dev luajit
|
||||||
|
fi
|
||||||
|
sudo apt-get install liblua${{ matrix.lua_version }}-dev lua${{ matrix.lua_version }}
|
||||||
|
|
||||||
|
- name: Cache luarocks
|
||||||
|
id: cache-luarocks
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: /tmp/luarocks
|
||||||
|
# The build input for luarocks changes per test, so we need separate caches
|
||||||
|
key: ${{ github.workflow }}-${{ runner.os }}-${{ matrix.test_name }}-luarocks-3.5.0
|
||||||
|
|
||||||
|
- name: Install fresh Luarocks
|
||||||
|
if: steps.cache-luarocks.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
wget -O /tmp/luarocks.tar.gz https://github.com/luarocks/luarocks/archive/v3.5.0.tar.gz
|
||||||
|
mkdir /tmp/luarocks
|
||||||
|
tar -xf /tmp/luarocks.tar.gz -C /tmp/luarocks --strip-components=1
|
||||||
|
cd /tmp/luarocks
|
||||||
|
./configure --lua-version=${{ matrix.lua_version }} --with-lua-include=${LUAINCLUDE} ${{ matrix.luarocks_args }}
|
||||||
|
make build
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
- name: Install cached Luarocks
|
||||||
|
if: steps.cache-luarocks.outputs.cache-hit == 'true'
|
||||||
|
run: |
|
||||||
|
cd /tmp/luarocks
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
- name: Cache xcb-errors
|
||||||
|
id: cache-xcb-errors
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: /tmp/xcb-errors
|
||||||
|
key: ${{ github.workflow }}-${{ runner.os }}-xcb-errors-1.0
|
||||||
|
|
||||||
|
- name: Install fresh xcb-errors
|
||||||
|
if: steps.cache-xcb-errors.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
git clone --recursive --depth 1 --branch 1.0 https://gitlab.freedesktop.org/xorg/lib/libxcb-errors.git /tmp/xcb-errors
|
||||||
|
cd /tmp/xcb-errors
|
||||||
|
./autogen.sh --prefix=/usr
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
- name: Install cached xcb-errors
|
||||||
|
if: steps.cache-xcb-errors.outputs.cache-hit == 'true'
|
||||||
|
run: |
|
||||||
|
cd /tmp/xcb-errors
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
- name: Install rocks
|
||||||
|
run: |
|
||||||
|
sudo -H luarocks install lgi ${{ matrix.lgi_version }}
|
||||||
|
sudo -H luarocks install ldoc
|
||||||
|
sudo -H luarocks install busted
|
||||||
|
|
||||||
|
- name: Install QA check rocks
|
||||||
|
if: matrix.check_qa
|
||||||
|
run: |
|
||||||
|
sudo -H luarocks install luacheck
|
||||||
|
sudo -H luarocks install depgraph
|
||||||
|
|
||||||
|
- name: Install cluacov rock
|
||||||
|
if: matrix.coverage
|
||||||
|
run: sudo -H luarocks install cluacov
|
||||||
|
|
||||||
|
- name: Install coveralls rock
|
||||||
|
if: matrix.coverage == 'coveralls'
|
||||||
|
run: sudo -H luarocks install luacov-coveralls
|
||||||
|
|
||||||
|
- name: Install codecov.io uploader
|
||||||
|
if: matrix.coverage == 'codecov'
|
||||||
|
run: wget -O /tmp/codecov-bash https://codecov.io/bash
|
||||||
|
|
||||||
|
# Check out repository to ${{ github.workspace }}
|
||||||
|
# Automatically picks the current branch/PR
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Create Build Environment
|
||||||
|
run: cmake -E make_directory -B "${{ github.workspace }}/build"
|
||||||
|
|
||||||
|
- name: Build Awesome version string
|
||||||
|
run: |
|
||||||
|
# If this workflow is triggered by a pull request, we get a base branch.
|
||||||
|
# Otherwise, check if the current commit has a meaningful name.
|
||||||
|
if [ -n "${{ github.base_ref }}" ]; then
|
||||||
|
AWESOME_VERSION="${{ github.base_ref }}"
|
||||||
|
else
|
||||||
|
AWESOME_VERSION="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
AWESOME_VERSION="${AWESOME_VERSION}-g$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
|
if [ "${{ github.event_name }}" == "pull_request" && -n "${{ matrix.test_prev_commits }}" ]; then
|
||||||
|
AWESOME_VERSION="${AWESOME_VERSION}-PR${{ github.event.number }}"
|
||||||
|
elif [ "${{ github.event_name }}" == "pull_request" ]; then
|
||||||
|
AWESOME_VERSION="v9999-PR${{ github.event.number }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "AWESOME_VERSION=${AWESOME_VERSION}" >> ${GITHUB_ENV}
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
run: |
|
||||||
|
cmake -S ${{ github.workspace }} -B "${{ github.workspace}}/build" \
|
||||||
|
-DAWESOME_VERSION=$AWESOME_VERSION \
|
||||||
|
-DLUA_LIBRARY=$LUALIBRARY \
|
||||||
|
-DLUA_INCLUDE_DIR=$LUAINCLUDE \
|
||||||
|
-DGENERATE_DOC=OFF \
|
||||||
|
-DGENERATE_MANPAGES=OFF \
|
||||||
|
-DDO_COVERAGE=${{ matrix.coverage }} \
|
||||||
|
-DTEST_MANUAL_SCREENS=${{ matrix.manual_screens }} \
|
||||||
|
-DSTRICT_TESTS=true \
|
||||||
|
-DCMAKE_C_FLAGS="-Werror"
|
||||||
|
|
||||||
|
# Break beautiful so that trying to access the theme before `beautiful.init()` causes an error
|
||||||
|
- name: Patch beautiful for an empty default theme
|
||||||
|
if: matrix.empty_theme_while_loading
|
||||||
|
run: |
|
||||||
|
sed -i -e 's/theme = {}/theme = setmetatable({}, { __index = function() error("May not access theme before beautiful.init()") end })/' lib/beautiful/init.lua
|
||||||
|
grep -q 'May not access' lib/beautiful/init.lua
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cd "${{ github.workspace }}/build" && make
|
||||||
|
|
||||||
|
# Executable needs to be run once to provide coverage results
|
||||||
|
- name: Install and run
|
||||||
|
run: |
|
||||||
|
cd "${{ github.workspace }}/build"
|
||||||
|
sudo make install
|
||||||
|
awesome --version
|
||||||
|
|
||||||
|
- name: Run integration tests
|
||||||
|
run: cd "${{ github.workspace }}/build" && make check-integration
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: cd "${{ github.workspace }}/build" && make check-unit
|
||||||
|
|
||||||
|
- name: Run examples tests
|
||||||
|
if: matrix.coverage
|
||||||
|
run: cd "${{ github.workspace }}/build" && make check-examples
|
||||||
|
|
||||||
|
- name: Run requires tests
|
||||||
|
if: matrix.coverage
|
||||||
|
run: cd "${{ github.workspace }}/build" && make check-requires
|
||||||
|
|
||||||
|
- name: Run themes tests
|
||||||
|
run: cd "${{ github.workspace }}/build" && make check-themes
|
||||||
|
|
||||||
|
- name: Upload Lua code coverage report
|
||||||
|
if: matrix.coverage == 'codecov'
|
||||||
|
run: |
|
||||||
|
luacov
|
||||||
|
bash /tmp/codecov-bash -f build/luacov.report.out -X gcov -X coveragepy -F luacov
|
||||||
|
|
||||||
|
- name: Upload C code coverage report
|
||||||
|
if: matrix.coverage == 'codecov'
|
||||||
|
run: |
|
||||||
|
# Report coverage for each .gcno file separately.
|
||||||
|
# gcov will create .gcov files for the same source (e.g. for
|
||||||
|
# globalconf.h from awesome.c.gcno and event.c.gcno).
|
||||||
|
i=0
|
||||||
|
cd "${{ github.workspace }}/build"
|
||||||
|
find -path "*/lgi-check.dir" -prune -o \( -name '*.gcno' -print \) | while read -r gcno; do
|
||||||
|
gcov -pb "$gcno"
|
||||||
|
|
||||||
|
mkdir gcov.$(( ++i ))
|
||||||
|
mv *.gcov "gcov.$i"
|
||||||
|
|
||||||
|
# Delete any files for /usr.
|
||||||
|
# They are not relevant and might cause "Invalid path part" errors
|
||||||
|
# with Code Climate.
|
||||||
|
find "gcov.$i" -maxdepth 1 -type f -name '#usr#*.gcov' -delete
|
||||||
|
done
|
||||||
|
|
||||||
|
# Upload to Codecov.
|
||||||
|
bash /tmp/codecov-bash -X gcov -X coveragepy -F gcov
|
||||||
|
|
||||||
|
- name: Merge coverage
|
||||||
|
if: matrix.coverage == 'coveralls'
|
||||||
|
# Coveralls doesn't support GitHub Actions with `service_name` + `service_job_id` (yet?),
|
||||||
|
# but `luacov-coveralls` behaves as if they did by default.
|
||||||
|
# We need to explicitly remove the `service_name`, so that Coveralls uses the
|
||||||
|
# `repo_token` instead.
|
||||||
|
# See "Referencing a repository": https://docs.coveralls.io/api-introduction
|
||||||
|
run: |
|
||||||
|
luacov-coveralls \
|
||||||
|
--verbose \
|
||||||
|
--merge \
|
||||||
|
--repo-token ${{ secrets.COVERALLS_REPO_TOKEN || github.token }} \
|
||||||
|
--service-name ""
|
||||||
|
|
||||||
|
# `check-qa` is the only test that doesn't get a coverage report, so it has to run after all of that.
|
||||||
|
- name: Run qa tests
|
||||||
|
if: matrix.check_qa
|
||||||
|
run: cd "${{ github.workspace }}/build" && make check-qa
|
||||||
|
|
||||||
|
# Check each commit separately (to make git-bisect less annoying).
|
||||||
|
- name: Test previous commits
|
||||||
|
if: matrix.test_prev_commits && github.event_name == 'pull_request'
|
||||||
|
run: |
|
||||||
|
git remote add fork "${{ github.event.pull_request.head.repo.git_url }}"
|
||||||
|
# `actions/checkout` creates a shallow repo (`--depth 1`) by default,
|
||||||
|
# which is fine for everything up until now. But we need individual commits now.
|
||||||
|
# And we only want to unshallow now, to not slow down the checkout for other jobs.
|
||||||
|
git fetch --unshallow --all
|
||||||
|
|
||||||
|
rev_list="$(git rev-list --bisect-all origin/${{ github.base_ref }}..fork/${{ github.head_ref }})"
|
||||||
|
# The most recent commit has already been tested. So if that's the
|
||||||
|
# only commit in the PR, we can stop here.
|
||||||
|
if [[ $(echo "$rev_list" | wc -l) -lt 2 ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
commits="$(echo "$rev_list" | grep -v 'dist=0' | cut -d' ' -f 1)"
|
||||||
|
n="$(echo "$commits" | wc -l)"
|
||||||
|
|
||||||
|
echo "Testing $n commits:"
|
||||||
|
echo "$commits" | xargs -I{} git log -1 --pretty='%h %s' {}
|
||||||
|
|
||||||
|
failed=""
|
||||||
|
for commit in $commits; do
|
||||||
|
echo "Testing commit $commit"
|
||||||
|
|
||||||
|
# Some files are updated when compiling...
|
||||||
|
git checkout --force "$commit"
|
||||||
|
git show --stat --oneline
|
||||||
|
|
||||||
|
cmake -S ${{ github.workspace }} -B "${{ github.workspace}}/build" \
|
||||||
|
-DAWESOME_VERSION=$AWESOME_VERSION \
|
||||||
|
-DLUA_LIBRARY=$LUALIBRARY \
|
||||||
|
-DLUA_INCLUDE_DIR=$LUAINCLUDE \
|
||||||
|
-DGENERATE_DOC=OFF \
|
||||||
|
-DGENERATE_MANPAGES=OFF \
|
||||||
|
-DDO_COVERAGE=OFF \
|
||||||
|
-DTEST_MANUAL_SCREENS=${{ matrix.manual_screens }} \
|
||||||
|
-DSTRICT_TESTS=true \
|
||||||
|
-DCMAKE_C_FLAGS="-Werror"
|
||||||
|
|
||||||
|
cd "${{ github.workspace}}/build"
|
||||||
|
if ! ( make && make check-unit ); then
|
||||||
|
failed="$failed $commit"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
git checkout --quiet --force fork/${{ github.head_ref }}
|
||||||
|
if [ -n "$failed" ]; then
|
||||||
|
echo "Checks failed for these commits:"
|
||||||
|
|
||||||
|
for c in $failed; do
|
||||||
|
git show --no-patch --pretty="%h %s (%an, %ad)" "$c"
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# vim: filetype=yaml:expandtab:shiftwidth=2:tabstop=2
|
|
@ -49,7 +49,7 @@ globals = {
|
||||||
-- Enable cache (uses .luacheckcache relative to this rc file).
|
-- Enable cache (uses .luacheckcache relative to this rc file).
|
||||||
cache = true
|
cache = true
|
||||||
|
|
||||||
-- Do not enable colors to make the Travis CI output more readable.
|
-- Do not enable colors to make the CI output more readable.
|
||||||
color = false
|
color = false
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -3,7 +3,8 @@ pull_request_rules:
|
||||||
conditions:
|
conditions:
|
||||||
- label!=no-mergify
|
- label!=no-mergify
|
||||||
- '#approved-reviews-by>=2'
|
- '#approved-reviews-by>=2'
|
||||||
- status-success=Travis CI - Pull Request
|
- status-success=Build & Test
|
||||||
|
- status-success=Update API docs
|
||||||
- status-success=codecov/patch
|
- status-success=codecov/patch
|
||||||
- status-success=coverage/coveralls
|
- status-success=coverage/coveralls
|
||||||
actions:
|
actions:
|
||||||
|
|
340
.travis.yml
340
.travis.yml
|
@ -1,340 +0,0 @@
|
||||||
dist: xenial
|
|
||||||
language: c
|
|
||||||
|
|
||||||
# Build only master and stable branches. Other branches go through PRs.
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- 3.5
|
|
||||||
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages: &BASE_PACKAGES
|
|
||||||
# Build dependencies.
|
|
||||||
# See also `apt-cache showsrc awesome | grep -E '^(Version|Build-Depends)'`.
|
|
||||||
- libnotify-bin
|
|
||||||
- libcairo2-dev
|
|
||||||
- gir1.2-gtk-3.0
|
|
||||||
- libpango1.0-dev
|
|
||||||
- libxcb-xtest0-dev
|
|
||||||
- libxcb-icccm4-dev
|
|
||||||
- libxcb-randr0-dev
|
|
||||||
- libxcb-keysyms1-dev
|
|
||||||
- libxcb-xinerama0-dev
|
|
||||||
- libdbus-1-dev
|
|
||||||
- libxdg-basedir-dev
|
|
||||||
- libstartup-notification0-dev
|
|
||||||
- imagemagick
|
|
||||||
- libxcb1-dev
|
|
||||||
- libxcb-shape0-dev
|
|
||||||
- libxcb-util0-dev
|
|
||||||
- libx11-xcb-dev
|
|
||||||
- libxcb-cursor-dev
|
|
||||||
- libxcb-xkb-dev
|
|
||||||
- libxcb-xfixes0-dev
|
|
||||||
- libxkbcommon-dev
|
|
||||||
- libxkbcommon-x11-dev
|
|
||||||
# Deps for tests.
|
|
||||||
- dbus-x11
|
|
||||||
- xterm
|
|
||||||
- xdotool
|
|
||||||
- wmctrl
|
|
||||||
- xterm
|
|
||||||
- xvfb
|
|
||||||
- zsh
|
|
||||||
- x11-apps
|
|
||||||
# Need xorg-macros
|
|
||||||
- xutils-dev
|
|
||||||
# lgi.
|
|
||||||
- gir1.2-pango-1.0
|
|
||||||
- libgirepository1.0-dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- env: LUA=5.2 LUANAME=lua5.2 DO_COVERAGE=coveralls MANUAL_SCREENS=1
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- *BASE_PACKAGES
|
|
||||||
- liblua5.2-dev
|
|
||||||
- lua5.2
|
|
||||||
- env: LUA=5.3 LUANAME=lua5.3 DO_COVERAGE=codecov MANUAL_SCREENS=1
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- *BASE_PACKAGES
|
|
||||||
- liblua5.3-dev
|
|
||||||
- lua5.3
|
|
||||||
- env: LUA=5.1 LUANAME=lua5.1 BUILD_IN_DIR=/tmp/awesome-build WITH_XCB_ERRORS=yes
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- *BASE_PACKAGES
|
|
||||||
- liblua5.1-dev
|
|
||||||
- lua5.1
|
|
||||||
# For xcb-errors
|
|
||||||
- xcb-proto
|
|
||||||
- env: LUA=5.1 LUANAME=luajit-2.0 LUALIBRARY=/usr/lib/x86_64-linux-gnu/libluajit-5.1.so LUAROCKS_ARGS=--lua-suffix=jit-2.0.4
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- *BASE_PACKAGES
|
|
||||||
- libluajit-5.1-dev
|
|
||||||
- luajit
|
|
||||||
# Lua 5.2 with older lgi and screen size not divisible by 2.
|
|
||||||
# SOURCE_DATE_EPOCH: used for stable dates during build.
|
|
||||||
- env: LUA=5.2 LUANAME=lua5.2 LGIVER=0.8.0 TESTS_SCREEN_SIZE=1921x1079 BUILD_APIDOC=true DO_CHECKQA=1 EMPTY_THEME_WHILE_LOADING=1 SOURCE_DATE_EPOCH=1893456000 TEST_PREV_COMMITS=1
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- *BASE_PACKAGES
|
|
||||||
- liblua5.2-dev
|
|
||||||
- lua5.2
|
|
||||||
- lua-discount
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
# Secure GH_APIDOC_TOKEN to push to awesomeWM/apidoc.
|
|
||||||
- secure: "R/HYDclnws1I1+v9Yjt+RKa4CsFhbBT9tiwE3EfPhEj2KCYX4sFRMxuZvLf5sq0XWdrQaPhQ54fgAZGr3f054JKRXcTB0g9J6nhSHz9kIjPh446gafUhEeDQcZRwM/MeCWiwFIkiZm6smYoDFE9JTWu6quNV+lQ4kcVDOp2ibEc="
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- if [ "$BUILD_APIDOC" = true ] && [ -n "$DO_COVERAGE" ]; then echo "BUILD_APIDOC and DO_COVERAGE are not meant to be used together." >&2; exit 1; fi
|
|
||||||
- if [ -z $LUAINCLUDE ]; then LUAINCLUDE=/usr/include/${LUANAME}; fi
|
|
||||||
- if [ -z $LUALIBRARY ]; then LUALIBRARY=/usr/lib/x86_64-linux-gnu/lib${LUANAME}.so; fi
|
|
||||||
- cmake --version
|
|
||||||
|
|
||||||
install:
|
|
||||||
- sudo gem install asciidoctor
|
|
||||||
- |
|
|
||||||
# Install xcb-util-xrm.
|
|
||||||
set -e
|
|
||||||
(git clone --depth 1 --recursive https://github.com/Airblader/xcb-util-xrm.git /tmp/xcb-util-xrm
|
|
||||||
cd /tmp/xcb-util-xrm
|
|
||||||
./autogen.sh --prefix=/usr
|
|
||||||
make && sudo make install)
|
|
||||||
|
|
||||||
# Install xcb-errors if needed
|
|
||||||
- |
|
|
||||||
set -e
|
|
||||||
if [[ "$WITH_XCB_ERRORS" == "yes" ]]; then
|
|
||||||
git clone --depth 1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-errors.git /tmp/xcb-errors
|
|
||||||
(cd /tmp/xcb-errors
|
|
||||||
./autogen.sh --prefix=/usr
|
|
||||||
make && sudo make install)
|
|
||||||
fi
|
|
||||||
|
|
||||||
- |
|
|
||||||
# Install Lua (per env).
|
|
||||||
if [[ "$LUANAME" == "luajit-2.0" ]]; then
|
|
||||||
# "Create" /usr/bin/lua if needed (Yup, this is a bad hack)
|
|
||||||
if [ ! -e "/usr/bin/lua" ]; then sudo ln -s /usr/bin/luajit /usr/bin/lua; fi
|
|
||||||
else
|
|
||||||
# lua5.3 does not install /usr/bin/lua, but lua5.1/lua5.2 do.
|
|
||||||
if [ ! -e "/usr/bin/lua" ]; then sudo ln -s /usr/bin/${LUANAME} /usr/bin/lua; fi
|
|
||||||
fi
|
|
||||||
- lua -v
|
|
||||||
|
|
||||||
# Install luarocks (for the selected Lua version).
|
|
||||||
- |
|
|
||||||
travis_retry wget https://github.com/luarocks/luarocks/archive/v3.0.4.tar.gz
|
|
||||||
tar xf v3.0.4.tar.gz -C /tmp
|
|
||||||
(cd /tmp/luarocks-* \
|
|
||||||
&& ./configure --lua-version=$LUA --with-lua-include=${LUAINCLUDE} ${LUAROCKS_ARGS} \
|
|
||||||
&& make build \
|
|
||||||
&& sudo make install)
|
|
||||||
|
|
||||||
- travis_retry sudo luarocks install lgi $LGIVER
|
|
||||||
|
|
||||||
# Install busted for "make check-unit".
|
|
||||||
- travis_retry sudo luarocks install busted
|
|
||||||
# Install luacheck for "make check-qa".
|
|
||||||
- if [ "$DO_CHECKQA" = 1 ]; then travis_retry sudo luarocks install luacheck; fi
|
|
||||||
# Install depgraph for "make check-qa".
|
|
||||||
- if [ "$DO_CHECKQA" = 1 ]; then travis_retry sudo luarocks install depgraph; fi
|
|
||||||
|
|
||||||
# Install ldoc for building docs and check-ldoc-warnings.
|
|
||||||
- |
|
|
||||||
if [ "$BUILD_APIDOC" = "true" ] || [ "$DO_CHECKQA" = 1 ]; then
|
|
||||||
travis_retry sudo luarocks install ldoc
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install dependencies for code coverage testing.
|
|
||||||
- if [ -n "$DO_COVERAGE" ]; then sudo luarocks install cluacov; fi
|
|
||||||
- if [ "$DO_COVERAGE" = "coveralls" ]; then sudo luarocks install luacov-coveralls; fi
|
|
||||||
|
|
||||||
# Determine custom version.
|
|
||||||
- export AWESOME_VERSION="${TRAVIS_BRANCH}-g$(git rev-parse --short HEAD)"
|
|
||||||
- |
|
|
||||||
if [ "$TRAVIS_PULL_REQUEST" != false ] && [ "$TEST_PREV_COMMITS" = 1 ]; then
|
|
||||||
AWESOME_VERSION="${AWESOME_VERSION}-PR${TRAVIS_PULL_REQUEST}"
|
|
||||||
elif [ "$TRAVIS_PULL_REQUEST" != false ]; then
|
|
||||||
AWESOME_VERSION="v9999-PR${TRAVIS_PULL_REQUEST}";
|
|
||||||
fi
|
|
||||||
# function for codecov support
|
|
||||||
- if [ "$DO_COVERAGE" = "codecov" ]; then travis_retry wget -O /tmp/codecov-bash https://codecov.io/bash; fi
|
|
||||||
- |
|
|
||||||
do_codecov() {
|
|
||||||
echo "== do_codecov in $PWD: $*: build/luacov.stats.out: =="
|
|
||||||
if [ "$DO_COVERAGE" = "codecov" ]; then
|
|
||||||
test -f build/luacov.stats.out || { echo 'build/luacov.stats.out does not exist.'; return 1; }
|
|
||||||
luacov || return 1
|
|
||||||
travis_retry bash /tmp/codecov-bash -f build/luacov.report.out -X gcov -X coveragepy -F luacov -e LUANAME,LGIVER || return 1
|
|
||||||
rm build/luacov.report.out build/luacov.stats.out
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
- |
|
|
||||||
do_codecov_gcov() {
|
|
||||||
echo "== do_codecov_gcov in $PWD: $*: =="
|
|
||||||
if [ "$DO_COVERAGE" = "codecov" ]; then
|
|
||||||
# Report coverage for each .gcno file separately.
|
|
||||||
# gcov will create .gcov files for the same source (e.g. for
|
|
||||||
# globalconf.h from awesome.c.gcno and event.c.gcno).
|
|
||||||
i=0
|
|
||||||
set -x
|
|
||||||
(
|
|
||||||
cd build
|
|
||||||
find -path "*/lgi-check.dir" -prune -o \( -name '*.gcno' -print \) | while read -r gcno; do
|
|
||||||
gcov -pb "$gcno"
|
|
||||||
|
|
||||||
mkdir gcov.$(( ++i ))
|
|
||||||
mv *.gcov "gcov.$i"
|
|
||||||
|
|
||||||
# Delete any files for /usr.
|
|
||||||
# They are not relevant and might cause "Invalid path part" errors
|
|
||||||
# with Code Climate.
|
|
||||||
find "gcov.$i" -maxdepth 1 -type f -name '#usr#*.gcov' -delete
|
|
||||||
done
|
|
||||||
|
|
||||||
# Upload to Codecov.
|
|
||||||
travis_retry bash /tmp/codecov-bash -X gcov -X coveragepy -F gcov -e LUANAME,LGIVER
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Functions for custom Travis folds.
|
|
||||||
- |
|
|
||||||
travis_fold_start() {
|
|
||||||
travis_fold start "$1"
|
|
||||||
travis_fold_current="$1"
|
|
||||||
}
|
|
||||||
travis_fold_end() {
|
|
||||||
travis_fold end "$travis_fold_current"
|
|
||||||
}
|
|
||||||
travis_run_in_fold() {
|
|
||||||
travis_fold_start "$1"
|
|
||||||
shift
|
|
||||||
"$@" || return 1
|
|
||||||
travis_fold_end
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
script:
|
|
||||||
- if [ "$MANUAL_SCREENS" != "1" ]; then export MANUAL_SCREENS=0; fi
|
|
||||||
- export CMAKE_ARGS="-DLUA_LIBRARY=${LUALIBRARY} -D LUA_INCLUDE_DIR=${LUAINCLUDE} -D OVERRIDE_VERSION=$AWESOME_VERSION -D STRICT_TESTS=true -D DO_COVERAGE=$DO_COVERAGE -D TEST_MANUAL_SCREENS=$MANUAL_SCREENS -D CMAKE_C_FLAGS=-Werror"
|
|
||||||
- |
|
|
||||||
if [ "$EMPTY_THEME_WHILE_LOADING" = 1 ]; then
|
|
||||||
# Break beautiful so that trying to access the theme before beautiful.init() causes an error
|
|
||||||
sed -i -e 's/theme = {}/theme = setmetatable({}, { __index = function() error("May not access theme before beautiful.init()") end })/' lib/beautiful/init.lua \
|
|
||||||
&& grep -q 'May not access' lib/beautiful/init.lua
|
|
||||||
fi
|
|
||||||
- |
|
|
||||||
set -e
|
|
||||||
if [ -n "$BUILD_IN_DIR" ]; then
|
|
||||||
# Explicitly remove the Makefile to not build from the src dir accidentally.
|
|
||||||
rm Makefile
|
|
||||||
SOURCE_DIRECTORY="$PWD"
|
|
||||||
mkdir "$BUILD_IN_DIR"
|
|
||||||
cd "$BUILD_IN_DIR"
|
|
||||||
travis_run_in_fold "build_in_dir" cmake $CMAKE_ARGS "$SOURCE_DIRECTORY"
|
|
||||||
fi
|
|
||||||
- travis_run_in_fold "make" make ; sleep 5
|
|
||||||
- make ; sleep 5
|
|
||||||
- make ; sleep 5
|
|
||||||
- make ; sleep 5
|
|
||||||
- |
|
|
||||||
if [ "$TRAVIS_TEST_RESULT" = 0 ]; then
|
|
||||||
travis_run_in_fold "make.install" sudo env PATH=$PATH make install
|
|
||||||
awesome --version
|
|
||||||
fi
|
|
||||||
# Run checks.
|
|
||||||
- |
|
|
||||||
if [ "$TRAVIS_TEST_RESULT" = 0 ]; then
|
|
||||||
set -ex
|
|
||||||
if [ -n "$DO_COVERAGE" ]; then
|
|
||||||
travis_fold_start "DO_COVERAGE"
|
|
||||||
|
|
||||||
# Run tests/examples explicitly.
|
|
||||||
make check-examples || exit 1
|
|
||||||
do_codecov samples
|
|
||||||
|
|
||||||
make check-unit || exit 1
|
|
||||||
do_codecov unittests
|
|
||||||
make check-integration || exit 1
|
|
||||||
do_codecov functionaltests
|
|
||||||
make check-themes || exit 1
|
|
||||||
do_codecov themes
|
|
||||||
do_codecov_gcov c_code
|
|
||||||
|
|
||||||
travis_fold_end
|
|
||||||
else
|
|
||||||
# TODO: does not run check-examples. Should it?
|
|
||||||
travis_run_in_fold "make.check-unit" make check-unit
|
|
||||||
travis_run_in_fold "make.check-integration" make check-integration
|
|
||||||
travis_run_in_fold "make.check-themes" make check-themes
|
|
||||||
fi
|
|
||||||
set +x
|
|
||||||
fi
|
|
||||||
# Run check-qa.
|
|
||||||
- |
|
|
||||||
if [ "$DO_CHECKQA" = 1 ]; then
|
|
||||||
travis_run_in_fold "make.check-qa" make check-qa
|
|
||||||
fi
|
|
||||||
- |
|
|
||||||
if [ "$TEST_PREV_COMMITS" = 1 ] && ! [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
|
||||||
set -e
|
|
||||||
# Check each commit separately (to make git-bisect less annoying).
|
|
||||||
# Fix Travis' commit range (https://github.com/travis-ci/travis-ci/issues/4596).
|
|
||||||
commit_range="${TRAVIS_COMMIT_RANGE/.../..}"
|
|
||||||
echo "Testing previous commits ($commit_range)"
|
|
||||||
rev_list="$(git rev-list --bisect-all $commit_range)"
|
|
||||||
echo "rev-list: $rev_list"
|
|
||||||
commits="$(echo "$rev_list" | grep -v 'dist=0' | cut -d\ -f 1)"
|
|
||||||
n="$(echo "$commits" | wc -l)"
|
|
||||||
echo "Testing $n commits: $commits"
|
|
||||||
i=0
|
|
||||||
failed=
|
|
||||||
for commit in $commits; do
|
|
||||||
i=$((i+1))
|
|
||||||
travis_fold_start "test_commit_${commit}_.$i.$n"
|
|
||||||
echo "Testing commit $commit"
|
|
||||||
git reset --hard # Some files are updated when compiling...
|
|
||||||
git checkout "$commit"
|
|
||||||
git --no-pager show --stat
|
|
||||||
|
|
||||||
if ! make all check CMAKE_ARGS+="-D DO_COVERAGE=0"; then
|
|
||||||
failed="$failed $commit"
|
|
||||||
fi
|
|
||||||
travis_fold_end
|
|
||||||
done
|
|
||||||
|
|
||||||
git checkout -qf FETCH_HEAD
|
|
||||||
if [ -n "$failed" ]; then
|
|
||||||
echo "Checks failed for these commits:"
|
|
||||||
for c in $failed; do
|
|
||||||
git log -1 --pretty="%h %s (%an, %ad)" "$c"
|
|
||||||
done
|
|
||||||
false
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
# Push updated API docs for relevant branches, e.g. non-PRs builds on master.
|
|
||||||
- if [ "$BUILD_APIDOC" = "true" ]; then build-utils/travis-apidoc.sh; fi
|
|
||||||
# Push code coverage information
|
|
||||||
- |
|
|
||||||
set -e
|
|
||||||
if [ "$DO_COVERAGE" = "coveralls" ]; then
|
|
||||||
test -f build/luacov.stats.out || { echo 'build/luacov.stats.out does not exist.'; return 1; }
|
|
||||||
luacov-coveralls --verbose --merge
|
|
||||||
fi
|
|
|
@ -619,13 +619,14 @@ main(int argc, char **argv)
|
||||||
bool success = true;
|
bool success = true;
|
||||||
/* Get the first config that will be tried */
|
/* Get the first config that will be tried */
|
||||||
const char *config = luaA_find_config(&xdg, confpath, true_config_callback);
|
const char *config = luaA_find_config(&xdg, confpath, true_config_callback);
|
||||||
|
fprintf(stdout, "Checking config '%s'... ", config);
|
||||||
|
|
||||||
/* Try to parse it */
|
/* Try to parse it */
|
||||||
lua_State *L = luaL_newstate();
|
lua_State *L = luaL_newstate();
|
||||||
if(luaL_loadfile(L, config))
|
if(luaL_loadfile(L, config))
|
||||||
{
|
{
|
||||||
const char *err = lua_tostring(L, -1);
|
const char *err = lua_tostring(L, -1);
|
||||||
fprintf(stderr, "%s\n", err);
|
fprintf(stdout, "\nERROR: %s\n", err);
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
p_delete(&config);
|
p_delete(&config);
|
||||||
|
@ -633,12 +634,11 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
if(!success)
|
if(!success)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "✘ Configuration file syntax error.\n");
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr, "✔ Configuration file syntax OK.\n");
|
fprintf(stdout, "OK\n");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,14 @@ if (NOT LUA_FOUND)
|
||||||
"You might want to hint it using the LUA_DIR environment variable, "
|
"You might want to hint it using the LUA_DIR environment variable, "
|
||||||
"or set the LUA_INCLUDE_DIR / LUA_LIBRARY CMake variables.")
|
"or set the LUA_INCLUDE_DIR / LUA_LIBRARY CMake variables.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(LUA_FULL_VERSION "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}")
|
||||||
|
# 5.1 <= LUA_VERSION < 5.4
|
||||||
|
if(NOT ((LUA_FULL_VERSION VERSION_EQUAL 5.1.0 OR LUA_FULL_VERSION VERSION_GREATER 5.1.0) AND LUA_FULL_VERSION VERSION_LESS 5.4.0))
|
||||||
|
message(FATAL_ERROR "Awesome only supports Lua versions 5.1-5.3, please refer to"
|
||||||
|
"https://awesomewm.org/apidoc/documentation/10-building-and-testing.md.html#Building")
|
||||||
|
endif()
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# {{{ Check if documentation can be build
|
# {{{ Check if documentation can be build
|
||||||
|
|
|
@ -77,7 +77,7 @@ mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
|
||||||
menubar.utils.terminal = terminal -- Set the terminal for applications that require it
|
menubar.utils.terminal = terminal -- Set the terminal for applications that require it
|
||||||
-- }}}
|
-- }}}
|
||||||
|
|
||||||
-- {{{ Tag
|
-- {{{ Tag layout
|
||||||
-- @DOC_LAYOUT@
|
-- @DOC_LAYOUT@
|
||||||
-- Table of layouts to cover with awful.layout.inc, order matters.
|
-- Table of layouts to cover with awful.layout.inc, order matters.
|
||||||
tag.connect_signal("request::default_layouts", function()
|
tag.connect_signal("request::default_layouts", function()
|
||||||
|
@ -99,6 +99,27 @@ tag.connect_signal("request::default_layouts", function()
|
||||||
end)
|
end)
|
||||||
-- }}}
|
-- }}}
|
||||||
|
|
||||||
|
-- {{{ Wallpaper
|
||||||
|
-- @DOC_WALLPAPER@
|
||||||
|
screen.connect_signal("request::wallpaper", function(s)
|
||||||
|
awful.wallpaper {
|
||||||
|
screen = s,
|
||||||
|
widget = {
|
||||||
|
{
|
||||||
|
image = beautiful.wallpaper,
|
||||||
|
upscale = true,
|
||||||
|
downscale = true,
|
||||||
|
widget = wibox.widget.imagebox,
|
||||||
|
},
|
||||||
|
valign = "center",
|
||||||
|
halign = "center",
|
||||||
|
tiled = false,
|
||||||
|
widget = wibox.container.tile,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
-- }}}
|
||||||
|
|
||||||
-- {{{ Wibar
|
-- {{{ Wibar
|
||||||
|
|
||||||
-- Keyboard map indicator and switcher
|
-- Keyboard map indicator and switcher
|
||||||
|
@ -107,19 +128,6 @@ mykeyboardlayout = awful.widget.keyboardlayout()
|
||||||
-- Create a textclock widget
|
-- Create a textclock widget
|
||||||
mytextclock = wibox.widget.textclock()
|
mytextclock = wibox.widget.textclock()
|
||||||
|
|
||||||
-- @DOC_WALLPAPER@
|
|
||||||
screen.connect_signal("request::wallpaper", function(s)
|
|
||||||
-- Wallpaper
|
|
||||||
if beautiful.wallpaper then
|
|
||||||
local wallpaper = beautiful.wallpaper
|
|
||||||
-- If wallpaper is a function, call it with the screen
|
|
||||||
if type(wallpaper) == "function" then
|
|
||||||
wallpaper = wallpaper(s)
|
|
||||||
end
|
|
||||||
gears.wallpaper.maximized(wallpaper, s, true)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- @DOC_FOR_EACH_SCREEN@
|
-- @DOC_FOR_EACH_SCREEN@
|
||||||
screen.connect_signal("request::desktop_decoration", function(s)
|
screen.connect_signal("request::desktop_decoration", function(s)
|
||||||
-- Each screen has its own tag table.
|
-- Each screen has its own tag table.
|
||||||
|
@ -179,26 +187,27 @@ screen.connect_signal("request::desktop_decoration", function(s)
|
||||||
|
|
||||||
-- @DOC_WIBAR@
|
-- @DOC_WIBAR@
|
||||||
-- Create the wibox
|
-- Create the wibox
|
||||||
s.mywibox = awful.wibar({ position = "top", screen = s })
|
s.mywibox = awful.wibar {
|
||||||
|
position = "top",
|
||||||
-- @DOC_SETUP_WIDGETS@
|
screen = s,
|
||||||
-- Add widgets to the wibox
|
-- @DOC_SETUP_WIDGETS@
|
||||||
s.mywibox.widget = {
|
widget = {
|
||||||
layout = wibox.layout.align.horizontal,
|
layout = wibox.layout.align.horizontal,
|
||||||
{ -- Left widgets
|
{ -- Left widgets
|
||||||
layout = wibox.layout.fixed.horizontal,
|
layout = wibox.layout.fixed.horizontal,
|
||||||
mylauncher,
|
mylauncher,
|
||||||
s.mytaglist,
|
s.mytaglist,
|
||||||
s.mypromptbox,
|
s.mypromptbox,
|
||||||
},
|
},
|
||||||
s.mytasklist, -- Middle widget
|
s.mytasklist, -- Middle widget
|
||||||
{ -- Right widgets
|
{ -- Right widgets
|
||||||
layout = wibox.layout.fixed.horizontal,
|
layout = wibox.layout.fixed.horizontal,
|
||||||
mykeyboardlayout,
|
mykeyboardlayout,
|
||||||
wibox.widget.systray(),
|
wibox.widget.systray(),
|
||||||
mytextclock,
|
mytextclock,
|
||||||
s.mylayoutbox,
|
s.mylayoutbox,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
-- }}}
|
-- }}}
|
||||||
|
|
|
@ -63,7 +63,6 @@ local allowed_deps = {
|
||||||
},
|
},
|
||||||
-- TODO: Get rid of these
|
-- TODO: Get rid of these
|
||||||
["gears.surface"] = { ["wibox.hierarchy"] = true },
|
["gears.surface"] = { ["wibox.hierarchy"] = true },
|
||||||
["gears.connection"] = { ["*"] = true },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Turn "foo.bar.baz" into "foo.bar". Returns nil if there is nothing more to
|
-- Turn "foo.bar.baz" into "foo.bar". Returns nil if there is nothing more to
|
||||||
|
|
|
@ -124,6 +124,10 @@ to an object such as the mouse.
|
||||||
The `naughty.layout.box` allows to provide custom widgets to use within the
|
The `naughty.layout.box` allows to provide custom widgets to use within the
|
||||||
notifications.
|
notifications.
|
||||||
|
|
||||||
|
The `awful.wallpaper` provides a non-intereactive "backgroud" for one or more
|
||||||
|
`screen`. While it uses normal widget, it will not automatically be repainted
|
||||||
|
if they change. It will also not provide any mouse events.
|
||||||
|
|
||||||
Finally, the `awful.titlebar`, while not technically a real `wibox`, acts
|
Finally, the `awful.titlebar`, while not technically a real `wibox`, acts
|
||||||
exactly the same way and allows to attach widgets on each side of clients.
|
exactly the same way and allows to attach widgets on each side of clients.
|
||||||
|
|
||||||
|
@ -373,6 +377,50 @@ Code:
|
||||||
layout = wibox.layout.fixed.horizontal,
|
layout = wibox.layout.fixed.horizontal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Accessing widgets
|
||||||
|
|
||||||
|
For each widget or container, it is possible to add an `identifier` attribute
|
||||||
|
so that it can be accessed later.
|
||||||
|
|
||||||
|
Widgets defined using `setup` can be accessed using these methods:
|
||||||
|
|
||||||
|
* Avoiding the issue by using externally created widgets
|
||||||
|
* Using `my_wibox.my_first_widget.my_second_widget` style access
|
||||||
|
* Using JavaScript like `my_wibox:get_children_by_id("my_second_widget")[1]`
|
||||||
|
|
||||||
|
The first method mixes the imperative and declarative syntax, and makes the code
|
||||||
|
less readable. The second is a little verbose and only works if every node in
|
||||||
|
the chain has a valid identifier. The last one doesn't require long paths,
|
||||||
|
but it is not easy to get a specific instance if multiple widgets have the
|
||||||
|
same identifier.
|
||||||
|
|
||||||
|
WARNING: The widget identifier must not use a reserved name. This includes all
|
||||||
|
method names, existing widget attributes, `layout` and `widget`. Names should
|
||||||
|
also respect the Lua variable conventions (case-sensitive, alphanumeric,
|
||||||
|
underscore characters and non-numeric first character).
|
||||||
|
|
||||||
|
Code:
|
||||||
|
|
||||||
|
s.mywibox : setup {
|
||||||
|
{
|
||||||
|
id = "second",
|
||||||
|
widget = wibox.widget.textbox
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id = "third",
|
||||||
|
widget = wibox.widget.textbox
|
||||||
|
},
|
||||||
|
id = "first",
|
||||||
|
layout = wibox.layout.fixed.horizontal,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mywibox.first.second:set_markup("changed!")
|
||||||
|
s.mywibox:get_children_by_id("third")[1]:set_markup("Also changed!")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Extending the system
|
### Extending the system
|
||||||
|
|
||||||
This system is very flexible. Each section attribute (the entries with string
|
This system is very flexible. Each section attribute (the entries with string
|
||||||
|
@ -474,99 +522,3 @@ necessary for three reasons:
|
||||||
at a later time (by the parent widget).
|
at a later time (by the parent widget).
|
||||||
* The code is highly redundant and some of the logic is delegated to the parent
|
* The code is highly redundant and some of the logic is delegated to the parent
|
||||||
widget to simplify everything.
|
widget to simplify everything.
|
||||||
|
|
||||||
## Accessing and updating widgets
|
|
||||||
|
|
||||||
There is 3 main ways to update the widgets. Each is best suited for it's own niche.
|
|
||||||
Choose the one that better suites the style of your code.
|
|
||||||
|
|
||||||
### Imperative
|
|
||||||
|
|
||||||
For each widget or container, it is possible to add an `identifier` attribute
|
|
||||||
so that it can be accessed later.
|
|
||||||
|
|
||||||
Widgets defined using `setup` can be accessed using these methods:
|
|
||||||
|
|
||||||
* Avoiding the issue by using externally created widgets
|
|
||||||
* Using `my_wibox.my_first_widget.my_second_widget` style access
|
|
||||||
* Using JavaScript like `my_wibox:get_children_by_id("my_second_widget")[1]`
|
|
||||||
|
|
||||||
The first method mixes the imperative and declarative syntax, and makes the code
|
|
||||||
less readable. The second is a little verbose and only works if every node in
|
|
||||||
the chain has a valid identifier. The last one doesn't require long paths,
|
|
||||||
but it is not easy to get a specific instance if multiple widgets have the
|
|
||||||
same identifier.
|
|
||||||
|
|
||||||
WARNING: The widget identifier must not use a reserved name. This includes all
|
|
||||||
method names, existing widget attributes, `layout` and `widget`. Names should
|
|
||||||
also respect the Lua variable conventions (case-sensitive, alphanumeric,
|
|
||||||
underscore characters and non-numeric first character).
|
|
||||||
|
|
||||||
Code:
|
|
||||||
|
|
||||||
s.mywibox : setup {
|
|
||||||
{
|
|
||||||
id = "second",
|
|
||||||
widget = wibox.widget.textbox
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id = "third",
|
|
||||||
widget = wibox.widget.textbox
|
|
||||||
},
|
|
||||||
id = "first",
|
|
||||||
layout = wibox.layout.fixed.horizontal,
|
|
||||||
}
|
|
||||||
|
|
||||||
s.mywibox.first.second:set_markup("changed!")
|
|
||||||
s.mywibox:get_children_by_id("third")[1]:set_markup("Also changed!")
|
|
||||||
|
|
||||||
### Reactive
|
|
||||||
|
|
||||||
![Reactive update](../images/gears_reactive.svg)
|
|
||||||
|
|
||||||
Think of reactive programming like Excel/Spreadsheets. You define rules or even
|
|
||||||
business logic that will automatically be re-evaluated every time the data it
|
|
||||||
uses changes. In the background, this rewrites the function and automatically
|
|
||||||
creates all the signal connections. Lua is not a reactive language, so it has it's
|
|
||||||
limits and you should read the rules defined in the `gears.reactive` documentation
|
|
||||||
carefully. However, when it works, it is by far the simplest way to update a
|
|
||||||
widget defined using the declarative syntax.
|
|
||||||
|
|
||||||
#### Reactive expressions
|
|
||||||
|
|
||||||
A reactive expression is just a function wrapped by a `gears.reactive` object. This
|
|
||||||
will introspect the content and write all the boilerplate. Note that this *only* works
|
|
||||||
in declarative trees. It *cannot* be mixed with the imperative API.
|
|
||||||
|
|
||||||
@DOC_wibox_decl_doc_reactive_expr1_EXAMPLE@
|
|
||||||
|
|
||||||
Unlike QML, AwesomeWM also support extracting reactive blocks out of the tree:
|
|
||||||
|
|
||||||
@DOC_wibox_decl_doc_reactive_expr2_EXAMPLE@
|
|
||||||
|
|
||||||
#### Watcher objects
|
|
||||||
|
|
||||||
`gears.watcher` objects poll files or command at an interval. They do so using
|
|
||||||
background threads, so it wont affect performance much. They can be attached
|
|
||||||
directly to a widget or used with a `gears.connection` as demonstrated below.
|
|
||||||
Using `gears.connection` is preferred when the value is needed by multiple
|
|
||||||
widgets (see the CPU graph example later in this document).
|
|
||||||
|
|
||||||
@DOC_wibox_widget_progressbar_watcher_EXAMPLE@
|
|
||||||
|
|
||||||
### Declarative
|
|
||||||
|
|
||||||
The other way to interact with widgets is by creating `gears.connection` objects.
|
|
||||||
They can be added in your declarative widget tree like this:
|
|
||||||
|
|
||||||
@DOC_wibox_decl_doc_connection_EXAMPLE@
|
|
||||||
|
|
||||||
It is also possible to use them to bind non-widget objects with widgets:
|
|
||||||
|
|
||||||
@DOC_wibox_decl_doc_connection3_EXAMPLE@
|
|
||||||
|
|
||||||
One useful feature is that when the `gears.connection` has a callback, it has
|
|
||||||
direct access to all `id`s as variables or using `get_children_by_id`:
|
|
||||||
|
|
||||||
@DOC_wibox_decl_doc_connection5_EXAMPLE@
|
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,17 @@ variables such as `bg_normal`. To get a list of all official variables, see
|
||||||
the [appearance guide](../documentation/06-appearance.md.html).
|
the [appearance guide](../documentation/06-appearance.md.html).
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
sections.DOC_WALLPAPER = [[
|
||||||
|
The AwesomeWM wallpaper module, `awful.wallpaper` support both per-screen wallpaper
|
||||||
|
and wallpaper across multiple screens. In the default configuration, the `"request::wallpaper"` signal
|
||||||
|
is emitted everytime a screen is added, moved, resized or when the bars
|
||||||
|
(`awful.wibar`) are moved.
|
||||||
|
|
||||||
|
This is will suited for single-screen wallpapers. If you wish to use multi-screen wallpaper,
|
||||||
|
it is better to create a global wallpaper object and edit it when the screen change. See
|
||||||
|
the `add_screen`/`remove_screens` methods and the `screens` property of `awful.wallpaper` for
|
||||||
|
examples.
|
||||||
|
]]
|
||||||
|
|
||||||
sections.DOC_DEFAULT_APPLICATIONS = [[
|
sections.DOC_DEFAULT_APPLICATIONS = [[
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,10 @@ have not opened any programs. On the top right you see the time/date and a
|
||||||
symbol showing the current layout. You can also click on the symbol to change
|
symbol showing the current layout. You can also click on the symbol to change
|
||||||
the active layout.
|
the active layout.
|
||||||
|
|
||||||
One of the big advantages of Awesome over other tiling window managers is its good
|
One of the big advantages of Awesome over other tiling window managers is its
|
||||||
mouse support. Awesome can act as a full floating window manager (almost like
|
good mouse support. Awesome can act as a full floating window manager (almost
|
||||||
openbox) if you want. For this basic tutorial we will mainly focus on keyboard
|
like openbox) if you want. For this basic tutorial we will mainly focus on
|
||||||
control, so let's learn some key bindings now.
|
keyboard control, so let's learn some key bindings now.
|
||||||
|
|
||||||
Let's open a terminal: press *Mod4+Enter*. Mod4 is your "Windows key", the key
|
Let's open a terminal: press *Mod4+Enter*. Mod4 is your "Windows key", the key
|
||||||
between Ctrl and Alt. You can change the modkey if you want, but we'll get to
|
between Ctrl and Alt. You can change the modkey if you want, but we'll get to
|
||||||
|
@ -65,20 +65,23 @@ overview now also provides a cheat sheet for controlling Vim.
|
||||||
|
|
||||||
## Change the theme
|
## Change the theme
|
||||||
|
|
||||||
Awesome has four themes you can choose from: *default*, *sky*, *xresources*, and
|
Awesome has multiple builtin themes you can choose from:
|
||||||
*zenburn*.
|
|
||||||
|
|
||||||
To change the theme, open your rc.lua and edit this line near the beginning of
|
* *default*
|
||||||
the file:
|
* *gtk*
|
||||||
|
* *sky*
|
||||||
|
* *xresources*
|
||||||
|
* *zenburn*
|
||||||
|
|
||||||
|
To change the theme, open your `rc.lua`, find this line near the beginning of
|
||||||
|
the file, and change `default` to one of the other values mentioned:
|
||||||
|
|
||||||
beautiful.init(gears.filesystem.get_themes_dir() .. "default/theme.lua")
|
beautiful.init(gears.filesystem.get_themes_dir() .. "default/theme.lua")
|
||||||
|
|
||||||
For this tutorial we will stick with the default theme.
|
However, for this tutorial we will copy and customize the default theme.
|
||||||
|
Copy `/usr/share/awesome/themes/default/theme.lua` to `~/.config/awesome/`
|
||||||
Now we will customize the theme. Copy
|
and change the line shown above in `rc.lua` like this. Make sure to replace
|
||||||
`/usr/share/awesome/themes/default/theme.lua` to `~/.config/awesome/` and change
|
`USER` with your user name.
|
||||||
the above line in your theme like this (remember to replace `USER` with your
|
|
||||||
user name):
|
|
||||||
|
|
||||||
beautiful.init("/home/USER/.config/awesome/theme.lua")
|
beautiful.init("/home/USER/.config/awesome/theme.lua")
|
||||||
|
|
||||||
|
@ -89,7 +92,9 @@ this line in your theme file:
|
||||||
|
|
||||||
theme.wallpaper = themes_path.."default/background.png"
|
theme.wallpaper = themes_path.."default/background.png"
|
||||||
|
|
||||||
The default uses a path relative to `themes_path` by using the `..` operator to join two strings together. To just set it to an absolute path for example, you could do:
|
The default uses a path relative to `themes_path` by using the `..` operator to
|
||||||
|
join two strings together. To just set it to an absolute path for example,
|
||||||
|
you could do:
|
||||||
|
|
||||||
theme.wallpaper = "/usr/share/backgrounds/my-awesome-wallpaper.png"
|
theme.wallpaper = "/usr/share/backgrounds/my-awesome-wallpaper.png"
|
||||||
|
|
||||||
|
|
|
@ -36,8 +36,7 @@ Do **not** use such functions and prefer `awful.spawn.easy_async`,
|
||||||
fast enough and won't impact the main event loop iteration time, you are wrong.
|
fast enough and won't impact the main event loop iteration time, you are wrong.
|
||||||
*Every* calls to `io.open` are impacted by the system `iowait` queue and can
|
*Every* calls to `io.open` are impacted by the system `iowait` queue and can
|
||||||
spend hundreds of milliseconds blocked *before* being executed. Note that
|
spend hundreds of milliseconds blocked *before* being executed. Note that
|
||||||
some common widget or probe libraries such as
|
some common widget or probe libraries do not follow this
|
||||||
[Vicious](https://github.com/Mic92/vicious) do not follow this
|
|
||||||
advice currently and are known to cause input lag on some systems (but not all).
|
advice currently and are known to cause input lag on some systems (but not all).
|
||||||
|
|
||||||
In both case, a warning like:
|
In both case, a warning like:
|
||||||
|
@ -53,6 +52,19 @@ provide a theme daemon. For more information about how to manage the
|
||||||
look and feel of applications, refer to the
|
look and feel of applications, refer to the
|
||||||
[Arch Linux Wiki](https://wiki.archlinux.org/index.php/Category:Eye_candy).
|
[Arch Linux Wiki](https://wiki.archlinux.org/index.php/Category:Eye_candy).
|
||||||
|
|
||||||
|
### Awesome doesn't show up on my login screen
|
||||||
|
|
||||||
|
There have been cases where Awesome wasn't correctly registered with the display manager,
|
||||||
|
usually due to a missing `.desktop` file.
|
||||||
|
|
||||||
|
To fix such issues, copy `awesome.desktop` from the root of [the repository](https://github.com/awesomeWM/awesome/) to `/usr/share/xsessions/`.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl https://raw.githubusercontent.com/awesomeWM/awesome/master/awesome.desktop | sudo tee /usr/share/xsessions/awesome.desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
If you installed Awesome through a package manager, you might want to check if the package includes that file and, if not, notify the maintainer to add it as appropriate.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### How to change the default window management layout?
|
### How to change the default window management layout?
|
||||||
|
|
|
@ -121,6 +121,8 @@ local function parse_files(paths, property_name, matcher, name_matcher)
|
||||||
local exp2 = matcher or "[-*]*[ ]*".. property_name ..".(.+)"
|
local exp2 = matcher or "[-*]*[ ]*".. property_name ..".(.+)"
|
||||||
local exp3 = name_matcher or "[. ](.+)"
|
local exp3 = name_matcher or "[. ](.+)"
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
local names = {} -- Used to check for duplicates
|
||||||
local ret = {}
|
local ret = {}
|
||||||
|
|
||||||
table.sort(paths)
|
table.sort(paths)
|
||||||
|
@ -162,13 +164,47 @@ local function parse_files(paths, property_name, matcher, name_matcher)
|
||||||
"seems to be misformatted. Use `beautiful.namespace_name`"
|
"seems to be misformatted. Use `beautiful.namespace_name`"
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
table.insert(ret, {
|
local insert_name = name:gsub("_", "_")
|
||||||
file = file,
|
local link = get_link(file, var, var:match(exp3):gsub("_", "\\_"))
|
||||||
name = name:gsub("_", "_"),
|
local desc = buffer:gmatch("[-*/ \n]+([^\n.]*)")() or ""
|
||||||
link = get_link(file, var, var:match(exp3):gsub("_", "\\_")),
|
local mod = path_to_module(file)
|
||||||
desc = buffer:gmatch("[-*/ \n]+([^\n.]*)")() or "",
|
|
||||||
mod = path_to_module(file),
|
if names[insert_name] == nil then
|
||||||
})
|
count = count + 1
|
||||||
|
table.insert(ret, count, {
|
||||||
|
file = file,
|
||||||
|
name = insert_name,
|
||||||
|
link = link,
|
||||||
|
desc = desc,
|
||||||
|
mod = mod
|
||||||
|
})
|
||||||
|
names[insert_name] = count
|
||||||
|
else
|
||||||
|
link = link .. "(" .. mod .. ")"
|
||||||
|
if type(ret[names[insert_name]].link) ~= "table" then
|
||||||
|
ret[names[insert_name]].file = {
|
||||||
|
ret[names[insert_name]].file,
|
||||||
|
file
|
||||||
|
}
|
||||||
|
ret[names[insert_name]].link = {
|
||||||
|
ret[names[insert_name]].link .. " (" .. ret[names[insert_name]].mod .. ")",
|
||||||
|
link
|
||||||
|
}
|
||||||
|
ret[names[insert_name]].desc = {
|
||||||
|
ret[names[insert_name]].desc,
|
||||||
|
desc
|
||||||
|
}
|
||||||
|
ret[names[insert_name]].mod = {
|
||||||
|
ret[names[insert_name]].mod,
|
||||||
|
mod
|
||||||
|
}
|
||||||
|
else
|
||||||
|
table.insert(ret[names[insert_name]].file, file)
|
||||||
|
table.insert(ret[names[insert_name]].link, link)
|
||||||
|
table.insert(ret[names[insert_name]].desc, desc)
|
||||||
|
table.insert(ret[names[insert_name]].mod, mod)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
buffer = ""
|
buffer = ""
|
||||||
|
@ -190,7 +226,15 @@ local function create_table(entries, columns, prefix)
|
||||||
local line = " <tr>"
|
local line = " <tr>"
|
||||||
|
|
||||||
for _, column in ipairs(columns) do
|
for _, column in ipairs(columns) do
|
||||||
line = line.."<td>"..entry[column].."</td>"
|
if type(entry[column]) == "table" then
|
||||||
|
line = line .. "<td><ul>"
|
||||||
|
for _,v in pairs(entry[column]) do
|
||||||
|
line = line .. "<li>" .. v .. "</li>"
|
||||||
|
end
|
||||||
|
line = line .. "</ul></td>"
|
||||||
|
else
|
||||||
|
line = line.."<td>"..entry[column].."</td>"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(lines, prefix..line.."</tr>\n")
|
table.insert(lines, prefix..line.."</tr>\n")
|
||||||
|
|
|
@ -1,80 +1,80 @@
|
||||||
* <table class='widget_list' border=1>
|
<div class='flex-list'>
|
||||||
* <tr><td>num_glyphs </td></tr>
|
* <div>num_glyphs</div>
|
||||||
* <tr><td>cursor </td></tr>
|
* <div>arrow</div>
|
||||||
* <tr><td>arrow </td></tr>
|
* <div>based_arrow_down</div>
|
||||||
* <tr><td>based_arrow_down </td></tr>
|
* <div>based_arrow_up</div>
|
||||||
* <tr><td>based_arrow_up </td></tr>
|
* <div>boat</div>
|
||||||
* <tr><td>boat </td></tr>
|
* <div>bogosity</div>
|
||||||
* <tr><td>bogosity </td></tr>
|
* <div>bottom_left_corner</div>
|
||||||
* <tr><td>bottom_left_corner </td></tr>
|
* <div>bottom_right_corner</div>
|
||||||
* <tr><td>bottom_right_corner </td></tr>
|
* <div>bottom_side</div>
|
||||||
* <tr><td>bottom_side </td></tr>
|
* <div>bottom_tee</div>
|
||||||
* <tr><td>bottom_tee </td></tr>
|
* <div>box_spiral</div>
|
||||||
* <tr><td>box_spiral </td></tr>
|
* <div>center_ptr</div>
|
||||||
* <tr><td>center_ptr </td></tr>
|
* <div>circle</div>
|
||||||
* <tr><td>circle </td></tr>
|
* <div>clock</div>
|
||||||
* <tr><td>clock </td></tr>
|
* <div>coffee_mug</div>
|
||||||
* <tr><td>coffee_mug </td></tr>
|
* <div>cross</div>
|
||||||
* <tr><td>cross </td></tr>
|
* <div>crosshair</div>
|
||||||
* <tr><td>cross_reverse </td></tr>
|
* <div>cross_reverse</div>
|
||||||
* <tr><td>crosshair </td></tr>
|
* <div>cursor</div>
|
||||||
* <tr><td>diamond_cross </td></tr>
|
* <div>diamond_cross</div>
|
||||||
* <tr><td>dot </td></tr>
|
* <div>dotbox</div>
|
||||||
* <tr><td>dotbox </td></tr>
|
* <div>dot</div>
|
||||||
* <tr><td>double_arrow </td></tr>
|
* <div>double_arrow</div>
|
||||||
* <tr><td>draft_large </td></tr>
|
* <div>draft_large</div>
|
||||||
* <tr><td>draft_small </td></tr>
|
* <div>draft_small</div>
|
||||||
* <tr><td>draped_box </td></tr>
|
* <div>draped_box</div>
|
||||||
* <tr><td>exchange </td></tr>
|
* <div>exchange</div>
|
||||||
* <tr><td>fleur </td></tr>
|
* <div>fleur</div>
|
||||||
* <tr><td>gobbler </td></tr>
|
* <div>gobbler</div>
|
||||||
* <tr><td>gumby </td></tr>
|
* <div>gumby</div>
|
||||||
* <tr><td>hand </td></tr>
|
* <div>hand</div>
|
||||||
* <tr><td>hand </td></tr>
|
* <div>hand</div>
|
||||||
* <tr><td>heart </td></tr>
|
* <div>heart</div>
|
||||||
* <tr><td>icon </td></tr>
|
* <div>icon</div>
|
||||||
* <tr><td>iron_cross </td></tr>
|
* <div>iron_cross</div>
|
||||||
* <tr><td>left_ptr </td></tr>
|
* <div>leftbutton</div>
|
||||||
* <tr><td>left_side </td></tr>
|
* <div>left_ptr</div>
|
||||||
* <tr><td>left_tee </td></tr>
|
* <div>left_side</div>
|
||||||
* <tr><td>leftbutton </td></tr>
|
* <div>left_tee</div>
|
||||||
* <tr><td>ll_angle </td></tr>
|
* <div>ll_angle</div>
|
||||||
* <tr><td>lr_angle </td></tr>
|
* <div>lr_angle</div>
|
||||||
* <tr><td>man </td></tr>
|
* <div>man</div>
|
||||||
* <tr><td>middlebutton </td></tr>
|
* <div>middlebutton</div>
|
||||||
* <tr><td>mouse </td></tr>
|
* <div>mouse</div>
|
||||||
* <tr><td>pencil </td></tr>
|
* <div>pencil</div>
|
||||||
* <tr><td>pirate </td></tr>
|
* <div>pirate</div>
|
||||||
* <tr><td>plus </td></tr>
|
* <div>plus</div>
|
||||||
* <tr><td>question_arrow </td></tr>
|
* <div>question_arrow</div>
|
||||||
* <tr><td>right_ptr </td></tr>
|
* <div>rightbutton</div>
|
||||||
* <tr><td>right_side </td></tr>
|
* <div>right_ptr</div>
|
||||||
* <tr><td>right_tee </td></tr>
|
* <div>right_side</div>
|
||||||
* <tr><td>rightbutton </td></tr>
|
* <div>right_tee</div>
|
||||||
* <tr><td>rtl_logo </td></tr>
|
* <div>rtl_logo</div>
|
||||||
* <tr><td>sailboat </td></tr>
|
* <div>sailboat</div>
|
||||||
* <tr><td>sb_down_arrow </td></tr>
|
* <div>sb_down_arrow</div>
|
||||||
* <tr><td>sb_h_double_arrow </td></tr>
|
* <div>sb_h_double_arrow</div>
|
||||||
* <tr><td>sb_left_arrow </td></tr>
|
* <div>sb_left_arrow</div>
|
||||||
* <tr><td>sb_right_arrow </td></tr>
|
* <div>sb_right_arrow</div>
|
||||||
* <tr><td>sb_up_arrow </td></tr>
|
* <div>sb_up_arrow</div>
|
||||||
* <tr><td>sb_v_double_arrow </td></tr>
|
* <div>sb_v_double_arrow</div>
|
||||||
* <tr><td>shuttle </td></tr>
|
* <div>shuttle</div>
|
||||||
* <tr><td>sizing </td></tr>
|
* <div>sizing</div>
|
||||||
* <tr><td>spider </td></tr>
|
* <div>spider</div>
|
||||||
* <tr><td>spraycan </td></tr>
|
* <div>spraycan</div>
|
||||||
* <tr><td>star </td></tr>
|
* <div>star</div>
|
||||||
* <tr><td>target </td></tr>
|
* <div>target</div>
|
||||||
* <tr><td>tcross </td></tr>
|
* <div>tcross</div>
|
||||||
* <tr><td>top_left_arrow </td></tr>
|
* <div>top_left_arrow</div>
|
||||||
* <tr><td>top_left_corner </td></tr>
|
* <div>top_left_corner</div>
|
||||||
* <tr><td>top_right_corner </td></tr>
|
* <div>top_right_corner</div>
|
||||||
* <tr><td>top_side </td></tr>
|
* <div>top_side</div>
|
||||||
* <tr><td>top_tee </td></tr>
|
* <div>top_tee</div>
|
||||||
* <tr><td>trek </td></tr>
|
* <div>trek</div>
|
||||||
* <tr><td>ul_angle </td></tr>
|
* <div>ul_angle</div>
|
||||||
* <tr><td>umbrella </td></tr>
|
* <div>umbrella</div>
|
||||||
* <tr><td>ur_angle </td></tr>
|
* <div>ur_angle</div>
|
||||||
* <tr><td>watch </td></tr>
|
* <div>watch</div>
|
||||||
* <tr><td>xterm </td></tr>
|
* <div>xterm</div>
|
||||||
* </table>
|
* </div>
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
--- Set a widget at a specific index, replace the current one.
|
--- Set a widget at a specific index, replacing the current one.
|
||||||
--
|
--
|
||||||
-- @tparam number index A widget or a widget index
|
-- @tparam number index A widget or a widget index
|
||||||
-- @tparam widget widget2 The widget to take the place of the first one
|
-- @tparam widget widget2 The widget to replace the previous one with
|
||||||
-- @treturn boolean If the operation is successful
|
-- @treturn boolean Returns `true` if the widget was replaced successfully,
|
||||||
|
-- `false` otherwise.
|
||||||
-- @method set
|
-- @method set
|
||||||
-- @emits widget::replaced
|
-- @emits widget::replaced
|
||||||
-- @emitstparam widget::replaced widget self The layout.
|
-- @emitstparam widget::replaced widget self The layout.
|
||||||
-- @emitstparam widget::replaced widget widget index The inserted widget.
|
-- @emitstparam widget::replaced widget widget The inserted widget.
|
||||||
-- @emitstparam widget::replaced widget previous The previous widget.
|
-- @emitstparam widget::replaced widget previous The previous widget.
|
||||||
-- @emitstparam widget::replaced number index The replaced index.
|
-- @emitstparam widget::replaced number index The replaced index.
|
||||||
-- @interface layout
|
-- @interface layout
|
||||||
|
|
||||||
--- Replace the first instance of `widget` in the layout with `widget2`.
|
--- Replace the first instance of `widget` in the layout with `widget2`.
|
||||||
--
|
--
|
||||||
-- **Signal:** widget::replaced The argument is the new widget and the old one
|
|
||||||
-- and the index.
|
|
||||||
-- @tparam widget widget The widget to replace
|
-- @tparam widget widget The widget to replace
|
||||||
-- @tparam widget widget2 The widget to replace `widget` with
|
-- @tparam widget widget2 The widget to replace `widget` with
|
||||||
-- @tparam[opt=false] boolean recursive Dig in all compatible layouts to find the widget.
|
-- @tparam[opt=false] boolean recursive Recurse into all compatible layouts to
|
||||||
-- @treturn boolean If the operation is successful
|
-- find the widget.
|
||||||
|
-- @treturn boolean Returns `true` if the widget was replaced successfully,
|
||||||
|
-- `false` otherwise.
|
||||||
-- @method replace_widget
|
-- @method replace_widget
|
||||||
-- @emits widget::replaced
|
-- @emits widget::replaced
|
||||||
-- @emitstparam widget::replaced widget self The layout.
|
-- @emitstparam widget::replaced widget self The layout.
|
||||||
|
@ -31,35 +32,44 @@
|
||||||
--
|
--
|
||||||
-- @tparam number index1 The first widget index
|
-- @tparam number index1 The first widget index
|
||||||
-- @tparam number index2 The second widget index
|
-- @tparam number index2 The second widget index
|
||||||
-- @treturn boolean If the operation is successful
|
-- @treturn boolean Returns `true` if the widget was replaced successfully,
|
||||||
|
-- `false` otherwise.
|
||||||
-- @method swap
|
-- @method swap
|
||||||
-- @emits widget::swapped
|
-- @emits widget::swapped
|
||||||
-- @emitstparam widget::swapped widget self The layout.
|
-- @emitstparam widget::swapped widget self The layout.
|
||||||
-- @emitstparam widget::swapped widget widget1 index The first widget.
|
-- @emitstparam widget::swapped widget widget1 The first widget.
|
||||||
-- @emitstparam widget::swapped widget widget2 index The second widget.
|
-- @emitstparam widget::swapped widget widget2 The second widget.
|
||||||
-- @emitstparam widget::swapped number index1 The first index.
|
-- @emitstparam widget::swapped number index1 The first index.
|
||||||
-- @emitstparam widget::swapped number index1 The second index.
|
-- @emitstparam widget::swapped number index1 The second index.
|
||||||
-- @interface layout
|
-- @interface layout
|
||||||
|
|
||||||
--- Swap 2 widgets in a layout.
|
--- Swap 2 widgets in a layout.
|
||||||
-- If widget1 is present multiple time, only the first instance is swapped
|
--
|
||||||
-- **Signal:** widget::swapped The arguments are both widgets and both (new) indexes.
|
-- If `widget1` is present multiple time, only the first instance is swapped.
|
||||||
-- if the layouts not the same, then only `widget::replaced` will be emitted.
|
--
|
||||||
|
-- Calls `set` internally, so the signal `widget::replaced` is emitted for both
|
||||||
|
-- widgets as well.
|
||||||
|
--
|
||||||
-- @tparam widget widget1 The first widget
|
-- @tparam widget widget1 The first widget
|
||||||
-- @tparam widget widget2 The second widget
|
-- @tparam widget widget2 The second widget
|
||||||
-- @tparam[opt=false] boolean recursive Dig in all compatible layouts to find the widget.
|
-- @tparam[opt=false] boolean recursive Recurse into all compatible layouts to
|
||||||
-- @treturn boolean If the operation is successful
|
-- find the widget.
|
||||||
|
-- @treturn boolean Returns `true` if the widget was replaced successfully,
|
||||||
|
-- `false` otherwise.
|
||||||
-- @method swap_widgets
|
-- @method swap_widgets
|
||||||
-- @emits widget::swapped
|
-- @emits widget::swapped
|
||||||
-- @emitstparam widget::swapped widget self The layout.
|
-- @emitstparam widget::swapped widget self The layout.
|
||||||
-- @emitstparam widget::swapped widget widget1 index The first widget.
|
-- @emitstparam widget::swapped widget widget1 The first widget.
|
||||||
-- @emitstparam widget::swapped widget widget2 index The second widget.
|
-- @emitstparam widget::swapped widget widget2 The second widget.
|
||||||
-- @emitstparam widget::swapped number index1 The first index.
|
-- @emitstparam widget::swapped number index1 The first index.
|
||||||
-- @emitstparam widget::swapped number index1 The second index.
|
-- @emitstparam widget::swapped number index1 The second index.
|
||||||
-- @interface layout
|
-- @interface layout
|
||||||
|
-- @see set
|
||||||
|
|
||||||
--- Reset a ratio layout. This removes all widgets from the layout.
|
--- Reset the layout. This removes all widgets from the layout.
|
||||||
-- @method reset
|
-- @method reset
|
||||||
-- @emits widget::reset
|
-- @emits widget::reset
|
||||||
-- @emitstparam widget::reset widget self The layout.
|
-- @emitstparam widget::reset widget self The layout.
|
||||||
-- @interface layout
|
-- @interface layout
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
--<table class='widget_list' border=1>
|
||||||
|
-- <tr>
|
||||||
|
-- <th align='center'>Format</th>
|
||||||
|
-- <th align='center'>Description</th>
|
||||||
|
-- </tr>
|
||||||
|
-- <tr><td>%a</td><td>The abbreviated weekday name according to the current locale</td></tr>
|
||||||
|
-- <tr><td>%A</td><td>the full weekday name according to the current locale</td></tr>
|
||||||
|
-- <tr><td>%b</td><td>The abbreviated month name according to the current locale</td></tr>
|
||||||
|
-- <tr><td>%B</td><td>The full month name according to the current locale</td></tr>
|
||||||
|
-- <tr><td>%d</td><td>The day of the month as a decimal number (range 01 to 31)</td></tr>
|
||||||
|
-- <tr><td>%e</td><td>The day of the month as a decimal number (range 1 to 31)</td></tr>
|
||||||
|
-- <tr><td>%F</td><td>Equivalent to %Y-%m-%d (the ISO 8601 date format)</td></tr>
|
||||||
|
-- <tr><td>%H</td><td>The hour as a decimal number using a 24-hour clock (range 00 to 23)</td></tr>
|
||||||
|
-- <tr><td>%I</td><td>The hour as a decimal number using a 12-hour clock (range 01 to 12)</td></tr>
|
||||||
|
-- <tr><td>%k</td><td>The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank</td></tr>
|
||||||
|
-- <tr><td>%l</td><td>The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank</td></tr>
|
||||||
|
-- <tr><td>%m</td><td>The month as a decimal number (range 01 to 12)</td></tr>
|
||||||
|
-- <tr><td>%M</td><td>The minute as a decimal number (range 00 to 59)</td></tr>
|
||||||
|
-- <tr><td>%p</td><td>Either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale. Noon is treated as "PM" and midnight as "AM".</td></tr>
|
||||||
|
-- <tr><td>%P</td><td>Like %p but lowercase: "am" or "pm" or a corresponding string for the current locale</td></tr>
|
||||||
|
-- <tr><td>%r</td><td>The time in a.m. or p.m. notation</td></tr>
|
||||||
|
-- <tr><td>%R</td><td>The time in 24-hour notation (%H:%M)</td></tr>
|
||||||
|
-- <tr><td>%S</td><td>The second as a decimal number (range 00 to 60)</td></tr>
|
||||||
|
-- <tr><td>%T</td><td>The time in 24-hour notation with seconds (%H:%M:%S)</td></tr>
|
||||||
|
-- <tr><td>%y</td><td>The year as a decimal number without the century</td></tr>
|
||||||
|
-- <tr><td>%Y</td><td>The year as a decimal number including the century</td></tr>
|
||||||
|
-- <tr><td>%%</td><td>A literal % character</td></tr>
|
||||||
|
-- </table>
|
|
@ -37,16 +37,16 @@ should be useful for you.
|
||||||
|
|
||||||
<div class="index_guides">
|
<div class="index_guides">
|
||||||
<div>
|
<div>
|
||||||
<a href="../doc/documentation/07-my-first-awesome.md.html">Getting started</a>
|
@{07-my-first-awesome.md|Getting started}
|
||||||
<a href="../doc/documentation/90-FAQ.md.html">FAQ</a>
|
@{90-FAQ.md|FAQ}
|
||||||
<a href="../doc/documentation/01-readme.md.html">Read me</a>
|
@{01-readme.md|Read Me}
|
||||||
<a href="../doc/documentation/89-NEWS.md.html">NEWS</a>
|
@{89-NEWS.md|NEWS}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="../doc/documentation/03-declarative-layout.md.html">The widget system</a>
|
@{03-declarative-layout.md|The widget system}
|
||||||
<a href="../doc/documentation/09-options.md.html">Startup options</a>
|
@{09-options.md|Startup options}
|
||||||
<a href="../doc/documentation/05-awesomerc.md.html">The default rc.lua</a>
|
@{05-awesomerc.md|The default rc.lua}
|
||||||
<a href="../doc/documentation/08-client-layout-system.md.html">Window management</a>
|
@{08-client-layout-system.md|Window management}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -456,6 +456,25 @@ add_custom_tag {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Define the supermodule class.
|
||||||
|
-- This tag should be used at the module level. All properties from the
|
||||||
|
-- supermodule will be recursively added to the module by our ldoc template.
|
||||||
|
-- @supermodule supermodule
|
||||||
|
add_custom_tag {
|
||||||
|
name = "supermodule",
|
||||||
|
hidden = true,
|
||||||
|
auto_subtags = false
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Mark the item ad hidden.
|
||||||
|
-- This tag should be used to hide items from the documentation.
|
||||||
|
-- @hidden
|
||||||
|
add_custom_tag {
|
||||||
|
name = "hidden",
|
||||||
|
hidden = true,
|
||||||
|
auto_subtags = false
|
||||||
|
}
|
||||||
|
|
||||||
-- More fitting section names
|
-- More fitting section names
|
||||||
kind_names={topic='Documentation', module='Libraries', script='Sample files'}
|
kind_names={topic='Documentation', module='Libraries', script='Sample files'}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 124 KiB |
|
@ -257,6 +257,23 @@ table th, table td {
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-list > div {
|
||||||
|
flex-grow: 1;
|
||||||
|
/* Use base width based on font size
|
||||||
|
to make sure text fits when zooming */
|
||||||
|
width: 9em;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
border: 1px solid rgb(193, 204, 228);
|
||||||
|
}
|
||||||
|
|
||||||
#about {
|
#about {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
padding-left: 16em;
|
padding-left: 16em;
|
||||||
|
@ -503,3 +520,32 @@ pre .url { color: #272fc2; text-decoration: underline; }
|
||||||
.index_guides div a:hover {
|
.index_guides div a:hover {
|
||||||
background-color: #99b3ec;
|
background-color: #99b3ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inheritance diagram */
|
||||||
|
.inheritance .inheritance__level {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inheritance .inheritance__level--root {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inheritance .inheritance__level__node::before {
|
||||||
|
content: "↳";
|
||||||
|
}
|
||||||
|
|
||||||
|
.inheritance .inheritance__level__node--root::before {
|
||||||
|
/* simulate the spacing of the arrow character */
|
||||||
|
content: " ";
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-header__section {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
185
docs/ldoc.ltp
185
docs/ldoc.ltp
|
@ -38,11 +38,86 @@
|
||||||
# local use_li = ldoc.use_li
|
# local use_li = ldoc.use_li
|
||||||
# local display_name = ldoc.display_name
|
# local display_name = ldoc.display_name
|
||||||
# local iter = ldoc.modules.iter
|
# local iter = ldoc.modules.iter
|
||||||
# local function M(txt,item) return ldoc.markup(txt,item,ldoc.plain) end
|
# local function un_cmake(s) return s:gsub(";", ";"):gsub(""", '"') end
|
||||||
|
# local function M(txt,item) return ldoc.markup(txt and un_cmake(txt) or nil,item,ldoc.plain) end
|
||||||
# local nowrap = ldoc.wrap and '' or 'nowrap'
|
# local nowrap = ldoc.wrap and '' or 'nowrap'
|
||||||
# local html_space = function(s) return s:gsub(" ", "%%20") end
|
# local html_space = function(s) return s:gsub(" ", "%%20") end
|
||||||
# local no_underscores = function(s) return s:gsub("_", " ") end
|
# local no_underscores = function(s) return s:gsub("_", " ") end
|
||||||
|
|
||||||
|
# --------- modules hierarchy -------------
|
||||||
|
# local hierarchy = {}
|
||||||
|
# local curr = module
|
||||||
|
# while curr do
|
||||||
|
# hierarchy[#hierarchy + 1] = curr
|
||||||
|
# -- no need to do anything more if there is no explicite @supermodule
|
||||||
|
# if not curr.tags.supermodule then break end
|
||||||
|
# local super = curr.tags.supermodule[1] -- only consider one way inheritance
|
||||||
|
# local found = false
|
||||||
|
# for kind, mods, type in ldoc.kinds() do
|
||||||
|
# for mod in mods() do
|
||||||
|
# local name = display_name(mod)
|
||||||
|
# if name == super then
|
||||||
|
# curr = mod
|
||||||
|
# found = true
|
||||||
|
# end
|
||||||
|
# if found then break end
|
||||||
|
# end
|
||||||
|
# if found then break end
|
||||||
|
# end
|
||||||
|
# if not found then curr = nil end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# --------- merge modules content with supermodules -------------
|
||||||
|
# local all_module_kinds = {}
|
||||||
|
# if module then
|
||||||
|
# for kind,items in module.kinds() do
|
||||||
|
# local myitems = {}
|
||||||
|
# for item in items() do
|
||||||
|
# myitems[#myitems + 1] = item
|
||||||
|
# end
|
||||||
|
# all_module_kinds[#all_module_kinds + 1] = { kind = kind, items = myitems }
|
||||||
|
# end
|
||||||
|
# local filtered_kinds = { "Constructors", "Static module functions",
|
||||||
|
# "Functions", "Methods", "lib.gears.object.properties Functions" }
|
||||||
|
# for supermodule in iter(hierarchy) do
|
||||||
|
# for kind,items in supermodule.kinds() do
|
||||||
|
# local ignored = false
|
||||||
|
# for _,filtered in ldoc.pairs(filtered_kinds) do
|
||||||
|
# if kind == filtered then
|
||||||
|
# ignored = true
|
||||||
|
# break
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# if not ignored then
|
||||||
|
# local curr_kind = nil
|
||||||
|
# for k in iter(all_module_kinds) do
|
||||||
|
# if k.kind == kind then
|
||||||
|
# curr_kind = k
|
||||||
|
# break
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# if not curr_kind then
|
||||||
|
# curr_kind = { kind = kind, items = {} }
|
||||||
|
# all_module_kinds[#all_module_kinds + 1] = curr_kind
|
||||||
|
# end
|
||||||
|
# for item in items() do
|
||||||
|
# local tobeadded = true
|
||||||
|
# for i in iter(curr_kind.items) do
|
||||||
|
# if item.name == i.name then
|
||||||
|
# tobeadded = false
|
||||||
|
# break
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# if tobeadded then
|
||||||
|
# item.inherited = true -- force inherited status
|
||||||
|
# curr_kind.items[#curr_kind.items + 1] = item
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
<!-- Menu -->
|
<!-- Menu -->
|
||||||
|
|
||||||
<div id="navigation">
|
<div id="navigation">
|
||||||
|
@ -59,7 +134,8 @@
|
||||||
# if module and not ldoc.no_summary and #module.items > 0 then
|
# if module and not ldoc.no_summary and #module.items > 0 then
|
||||||
<h2>Contents</h2>
|
<h2>Contents</h2>
|
||||||
<ul>
|
<ul>
|
||||||
# for kind,items in module.kinds() do
|
# for k in iter(all_module_kinds) do
|
||||||
|
# local kind = k.kind
|
||||||
# if not kind:match("^ldoc_skip") then
|
# if not kind:match("^ldoc_skip") then
|
||||||
<li><a href="#$(no_spaces(kind))">$(kind)</a></li>
|
<li><a href="#$(no_spaces(kind))">$(kind)</a></li>
|
||||||
# end
|
# end
|
||||||
|
@ -109,41 +185,90 @@
|
||||||
<h1>Module: <code>$(module.name)</code></h1>
|
<h1>Module: <code>$(module.name)</code></h1>
|
||||||
<p>$(M(module.summary,module))</p>
|
<p>$(M(module.summary,module))</p>
|
||||||
<p>$(M(module.description,module))</p>
|
<p>$(M(module.description,module))</p>
|
||||||
# if module.tags.include then
|
|
||||||
$(M(ldoc.include_file(module.tags.include)))
|
|
||||||
# end
|
|
||||||
# if module.see then
|
|
||||||
# local li,il = use_li(module.see)
|
|
||||||
<h3>See also:</h3>
|
|
||||||
<ul>
|
|
||||||
# for see in iter(module.see) do
|
|
||||||
$(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
|
|
||||||
# end -- for
|
|
||||||
</ul>
|
|
||||||
# end -- if see
|
|
||||||
# if module.usage then
|
# if module.usage then
|
||||||
# local li,il = use_li(module.usage)
|
# local li,il = use_li(module.usage)
|
||||||
<h3>Usage:</h3>
|
<h3>Usage:</h3>
|
||||||
<ul>
|
<ul>
|
||||||
# for usage in iter(module.usage) do
|
# for usage in iter(module.usage) do
|
||||||
$(li)<pre class="example">$(ldoc.escape(usage))</pre>$(il)
|
$(li)<pre class="example">$(ldoc.escape(un_cmake(usage)))</pre>$(il)
|
||||||
# end -- for
|
# end -- for
|
||||||
</ul>
|
</ul>
|
||||||
# end -- if usage
|
# end -- if usage
|
||||||
|
<div class="extra-header">
|
||||||
|
# if module.tags.supermodule then
|
||||||
|
<div class="extra-header__section">
|
||||||
|
<h3>Class Hierarchy</h3>
|
||||||
|
<div class="inheritance">
|
||||||
|
# local function draw_hierary_recursifly(i)
|
||||||
|
# local is_root_level = (i == #hierarchy)
|
||||||
|
<ul class="inheritance__level $(is_root_level and 'inheritance__level--root' or '')">
|
||||||
|
<li class="inheritance__level__node $(is_root_level and 'inheritance__level__node--root' or '')">
|
||||||
|
# local mod = hierarchy[i]
|
||||||
|
# local name = display_name(hierarchy[i])
|
||||||
|
# if mod == module then
|
||||||
|
<strong>$(name)</strong>
|
||||||
|
# else
|
||||||
|
<a href="$(ldoc.ref_to_module(mod))">$(name)</a>
|
||||||
|
# end
|
||||||
|
</li>
|
||||||
|
# if i > 1 then
|
||||||
|
<li>
|
||||||
|
# draw_hierary_recursifly(i - 1)
|
||||||
|
</li>
|
||||||
|
# end
|
||||||
|
</ul>
|
||||||
|
# end -- function draw_hierary_recursifly
|
||||||
|
# draw_hierary_recursifly(#hierarchy)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
# end -- module.tags.supermodule
|
||||||
|
|
||||||
|
# if module.tags.include then
|
||||||
|
$(M(ldoc.include_file(module.tags.include)))
|
||||||
|
# end
|
||||||
|
|
||||||
# if module.info then
|
# if module.info then
|
||||||
|
<div class="extra-header__section">
|
||||||
<h3>Info:</h3>
|
<h3>Info:</h3>
|
||||||
<ul>
|
<ul>
|
||||||
# for tag, value in module.info:iter() do
|
# for tag, value in module.info:iter() do
|
||||||
<li><strong>$(tag)</strong>: $(M(value,module))</li>
|
# if tag == 'Author' then
|
||||||
|
<li>
|
||||||
|
<strong>Originally authored by</strong>: $(M(value,module))<br />
|
||||||
|
<small>(Full contributors list available on
|
||||||
|
<a href="https://github.com/awesomeWM/awesome/graphs/contributors">
|
||||||
|
our github project)
|
||||||
|
</a></small>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
# else
|
||||||
|
<li><strong>$(tag)</strong>: $(M(value,module))</li>
|
||||||
|
# end
|
||||||
# end
|
# end
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
# end -- if module.info
|
# end -- if module.info
|
||||||
|
|
||||||
|
# if module.see then
|
||||||
|
<div class="extra-header__section">
|
||||||
|
# local li,il = use_li(module.see)
|
||||||
|
# local list_or_p =(#module.see > 1) and 'ul' or 'p'
|
||||||
|
<h3>See also:</h3>
|
||||||
|
<$(list_or_p)>
|
||||||
|
# for see in iter(module.see) do
|
||||||
|
$(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
|
||||||
|
# end -- for
|
||||||
|
</$(list_or_p)>
|
||||||
|
</div>
|
||||||
|
# end -- if module.see
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
# if not ldoc.no_summary then
|
# if not ldoc.no_summary then
|
||||||
# -- bang out the tables of item types for this module (e.g Functions, Tables, etc)
|
# -- bang out the tables of item types for this module (e.g Functions, Tables, etc)
|
||||||
# local last_kind = ""
|
# local last_kind = ""
|
||||||
# for kind,items in module.kinds() do
|
# for k in iter(all_module_kinds) do
|
||||||
|
# local kind = k.kind
|
||||||
# if not kind:match("^ldoc_skip") then
|
# if not kind:match("^ldoc_skip") then
|
||||||
# if last_kind ~= "" then
|
# if last_kind ~= "" then
|
||||||
</table>
|
</table>
|
||||||
|
@ -151,8 +276,9 @@
|
||||||
<h2><a href="#$(no_spaces(kind))">$(kind)</a></h2>
|
<h2><a href="#$(no_spaces(kind))">$(kind)</a></h2>
|
||||||
<table class="function_list">
|
<table class="function_list">
|
||||||
# end
|
# end
|
||||||
# for item in items() do
|
# for item in iter(k.items) do if not item.tags.hidden then
|
||||||
# local dn = display_name(item)
|
# local dn = display_name(item)
|
||||||
|
# local inherited = (item.baseclass ~= module.name)
|
||||||
# if item.sanitize_type then item.sanitize_type(item, ldoc) end
|
# if item.sanitize_type then item.sanitize_type(item, ldoc) end
|
||||||
<tr>
|
<tr>
|
||||||
# if item.display_type and not item.compact_signature then
|
# if item.display_type and not item.compact_signature then
|
||||||
|
@ -166,14 +292,14 @@
|
||||||
# end
|
# end
|
||||||
</td>
|
</td>
|
||||||
# end
|
# end
|
||||||
<td colspan="$(item.inherited and 1 or 2)" class="summary">$(M(item.summary,item))</td>
|
<td colspan="$(inherited and 1 or 2)" class="summary">$(M(item.summary,item))</td>
|
||||||
# if item.inherited then
|
# if inherited then
|
||||||
<td class="baseclass" $(nowrap)>
|
<td class="baseclass" nowrap>
|
||||||
Inherited from $(item.baseclass)
|
Inherited from $(item.baseclass)
|
||||||
</td>
|
</td>
|
||||||
# end
|
# end
|
||||||
</tr>
|
</tr>
|
||||||
# end -- for items
|
# end end -- for items
|
||||||
# last_kind = kind
|
# last_kind = kind
|
||||||
#end -- for kinds
|
#end -- for kinds
|
||||||
</table>
|
</table>
|
||||||
|
@ -187,7 +313,8 @@
|
||||||
# --- function parameters or table fields.
|
# --- function parameters or table fields.
|
||||||
# local show_return = not ldoc.no_return_or_parms
|
# local show_return = not ldoc.no_return_or_parms
|
||||||
# local show_parms, last_kind = show_return, ""
|
# local show_parms, last_kind = show_return, ""
|
||||||
# for kind, items in module.kinds() do
|
# for k in iter(all_module_kinds) do
|
||||||
|
# local kind = k.kind
|
||||||
# local kitem = module.kinds:get_item(kind)
|
# local kitem = module.kinds:get_item(kind)
|
||||||
# local has_description = kitem and ldoc.descript(kitem) ~= ""
|
# local has_description = kitem and ldoc.descript(kitem) ~= ""
|
||||||
# if not kind:match("^ldoc_skip") then
|
# if not kind:match("^ldoc_skip") then
|
||||||
|
@ -205,13 +332,13 @@
|
||||||
# end
|
# end
|
||||||
# if kitem.usage then
|
# if kitem.usage then
|
||||||
<h3>Usage:</h3>
|
<h3>Usage:</h3>
|
||||||
<pre class="example">$(ldoc.prettify(kitem.usage[1]))</pre>
|
<pre class="example">$(ldoc.prettify(un_cmake(kitem.usage[1])))</pre>
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
# if not kind:match("^ldoc_skip") then
|
# if not kind:match("^ldoc_skip") then
|
||||||
<dl class="function">
|
<dl class="function">
|
||||||
# end
|
# end
|
||||||
# for item in items() do
|
# for item in iter(k.items) do if not item.tags.hidden then
|
||||||
<dt>
|
<dt>
|
||||||
<a name = "$(item.name)"></a>
|
<a name = "$(item.name)"></a>
|
||||||
<strong>$(display_name(item))</strong>
|
<strong>$(display_name(item))</strong>
|
||||||
|
@ -225,8 +352,8 @@
|
||||||
<span class="proptype">$(item.display_type)</span>
|
<span class="proptype">$(item.display_type)</span>
|
||||||
# end
|
# end
|
||||||
<span class="baseclass" $(nowrap)>
|
<span class="baseclass" $(nowrap)>
|
||||||
# if item.inherited then
|
# if item.baseclass ~= module.name then
|
||||||
· Inherited from $(item.baseclass)
|
· Inherited from $(M(item.baseclass, item))
|
||||||
# end
|
# end
|
||||||
# if item.extra_summary then
|
# if item.extra_summary then
|
||||||
# for _, col in ldoc.ipairs(item.extra_summary) do
|
# for _, col in ldoc.ipairs(item.extra_summary) do
|
||||||
|
@ -324,7 +451,7 @@
|
||||||
<h3>Usage:</h3>
|
<h3>Usage:</h3>
|
||||||
<ul>
|
<ul>
|
||||||
# for usage in iter(item.usage) do
|
# for usage in iter(item.usage) do
|
||||||
$(li)<pre class="example">$(ldoc.prettify(usage))</pre>$(il)
|
$(li)<pre class="example">$(ldoc.prettify(un_cmake(usage)))</pre>$(il)
|
||||||
# end -- for
|
# end -- for
|
||||||
</ul>
|
</ul>
|
||||||
# end -- if usage
|
# end -- if usage
|
||||||
|
@ -371,7 +498,7 @@
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</dd>
|
</dd>
|
||||||
# end -- for items
|
# end end -- for items
|
||||||
# last_kind = kind
|
# last_kind = kind
|
||||||
# end -- for kinds
|
# end -- for kinds
|
||||||
</dl>
|
</dl>
|
||||||
|
|
2
ewmh.c
2
ewmh.c
|
@ -468,7 +468,7 @@ ewmh_process_client_message(xcb_client_message_event_t *ev)
|
||||||
lua_State *L = globalconf_get_lua_State();
|
lua_State *L = globalconf_get_lua_State();
|
||||||
luaA_object_push(L, globalconf.tags.tab[idx]);
|
luaA_object_push(L, globalconf.tags.tab[idx]);
|
||||||
lua_pushstring(L, "ewmh");
|
lua_pushstring(L, "ewmh");
|
||||||
luaA_object_emit_signal(L, -1, "request::select", 1);
|
luaA_object_emit_signal(L, -2, "request::select", 1);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,28 @@ do
|
||||||
assert(root.object == root_object)
|
assert(root.object == root_object)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- The old wallpaper only took native surfaces.
|
||||||
|
--
|
||||||
|
-- This was a problem for the test backend. The new function takes both
|
||||||
|
-- native surfaces and LGI-ified Cairo surfaces.
|
||||||
|
function root.wallpaper(pattern)
|
||||||
|
if not pattern then return root._wallpaper() end
|
||||||
|
|
||||||
|
-- Checking for type will either potentially `error()` or always
|
||||||
|
-- return `userdata`. This check will error() when the surface is
|
||||||
|
-- already native.
|
||||||
|
local err = pcall(function() return pattern._native end)
|
||||||
|
|
||||||
|
-- The presence of `root._write_string` means the test backend is
|
||||||
|
-- used. Avoid passing the native surface.
|
||||||
|
if err and not root._write_string then
|
||||||
|
return root._wallpaper(pattern._native)
|
||||||
|
else
|
||||||
|
return root._wallpaper(pattern)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- root.bottons() used to be a capi function. However this proved confusing
|
-- root.bottons() used to be a capi function. However this proved confusing
|
||||||
-- as rc.lua used `awful.button` and `root.buttons()` used capi.button. There
|
-- as rc.lua used `awful.button` and `root.buttons()` used capi.button. There
|
||||||
-- was a little documented hack to "flatten" awful.button into a pair of
|
-- was a little documented hack to "flatten" awful.button into a pair of
|
||||||
|
|
|
@ -595,18 +595,42 @@ function client.object.move_to_screen(self, s)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Tag a client with the set of current tags.
|
--- Find suitable tags for newly created clients.
|
||||||
|
--
|
||||||
|
-- In most cases, the functionality you're actually looking for as a user will
|
||||||
|
-- either be
|
||||||
|
--
|
||||||
|
-- c:tags(c.screen.selected_tags)
|
||||||
|
--
|
||||||
|
-- or
|
||||||
|
--
|
||||||
|
-- local s = awful.screen.focused()
|
||||||
|
-- c:move_to_screen(s)
|
||||||
|
-- c:tags(s.selected_tags)
|
||||||
|
--
|
||||||
|
-- Despite its naming, this is primarily used to tag newly created clients.
|
||||||
|
-- As such, this method has no effect when applied to a client that already has
|
||||||
|
-- tags assigned (except for emitting `property::tag`).
|
||||||
|
--
|
||||||
|
-- Additionally, while it is a rare case, if the client's screen has no selected
|
||||||
|
-- tags at the point of calling this method, it will fall back to the screen's
|
||||||
|
-- full set of tags.
|
||||||
|
--
|
||||||
-- @method to_selected_tags
|
-- @method to_selected_tags
|
||||||
-- @see screen.selected_tags
|
-- @see screen.selected_tags
|
||||||
function client.object.to_selected_tags(self)
|
function client.object.to_selected_tags(self)
|
||||||
local tags = {}
|
local tags = {}
|
||||||
|
|
||||||
|
-- From the client's current tags, find the ones that
|
||||||
|
-- belong to the client's screen
|
||||||
for _, t in ipairs(self:tags()) do
|
for _, t in ipairs(self:tags()) do
|
||||||
if get_screen(t.screen) == get_screen(self.screen) then
|
if get_screen(t.screen) == get_screen(self.screen) then
|
||||||
table.insert(tags, t)
|
table.insert(tags, t)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- If no tags were found,
|
||||||
|
-- choose the screen's selected tags, if any, or all of the screens tags
|
||||||
if self.screen then
|
if self.screen then
|
||||||
if #tags == 0 then
|
if #tags == 0 then
|
||||||
tags = self.screen.selected_tags
|
tags = self.screen.selected_tags
|
||||||
|
@ -617,6 +641,7 @@ function client.object.to_selected_tags(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Prevent clients from becoming untagged
|
||||||
if #tags ~= 0 then
|
if #tags ~= 0 then
|
||||||
self:tags(tags)
|
self:tags(tags)
|
||||||
end
|
end
|
||||||
|
@ -1134,10 +1159,13 @@ end
|
||||||
|
|
||||||
--- Change window factor of a client.
|
--- Change window factor of a client.
|
||||||
--
|
--
|
||||||
|
-- This will emit `property::windowfact` on the specific tag object
|
||||||
|
-- `c.screen.selected_tag`.
|
||||||
|
--
|
||||||
-- @legacylayout awful.client.incwfact
|
-- @legacylayout awful.client.incwfact
|
||||||
-- @tparam number add Amount to increase/decrease the client's window factor.
|
-- @tparam number add Amount to increase/decrease the client's window factor by.
|
||||||
-- Should be between `-current_window_factor` and something close to
|
-- Should be between `-current_window_factor` and something close to
|
||||||
-- infinite. The normalisation then ensures that the sum of all factors is 1.
|
-- infinite. Normalisation then ensures that the sum of all factors is 1.
|
||||||
-- @tparam client c the client.
|
-- @tparam client c the client.
|
||||||
-- @emits property::windowfact
|
-- @emits property::windowfact
|
||||||
function client.incwfact(add, c)
|
function client.incwfact(add, c)
|
||||||
|
|
|
@ -196,7 +196,7 @@ function focus.global_bydirection(dir, c, stacked)
|
||||||
local scr = get_screen(sel and sel.screen or screen.focused())
|
local scr = get_screen(sel and sel.screen or screen.focused())
|
||||||
|
|
||||||
-- change focus inside the screen
|
-- change focus inside the screen
|
||||||
focus.bydirection(dir, sel)
|
focus.bydirection(dir, sel, stacked)
|
||||||
|
|
||||||
-- if focus not changed, we must change screen
|
-- if focus not changed, we must change screen
|
||||||
if sel == capi.client.focus then
|
if sel == capi.client.focus then
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
--- D-Bus module for awful.
|
--- D-Bus module for awful.
|
||||||
--
|
--
|
||||||
-- This module simply request the org.awesomewm.awful name on the D-Bus
|
-- This module simply requests the org.awesomewm.awful name on the D-Bus
|
||||||
-- for futur usage by other awful modules.
|
-- for future usage by other awful modules.
|
||||||
--
|
--
|
||||||
-- @author Julien Danjou <julien@danjou.info>
|
-- @author Julien Danjou <julien@danjou.info>
|
||||||
-- @copyright 2009 Julien Danjou
|
-- @copyright 2009 Julien Danjou
|
||||||
|
|
|
@ -438,7 +438,10 @@ function widget.new(args)
|
||||||
end
|
end
|
||||||
|
|
||||||
function widget_instance:_create_group_columns(column_layouts, group, keys, s, wibox_height)
|
function widget_instance:_create_group_columns(column_layouts, group, keys, s, wibox_height)
|
||||||
local line_height = beautiful.get_font_height(self.font)
|
local line_height = math.max(
|
||||||
|
beautiful.get_font_height(self.font),
|
||||||
|
beautiful.get_font_height(self.description_font)
|
||||||
|
)
|
||||||
local group_label_height = line_height + self.group_margin
|
local group_label_height = line_height + self.group_margin
|
||||||
-- -1 for possible pagination:
|
-- -1 for possible pagination:
|
||||||
local max_height_px = wibox_height - group_label_height
|
local max_height_px = wibox_height - group_label_height
|
||||||
|
@ -472,7 +475,7 @@ function widget.new(args)
|
||||||
table.insert(((i<available_height_items) and new_keys or overlap_leftovers), keys[i])
|
table.insert(((i<available_height_items) and new_keys or overlap_leftovers), keys[i])
|
||||||
end
|
end
|
||||||
keys = new_keys
|
keys = new_keys
|
||||||
table.insert(keys, {key=markup.fg(self.modifiers_fg, "▽"), description=""})
|
table.insert(keys, {key="▽", description=""})
|
||||||
end
|
end
|
||||||
if not current_column then
|
if not current_column then
|
||||||
current_column = {layout=wibox.layout.fixed.vertical()}
|
current_column = {layout=wibox.layout.fixed.vertical()}
|
||||||
|
@ -481,32 +484,39 @@ function widget.new(args)
|
||||||
|
|
||||||
local function insert_keys(_keys, _add_new_column)
|
local function insert_keys(_keys, _add_new_column)
|
||||||
local max_label_width = 0
|
local max_label_width = 0
|
||||||
local max_label_content = ""
|
|
||||||
local joined_labels = ""
|
local joined_labels = ""
|
||||||
for i, key in ipairs(_keys) do
|
for i, key in ipairs(_keys) do
|
||||||
local length = string.len(key.key or '') + string.len(key.description or '')
|
|
||||||
local modifiers = key.mod
|
local modifiers = key.mod
|
||||||
if not modifiers or modifiers == "none" then
|
if not modifiers or modifiers == "none" then
|
||||||
modifiers = ""
|
modifiers = ""
|
||||||
else
|
else
|
||||||
length = length + string.len(modifiers) + 1 -- +1 for "+" character
|
|
||||||
modifiers = markup.fg(self.modifiers_fg, modifiers.."+")
|
modifiers = markup.fg(self.modifiers_fg, modifiers.."+")
|
||||||
end
|
end
|
||||||
|
local key_label = ""
|
||||||
|
if key.keylist and #key.keylist > 1 then
|
||||||
|
for each_key_idx, each_key in ipairs(key.keylist) do
|
||||||
|
key_label = key_label .. gstring.xml_escape(each_key)
|
||||||
|
if each_key_idx ~= #key.keylist then
|
||||||
|
key_label = key_label .. markup.fg(self.modifiers_fg, '/')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif key.key then
|
||||||
|
key_label = gstring.xml_escape(key.key)
|
||||||
|
end
|
||||||
local rendered_hotkey = markup.font(self.font,
|
local rendered_hotkey = markup.font(self.font,
|
||||||
modifiers .. (key.key or "") .. " "
|
modifiers .. key_label .. " "
|
||||||
) .. markup.font(self.description_font,
|
) .. markup.font(self.description_font,
|
||||||
key.description or ""
|
key.description or ""
|
||||||
)
|
)
|
||||||
if length > max_label_width then
|
local label_width = wibox.widget.textbox.get_markup_geometry(rendered_hotkey, s).width
|
||||||
max_label_width = length
|
if label_width > max_label_width then
|
||||||
max_label_content = rendered_hotkey
|
max_label_width = label_width
|
||||||
end
|
end
|
||||||
joined_labels = joined_labels .. rendered_hotkey .. (i~=#_keys and "\n" or "")
|
joined_labels = joined_labels .. rendered_hotkey .. (i~=#_keys and "\n" or "")
|
||||||
end
|
end
|
||||||
current_column.layout:add(wibox.widget.textbox(joined_labels))
|
current_column.layout:add(wibox.widget.textbox(joined_labels))
|
||||||
local max_width, _ = wibox.widget.textbox(max_label_content):get_preferred_size(s)
|
local max_width = max_label_width + self.group_margin
|
||||||
max_width = max_width + self.group_margin
|
if not current_column.max_width or (max_width > current_column.max_width) then
|
||||||
if not current_column.max_width or max_width > current_column.max_width then
|
|
||||||
current_column.max_width = max_width
|
current_column.max_width = max_width
|
||||||
end
|
end
|
||||||
-- +1 for group label:
|
-- +1 for group label:
|
||||||
|
|
|
@ -35,6 +35,7 @@ local ret = {
|
||||||
tooltip = require("awful.tooltip");
|
tooltip = require("awful.tooltip");
|
||||||
permissions = require("awful.permissions");
|
permissions = require("awful.permissions");
|
||||||
titlebar = require("awful.titlebar");
|
titlebar = require("awful.titlebar");
|
||||||
|
wallpaper = require("awful.wallpaper");
|
||||||
rules = require("awful.rules");
|
rules = require("awful.rules");
|
||||||
popup = require("awful.popup");
|
popup = require("awful.popup");
|
||||||
spawn = require("awful.spawn");
|
spawn = require("awful.spawn");
|
||||||
|
|
|
@ -1,6 +1,27 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
--- Create easily new key objects ignoring certain modifiers.
|
--- Create easily new key objects ignoring certain modifiers.
|
||||||
--
|
--
|
||||||
|
-- A key object can be used by @{awful.keyboard} and @{client} to define
|
||||||
|
-- keybindings.
|
||||||
|
--
|
||||||
|
-- Use awful.key to define a keybinding
|
||||||
|
-- ---
|
||||||
|
--
|
||||||
|
-- This example shows how to define a basic key object:
|
||||||
|
--
|
||||||
|
-- @DOC_text_awful_key_constructor_default_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- This example shows how to define the same basic key object with the
|
||||||
|
-- declarative pattern:
|
||||||
|
--
|
||||||
|
-- @DOC_text_awful_key_constructor_declarative_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- This second example of a key definition uses the numrow keygroup. In this
|
||||||
|
-- example, we define a key object, that select the tag to show according to
|
||||||
|
-- the key index from the numrow.
|
||||||
|
--
|
||||||
|
-- @DOC_text_awful_key_constructor_keygroup_EXAMPLE@
|
||||||
|
--
|
||||||
-- @author Julien Danjou <julien@danjou.info>
|
-- @author Julien Danjou <julien@danjou.info>
|
||||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
-- @copyright 2018 Emmanuel Lepage Vallee
|
-- @copyright 2018 Emmanuel Lepage Vallee
|
||||||
|
@ -24,21 +45,6 @@ local gobject = require("gears.object")
|
||||||
-- @property key
|
-- @property key
|
||||||
-- @param string
|
-- @param string
|
||||||
|
|
||||||
--- A group of keys.
|
|
||||||
--
|
|
||||||
-- The valid keygroups are:
|
|
||||||
--
|
|
||||||
-- * **numrow**: The row above the letters in the US PC-105/PC-104 keyboards
|
|
||||||
-- and its derivative. This is usually the number 1-9 followed by 0.
|
|
||||||
-- * **arrows**: The Left/Right/Top/Bottom keys usually located right of the
|
|
||||||
-- spacebar.
|
|
||||||
-- * **fkeys**: The keys F1 through F12 located at the topmost row of any
|
|
||||||
-- keyboard, plus F13 through F35 on specialist keyboards.
|
|
||||||
-- * **numpad**: The number keys on the keypad to the right of the letters and
|
|
||||||
-- the arrow keys. Not present in every keyboard.
|
|
||||||
--
|
|
||||||
-- @property keygroup
|
|
||||||
|
|
||||||
--- The table of modifier keys.
|
--- The table of modifier keys.
|
||||||
--
|
--
|
||||||
-- A modifier, such as `Control` are a predetermined set of keys that can be
|
-- A modifier, such as `Control` are a predetermined set of keys that can be
|
||||||
|
@ -99,6 +105,32 @@ local key = { mt = {}, hotkeys = {} }
|
||||||
|
|
||||||
local reverse_map = setmetatable({}, {__mode="k"})
|
local reverse_map = setmetatable({}, {__mode="k"})
|
||||||
|
|
||||||
|
--- The keygroups names.
|
||||||
|
--
|
||||||
|
-- It can be used instead of keygroup names.
|
||||||
|
--
|
||||||
|
-- Values associated to each property of this table are string:
|
||||||
|
--
|
||||||
|
-- - **NUMROW** = `"numrow"`: The row above the letters in the US PC-105/PC-104 keyboards and
|
||||||
|
-- its derivative. This is usually the number 1-9 followed by 0.
|
||||||
|
--
|
||||||
|
-- - **ARROWS** = `"arrows"`: The Left/Right/Top/Bottom keys usually located right of the
|
||||||
|
-- spacebar.
|
||||||
|
--
|
||||||
|
-- - **FKEYS** = `"fkeys"`: The keys F1 through F12 located at the topmost row of any
|
||||||
|
-- keyboard, plus F13 through F35 on specialist keyboards.
|
||||||
|
--
|
||||||
|
-- - **NUMPAD** = `"numpad"`: The number keys on the keypad to the right of the letters and
|
||||||
|
-- the arrow keys. Not present in every keyboard.
|
||||||
|
--
|
||||||
|
-- @table keygroup
|
||||||
|
key.keygroup = {
|
||||||
|
NUMROW = 'numrow', -- The number row.
|
||||||
|
ARROWS = 'arrows', -- The directionnal arrows.
|
||||||
|
FKEYS = 'fkeys', -- The function keys.
|
||||||
|
NUMPAD = 'numpad', -- The numpad keys.
|
||||||
|
}
|
||||||
|
|
||||||
function key:set_key(k)
|
function key:set_key(k)
|
||||||
for _, v in ipairs(self) do
|
for _, v in ipairs(self) do
|
||||||
v.key = k
|
v.key = k
|
||||||
|
@ -216,11 +248,13 @@ end
|
||||||
--
|
--
|
||||||
-- @constructorfct2 awful.key
|
-- @constructorfct2 awful.key
|
||||||
-- @tparam table args
|
-- @tparam table args
|
||||||
-- @tparam function args.key The key to trigger an event. It can be the character
|
-- @tparam string args.key The key to trigger an event. It can be the character
|
||||||
-- itself of `#+keycode` (**mandatory**).
|
-- itself of `#+keycode`.
|
||||||
-- @tparam function args.modifiers A list of modifier keys. Valid modifiers are:
|
-- @tparam[opt] string args.keygroup The keygroup to trigger an event. This
|
||||||
-- `Any`, `Mod1`, Mod2`, `Mod3`, `Mod4`, `Mod5`, `Shift`, `Lock` and `Control`.
|
-- parameter must be used as a replacement for the `key` parameter. See
|
||||||
-- This argument is (**mandatory**).
|
-- @{awful.key.keygroup}.
|
||||||
|
-- @tparam table args.modifiers A list of modifier keys. Valid modifiers are:
|
||||||
|
-- `Any`, `Mod1`, Mod2`, `Mod3`, `Mod4`, `Mod5`, `Shift`, `Lock` and `Control`.
|
||||||
-- @tparam function args.on_press Callback for when the key is pressed.
|
-- @tparam function args.on_press Callback for when the key is pressed.
|
||||||
-- @tparam function args.on_release Callback for when the key is released.
|
-- @tparam function args.on_release Callback for when the key is released.
|
||||||
|
|
||||||
|
|
|
@ -474,12 +474,20 @@ function keygrabber:start()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Stop the keygrabber.
|
--- Stop the keygrabber.
|
||||||
|
--
|
||||||
|
-- Also stops any `timeout`.
|
||||||
|
--
|
||||||
-- @method stop
|
-- @method stop
|
||||||
-- @emits stopped
|
-- @emits stopped
|
||||||
-- @emits property::current_instance
|
-- @emits property::current_instance
|
||||||
function keygrabber:stop(_stop_key, _stop_mods) -- (at)function disables ldoc params
|
function keygrabber:stop(_stop_key, _stop_mods) -- (at)function disables ldoc params
|
||||||
keygrab.stop(self.grabber)
|
keygrab.stop(self.grabber)
|
||||||
|
|
||||||
|
local timer = self._private.timer
|
||||||
|
if timer and timer.started then
|
||||||
|
timer:stop()
|
||||||
|
end
|
||||||
|
|
||||||
if self.stop_callback then
|
if self.stop_callback then
|
||||||
self.stop_callback(
|
self.stop_callback(
|
||||||
self.current_instance, _stop_key, _stop_mods, self.sequence
|
self.current_instance, _stop_key, _stop_mods, self.sequence
|
||||||
|
@ -706,12 +714,13 @@ function keygrab.run_with_keybindings(args)
|
||||||
else
|
else
|
||||||
gdebug.print_warning(
|
gdebug.print_warning(
|
||||||
"The hook's 3rd parameter has to be a function. " ..
|
"The hook's 3rd parameter has to be a function. " ..
|
||||||
gdebug.dump(v or {})
|
gdebug.dump_return(v or {})
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
gdebug.print_warning(
|
gdebug.print_warning(
|
||||||
"The keybindings should be awful.key objects".. gdebug.dump(v or {})
|
"The keybindings should be awful.key objects" ..
|
||||||
|
gdebug.dump_return(v or {})
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,28 +35,29 @@ mouse.wibox = {}
|
||||||
--
|
--
|
||||||
-- @DOC_screen_client_snap_EXAMPLE@
|
-- @DOC_screen_client_snap_EXAMPLE@
|
||||||
--
|
--
|
||||||
-- @tfield integer awful.mouse.snap.default_distance
|
-- @tfield integer snap.default_distance
|
||||||
-- @tparam[opt=8] integer default_distance
|
-- @tparam[opt=8] integer default_distance
|
||||||
-- @see awful.mouse.snap
|
-- @see awful.mouse.snap
|
||||||
|
|
||||||
--- The default distance before activating screen edge snap.
|
--- The default distance before activating screen edge snap.
|
||||||
-- @tfield integer awful.mouse.snap.aerosnap_distance
|
-- @tfield integer snap.aerosnap_distance
|
||||||
-- @tparam[opt=16] integer default_distance
|
-- @tparam[opt=16] integer aerosnap_distance
|
||||||
-- @see awful.mouse.snap
|
-- @see awful.mouse.snap
|
||||||
|
|
||||||
--- Enable screen edges snapping.
|
--- Enable screen edges snapping.
|
||||||
--
|
--
|
||||||
--
|
|
||||||
--
|
|
||||||
--@DOC_awful_placement_aero_snap_EXAMPLE@
|
--@DOC_awful_placement_aero_snap_EXAMPLE@
|
||||||
--
|
--
|
||||||
-- @tfield[opt=true] boolean awful.mouse.snap.edge_enabled
|
-- @tfield[opt=true] boolean snap.edge_enabled
|
||||||
|
-- @tparam boolean edge_enabled
|
||||||
|
|
||||||
--- Enable client to client snapping.
|
--- Enable client to client snapping.
|
||||||
-- @tfield[opt=true] boolean awful.mouse.snap.client_enabled
|
-- @tfield[opt=true] boolean snap.client_enabled
|
||||||
|
-- @tparam boolean client_enabled
|
||||||
|
|
||||||
--- Enable changing tag when a client is dragged to the edge of the screen.
|
--- Enable changing tag when a client is dragged to the edge of the screen.
|
||||||
-- @tfield[opt=false] integer awful.mouse.drag_to_tag.enabled
|
-- @tfield[opt=false] boolean drag_to_tag.enabled
|
||||||
|
-- @tparam boolean enabled
|
||||||
|
|
||||||
--- The snap outline background color.
|
--- The snap outline background color.
|
||||||
-- @beautiful beautiful.snap_bg
|
-- @beautiful beautiful.snap_bg
|
||||||
|
@ -341,9 +342,9 @@ function mouse.remove_client_mousebinding(button)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, b in ipairs {"left", "right", "middle"} do
|
for k, b in ipairs {"left", "middle", "right"} do
|
||||||
mouse.object["is_".. b .."_mouse_button_pressed"] = function()
|
mouse.object["is_".. b .."_mouse_button_pressed"] = function()
|
||||||
return capi.mouse.coords().buttons[1]
|
return capi.mouse.coords().buttons[k]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -367,6 +368,10 @@ end)
|
||||||
capi.mouse.set_index_miss_handler(function(_,key)
|
capi.mouse.set_index_miss_handler(function(_,key)
|
||||||
if mouse.object["get_"..key] then
|
if mouse.object["get_"..key] then
|
||||||
return mouse.object["get_"..key]()
|
return mouse.object["get_"..key]()
|
||||||
|
elseif mouse.object[key] and key:sub(1, 3) == "is_" then
|
||||||
|
return mouse.object[key]()
|
||||||
|
elseif mouse.object[key] then
|
||||||
|
return mouse.object[key]
|
||||||
else
|
else
|
||||||
return props[key]
|
return props[key]
|
||||||
end
|
end
|
||||||
|
|
|
@ -184,6 +184,10 @@ local function compose(...)
|
||||||
attach(d, ret, args)
|
attach(d, ret, args)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Cleanup the override because otherwise it might leak into
|
||||||
|
-- future calls.
|
||||||
|
rawset(args, "override_geometry", nil)
|
||||||
|
|
||||||
return last_geo, rets
|
return last_geo, rets
|
||||||
end, "compose")
|
end, "compose")
|
||||||
|
|
||||||
|
@ -276,6 +280,30 @@ local outer_positions = {
|
||||||
bottom_middle = function(r, w, _) return {x=r.x-w/2+r.width/2, y=r.y }, "middle" end,
|
bottom_middle = function(r, w, _) return {x=r.x-w/2+r.width/2, y=r.y }, "middle" end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Map the opposite side for a string
|
||||||
|
local opposites = {
|
||||||
|
top = "bottom",
|
||||||
|
bottom = "top",
|
||||||
|
left = "right",
|
||||||
|
right = "left",
|
||||||
|
width = "height",
|
||||||
|
height = "width",
|
||||||
|
x = "y",
|
||||||
|
y = "x",
|
||||||
|
}
|
||||||
|
|
||||||
|
-- List reletvant sides for each orientation.
|
||||||
|
local struts_orientation_to_sides = {
|
||||||
|
horizontal = { "top" , "bottom" },
|
||||||
|
vertical = { "left", "right" }
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Map orientation to the length components (width/height).
|
||||||
|
local orientation_to_length = {
|
||||||
|
horizontal = "width",
|
||||||
|
vertical = "height"
|
||||||
|
}
|
||||||
|
|
||||||
--- Add a context to the arguments.
|
--- Add a context to the arguments.
|
||||||
-- This function extend the argument table. The context is used by some
|
-- This function extend the argument table. The context is used by some
|
||||||
-- internal helper methods. If there already is a context, it has priority and
|
-- internal helper methods. If there already is a context, it has priority and
|
||||||
|
@ -477,8 +505,8 @@ wibox_update_strut = function(d, position, args)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Detect horizontal or vertical drawables
|
-- Detect horizontal or vertical drawables
|
||||||
local geo = area_common(d)
|
local geo = area_common(d)
|
||||||
local vertical = geo.width < geo.height
|
local orientation = geo.width < geo.height and "vertical" or "horizontal"
|
||||||
|
|
||||||
-- Look into the `position` string to find the relevants sides to crop from
|
-- Look into the `position` string to find the relevants sides to crop from
|
||||||
-- the workarea
|
-- the workarea
|
||||||
|
@ -486,17 +514,12 @@ wibox_update_strut = function(d, position, args)
|
||||||
|
|
||||||
local m = get_decoration(args)
|
local m = get_decoration(args)
|
||||||
|
|
||||||
if vertical then
|
for _, v in ipairs(struts_orientation_to_sides[orientation]) do
|
||||||
for _, v in ipairs {"right", "left"} do
|
if (not position) or position:match(v) then
|
||||||
if (not position) or position:match(v) then
|
-- Add the "short" rectangle lenght then the above and below margins.
|
||||||
struts[v] = geo.width + m[v]
|
struts[v] = geo[opposites[orientation_to_length[orientation]]]
|
||||||
end
|
+ m[v]
|
||||||
end
|
+ m[opposites[v]]
|
||||||
else
|
|
||||||
for _, v in ipairs {"top", "bottom"} do
|
|
||||||
if (not position) or position:match(v) then
|
|
||||||
struts[v] = geo.height + m[v]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
-- @author Emmanuel Lepage Vallee
|
-- @author Emmanuel Lepage Vallee
|
||||||
-- @copyright 2016 Emmanuel Lepage Vallee
|
-- @copyright 2016 Emmanuel Lepage Vallee
|
||||||
-- @popupmod awful.popup
|
-- @popupmod awful.popup
|
||||||
|
-- @supermodule wibox
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
local wibox = require( "wibox" )
|
local wibox = require( "wibox" )
|
||||||
local gtable = require( "gears.table" )
|
local gtable = require( "gears.table" )
|
||||||
|
|
|
@ -114,7 +114,6 @@ local io = io
|
||||||
local table = table
|
local table = table
|
||||||
local math = math
|
local math = math
|
||||||
local ipairs = ipairs
|
local ipairs = ipairs
|
||||||
local pcall = pcall
|
|
||||||
local capi =
|
local capi =
|
||||||
{
|
{
|
||||||
selection = selection
|
selection = selection
|
||||||
|
@ -258,6 +257,11 @@ local function history_add(id, command)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function have_multibyte_char_at(text, position)
|
||||||
|
return text:sub(position, position):wlen() == -1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Draw the prompt text with a cursor.
|
--- Draw the prompt text with a cursor.
|
||||||
-- @tparam table args The table of arguments.
|
-- @tparam table args The table of arguments.
|
||||||
-- @field text The text.
|
-- @field text The text.
|
||||||
|
@ -285,10 +289,14 @@ local function prompt_text_with_cursor(args)
|
||||||
text_start = gstring.xml_escape(text)
|
text_start = gstring.xml_escape(text)
|
||||||
text_end = ""
|
text_end = ""
|
||||||
else
|
else
|
||||||
char = gstring.xml_escape(text:sub(args.cursor_pos, args.cursor_pos))
|
local offset = 0
|
||||||
|
if have_multibyte_char_at(text, args.cursor_pos) then
|
||||||
|
offset = 1
|
||||||
|
end
|
||||||
|
char = gstring.xml_escape(text:sub(args.cursor_pos, args.cursor_pos + offset))
|
||||||
spacer = " "
|
spacer = " "
|
||||||
text_start = gstring.xml_escape(text:sub(1, args.cursor_pos - 1))
|
text_start = gstring.xml_escape(text:sub(1, args.cursor_pos - 1))
|
||||||
text_end = gstring.xml_escape(text:sub(args.cursor_pos + 1))
|
text_end = gstring.xml_escape(text:sub(args.cursor_pos + 1 + offset))
|
||||||
end
|
end
|
||||||
|
|
||||||
local cursor_color = gcolor.ensure_pango_color(args.cursor_color)
|
local cursor_color = gcolor.ensure_pango_color(args.cursor_color)
|
||||||
|
@ -544,9 +552,9 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
|
||||||
local function update()
|
local function update()
|
||||||
textbox:set_font(font)
|
textbox:set_font(font)
|
||||||
textbox:set_markup(prompt_text_with_cursor{
|
textbox:set_markup(prompt_text_with_cursor{
|
||||||
text = command, text_color = inv_col, cursor_color = cur_col,
|
text = command, text_color = inv_col, cursor_color = cur_col,
|
||||||
cursor_pos = cur_pos, cursor_ul = cur_ul, selectall = selectall,
|
cursor_pos = cur_pos, cursor_ul = cur_ul, selectall = selectall,
|
||||||
prompt = prettyprompt, highlighter = highlighter })
|
prompt = prettyprompt, highlighter = highlighter })
|
||||||
end
|
end
|
||||||
|
|
||||||
grabber = keygrabber.run(
|
grabber = keygrabber.run(
|
||||||
|
@ -663,6 +671,9 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
|
||||||
elseif key == "b" then
|
elseif key == "b" then
|
||||||
if cur_pos > 1 then
|
if cur_pos > 1 then
|
||||||
cur_pos = cur_pos - 1
|
cur_pos = cur_pos - 1
|
||||||
|
if have_multibyte_char_at(command, cur_pos) then
|
||||||
|
cur_pos = cur_pos - 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
elseif key == "d" then
|
elseif key == "d" then
|
||||||
if cur_pos <= #command then
|
if cur_pos <= #command then
|
||||||
|
@ -711,12 +722,20 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
|
||||||
end
|
end
|
||||||
elseif key == "f" then
|
elseif key == "f" then
|
||||||
if cur_pos <= #command then
|
if cur_pos <= #command then
|
||||||
cur_pos = cur_pos + 1
|
if have_multibyte_char_at(command, cur_pos) then
|
||||||
|
cur_pos = cur_pos + 2
|
||||||
|
else
|
||||||
|
cur_pos = cur_pos + 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
elseif key == "h" then
|
elseif key == "h" then
|
||||||
if cur_pos > 1 then
|
if cur_pos > 1 then
|
||||||
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
|
local offset = 0
|
||||||
cur_pos = cur_pos - 1
|
if have_multibyte_char_at(command, cur_pos - 1) then
|
||||||
|
offset = 1
|
||||||
|
end
|
||||||
|
command = command:sub(1, cur_pos - 2 - offset) .. command:sub(cur_pos)
|
||||||
|
cur_pos = cur_pos - 1 - offset
|
||||||
end
|
end
|
||||||
elseif key == "k" then
|
elseif key == "k" then
|
||||||
command = command:sub(1, cur_pos - 1)
|
command = command:sub(1, cur_pos - 1)
|
||||||
|
@ -844,8 +863,12 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
|
||||||
cur_pos = #command + 1
|
cur_pos = #command + 1
|
||||||
elseif key == "BackSpace" then
|
elseif key == "BackSpace" then
|
||||||
if cur_pos > 1 then
|
if cur_pos > 1 then
|
||||||
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
|
local offset = 0
|
||||||
cur_pos = cur_pos - 1
|
if have_multibyte_char_at(command, cur_pos - 1) then
|
||||||
|
offset = 1
|
||||||
|
end
|
||||||
|
command = command:sub(1, cur_pos - 2 - offset) .. command:sub(cur_pos)
|
||||||
|
cur_pos = cur_pos - 1 - offset
|
||||||
end
|
end
|
||||||
elseif key == "Delete" then
|
elseif key == "Delete" then
|
||||||
command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
|
command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
|
||||||
|
@ -889,22 +912,7 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
|
||||||
selectall = nil
|
selectall = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local success = pcall(update)
|
update()
|
||||||
while not success do
|
|
||||||
-- TODO UGLY HACK TODO
|
|
||||||
-- Setting the text failed. Most likely reason is that the user
|
|
||||||
-- entered a multibyte character and pressed backspace which only
|
|
||||||
-- removed the last byte. Let's remove another byte.
|
|
||||||
if cur_pos <= 1 then
|
|
||||||
-- No text left?!
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
|
|
||||||
cur_pos = cur_pos - 1
|
|
||||||
success = pcall(update)
|
|
||||||
end
|
|
||||||
|
|
||||||
if changed_callback then
|
if changed_callback then
|
||||||
changed_callback(command)
|
changed_callback(command)
|
||||||
end
|
end
|
||||||
|
|
|
@ -224,15 +224,56 @@ local capi =
|
||||||
client = client,
|
client = client,
|
||||||
}
|
}
|
||||||
local lgi = require("lgi")
|
local lgi = require("lgi")
|
||||||
|
local Gio = lgi.Gio
|
||||||
local GLib = lgi.GLib
|
local GLib = lgi.GLib
|
||||||
local util = require("awful.util")
|
local util = require("awful.util")
|
||||||
local gtable = require("gears.table")
|
local gtable = require("gears.table")
|
||||||
local gtimer = require("gears.timer")
|
local gtimer = require("gears.timer")
|
||||||
local aclient = require("awful.client")
|
local aclient = require("awful.client")
|
||||||
local watcher = require("gears.watcher")
|
local protected_call = require("gears.protected_call")
|
||||||
|
|
||||||
local spawn = {}
|
local spawn = {}
|
||||||
|
|
||||||
|
|
||||||
|
local end_of_file
|
||||||
|
do
|
||||||
|
-- API changes, bug fixes and lots of fun. Figure out how a EOF is signalled.
|
||||||
|
local input
|
||||||
|
if not pcall(function()
|
||||||
|
-- No idea when this API changed, but some versions expect a string,
|
||||||
|
-- others a table with some special(?) entries
|
||||||
|
input = Gio.DataInputStream.new(Gio.MemoryInputStream.new_from_data(""))
|
||||||
|
end) then
|
||||||
|
input = Gio.DataInputStream.new(Gio.MemoryInputStream.new_from_data({}))
|
||||||
|
end
|
||||||
|
local line, length = input:read_line()
|
||||||
|
if not line then
|
||||||
|
-- Fixed in 2016: NULL on the C side is transformed to nil in Lua
|
||||||
|
end_of_file = function(arg)
|
||||||
|
return not arg
|
||||||
|
end
|
||||||
|
elseif tostring(line) == "" and #line ~= length then
|
||||||
|
-- "Historic" behaviour for end-of-file:
|
||||||
|
-- - NULL is turned into an empty string
|
||||||
|
-- - The length variable is not initialized
|
||||||
|
-- It's highly unlikely that the uninitialized variable has value zero.
|
||||||
|
-- Use this hack to detect EOF.
|
||||||
|
end_of_file = function(arg1, arg2)
|
||||||
|
return #arg1 ~= arg2
|
||||||
|
end
|
||||||
|
else
|
||||||
|
assert(tostring(line) == "", "Cannot determine how to detect EOF")
|
||||||
|
-- The above uninitialized variable was fixed and thus length is
|
||||||
|
-- always 0 when line is NULL in C. We cannot tell apart an empty line and
|
||||||
|
-- EOF in this case.
|
||||||
|
require("gears.debug").print_warning("Cannot reliably detect EOF on an "
|
||||||
|
.. "GIOInputStream with this LGI version")
|
||||||
|
end_of_file = function(arg)
|
||||||
|
return tostring(arg) == ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function hash_command(cmd, rules)
|
local function hash_command(cmd, rules)
|
||||||
rules = rules or {}
|
rules = rules or {}
|
||||||
cmd = type(cmd) == "string" and cmd or table.concat(cmd, ';')
|
cmd = type(cmd) == "string" and cmd or table.concat(cmd, ';')
|
||||||
|
@ -351,10 +392,38 @@ end
|
||||||
-- @treturn[1] Integer the PID of the forked process.
|
-- @treturn[1] Integer the PID of the forked process.
|
||||||
-- @treturn[2] string Error message.
|
-- @treturn[2] string Error message.
|
||||||
-- @staticfct awful.spawn.with_line_callback
|
-- @staticfct awful.spawn.with_line_callback
|
||||||
|
function spawn.with_line_callback(cmd, callbacks)
|
||||||
|
local stdout_callback, stderr_callback, done_callback, exit_callback =
|
||||||
|
callbacks.stdout, callbacks.stderr, callbacks.output_done, callbacks.exit
|
||||||
|
local have_stdout, have_stderr = stdout_callback ~= nil, stderr_callback ~= nil
|
||||||
|
local pid, _, stdin, stdout, stderr = capi.awesome.spawn(cmd,
|
||||||
|
false, false, have_stdout, have_stderr, exit_callback)
|
||||||
|
if type(pid) == "string" then
|
||||||
|
-- Error
|
||||||
|
return pid
|
||||||
|
end
|
||||||
|
|
||||||
-- It is still "officially" in `awful.spawn`, but since `gears.watcher`
|
local done_before = false
|
||||||
-- depends on it, the implementation lives there.
|
local function step_done()
|
||||||
spawn.with_line_callback = watcher._with_line_callback
|
if have_stdout and have_stderr and not done_before then
|
||||||
|
done_before = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if done_callback then
|
||||||
|
done_callback()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if have_stdout then
|
||||||
|
spawn.read_lines(Gio.UnixInputStream.new(stdout, true),
|
||||||
|
stdout_callback, step_done, true)
|
||||||
|
end
|
||||||
|
if have_stderr then
|
||||||
|
spawn.read_lines(Gio.UnixInputStream.new(stderr, true),
|
||||||
|
stderr_callback, step_done, true)
|
||||||
|
end
|
||||||
|
assert(stdin == nil)
|
||||||
|
return pid
|
||||||
|
end
|
||||||
|
|
||||||
--- Asynchronously spawn a program and capture its output.
|
--- Asynchronously spawn a program and capture its output.
|
||||||
-- (wraps `spawn.with_line_callback`).
|
-- (wraps `spawn.with_line_callback`).
|
||||||
|
@ -369,10 +438,43 @@ spawn.with_line_callback = watcher._with_line_callback
|
||||||
-- @treturn[2] string Error message.
|
-- @treturn[2] string Error message.
|
||||||
-- @see spawn.with_line_callback
|
-- @see spawn.with_line_callback
|
||||||
-- @staticfct awful.spawn.easy_async
|
-- @staticfct awful.spawn.easy_async
|
||||||
|
function spawn.easy_async(cmd, callback)
|
||||||
-- It is still "officially" in `awful.spawn`, but since `gears.watcher`
|
local stdout = ''
|
||||||
-- depends on it, the implementation lives there.
|
local stderr = ''
|
||||||
spawn.easy_async = watcher._easy_async
|
local exitcode, exitreason
|
||||||
|
local function parse_stdout(str)
|
||||||
|
stdout = stdout .. str .. "\n"
|
||||||
|
end
|
||||||
|
local function parse_stderr(str)
|
||||||
|
stderr = stderr .. str .. "\n"
|
||||||
|
end
|
||||||
|
local function done_callback()
|
||||||
|
return callback(stdout, stderr, exitreason, exitcode)
|
||||||
|
end
|
||||||
|
local exit_callback_fired = false
|
||||||
|
local output_done_callback_fired = false
|
||||||
|
local function exit_callback(reason, code)
|
||||||
|
exitcode = code
|
||||||
|
exitreason = reason
|
||||||
|
exit_callback_fired = true
|
||||||
|
if output_done_callback_fired then
|
||||||
|
return done_callback()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function output_done_callback()
|
||||||
|
output_done_callback_fired = true
|
||||||
|
if exit_callback_fired then
|
||||||
|
return done_callback()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return spawn.with_line_callback(
|
||||||
|
cmd, {
|
||||||
|
stdout=parse_stdout,
|
||||||
|
stderr=parse_stderr,
|
||||||
|
exit=exit_callback,
|
||||||
|
output_done=output_done_callback
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
--- Call `spawn.easy_async` with a shell.
|
--- Call `spawn.easy_async` with a shell.
|
||||||
-- This calls `cmd` with `$SHELL -c` (via `awful.util.shell`).
|
-- This calls `cmd` with `$SHELL -c` (via `awful.util.shell`).
|
||||||
|
@ -399,8 +501,42 @@ end
|
||||||
-- operation finishes (e.g. due to end of file).
|
-- operation finishes (e.g. due to end of file).
|
||||||
-- @tparam[opt=false] boolean close Should the stream be closed after end-of-file?
|
-- @tparam[opt=false] boolean close Should the stream be closed after end-of-file?
|
||||||
-- @staticfct awful.spawn.read_lines
|
-- @staticfct awful.spawn.read_lines
|
||||||
|
function spawn.read_lines(input_stream, line_callback, done_callback, close)
|
||||||
|
local stream = Gio.DataInputStream.new(input_stream)
|
||||||
|
local function done()
|
||||||
|
if close then
|
||||||
|
stream:close()
|
||||||
|
end
|
||||||
|
stream:set_buffer_size(0)
|
||||||
|
if done_callback then
|
||||||
|
protected_call(done_callback)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local start_read, finish_read
|
||||||
|
start_read = function()
|
||||||
|
stream:read_line_async(GLib.PRIORITY_DEFAULT, nil, finish_read)
|
||||||
|
end
|
||||||
|
finish_read = function(obj, res)
|
||||||
|
local line, length = obj:read_line_finish(res)
|
||||||
|
if type(length) ~= "number" then
|
||||||
|
-- Error
|
||||||
|
print("Error in awful.spawn.read_lines:", tostring(length))
|
||||||
|
done()
|
||||||
|
elseif end_of_file(line, length) then
|
||||||
|
-- End of file
|
||||||
|
done()
|
||||||
|
else
|
||||||
|
-- Read a line
|
||||||
|
-- This needs tostring() for older lgi versions which returned
|
||||||
|
-- "GLib.Bytes" instead of Lua strings (I guess)
|
||||||
|
protected_call(line_callback, tostring(line))
|
||||||
|
|
||||||
spawn.read_lines = watcher._read_lines
|
-- Read the next line
|
||||||
|
start_read()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
start_read()
|
||||||
|
end
|
||||||
|
|
||||||
-- When a command should only be executed once or only have a single instance,
|
-- When a command should only be executed once or only have a single instance,
|
||||||
-- track the SNID set on them to prevent multiple execution.
|
-- track the SNID set on them to prevent multiple execution.
|
||||||
|
|
|
@ -349,7 +349,7 @@ function tag.find_fallback(screen, invalids)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- When all clients are removed from the tag.
|
--- Emitted when all clients are removed from the tag.
|
||||||
-- @signal cleared
|
-- @signal cleared
|
||||||
-- @see clear
|
-- @see clear
|
||||||
|
|
||||||
|
@ -1841,20 +1841,25 @@ capi.client.connect_signal("untagged", client_untagged)
|
||||||
capi.client.connect_signal("tagged", client_tagged)
|
capi.client.connect_signal("tagged", client_tagged)
|
||||||
capi.tag.connect_signal("request::select", tag.object.view_only)
|
capi.tag.connect_signal("request::select", tag.object.view_only)
|
||||||
|
|
||||||
--- True when a tagged client is urgent
|
--- Emitted when the number of urgent clients on this tag changes.
|
||||||
-- @signal property::urgent
|
-- @signal property::urgent
|
||||||
|
-- @param boolean `true` if there is at least one urgent client on the tag.
|
||||||
-- @see client.urgent
|
-- @see client.urgent
|
||||||
|
|
||||||
--- The number of urgent tagged clients
|
--- Emitted when the number of urgent clients on this tag changes.
|
||||||
-- @signal property::urgent_count
|
-- @signal property::urgent_count
|
||||||
|
-- @param integer The number of urgent clients on the tag.
|
||||||
-- @see client.urgent
|
-- @see client.urgent
|
||||||
|
|
||||||
--- Emitted when a screen is removed.
|
--- Emitted when a screen is removed.
|
||||||
|
--
|
||||||
-- This can be used to salvage existing tags by moving them to a new
|
-- This can be used to salvage existing tags by moving them to a new
|
||||||
-- screen (or creating a virtual screen). By default, there is no
|
-- screen (or creating a virtual screen).
|
||||||
-- handler for this request. The tags will be deleted. To prevent
|
--
|
||||||
-- this, an handler for this request must simply set a new screen
|
-- By default, there is no handler for this request and the tags will be deleted.
|
||||||
|
-- To prevent this, an handler for this request must simply set a new screen
|
||||||
-- for the tag.
|
-- for the tag.
|
||||||
|
--
|
||||||
-- @signal request::screen
|
-- @signal request::screen
|
||||||
-- @tparam string context Why it was called.
|
-- @tparam string context Why it was called.
|
||||||
|
|
||||||
|
|
|
@ -505,20 +505,33 @@ local function get_children_by_id(self, name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get a client's titlebar.
|
--- Create a new titlebar for the given client.
|
||||||
-- @tparam client c The client for which a titlebar is wanted.
|
--
|
||||||
|
-- Every client can hold up to four titlebars, one for each side (i.e. each
|
||||||
|
-- value of `args.position`).
|
||||||
|
--
|
||||||
|
-- If this constructor is called again with the same
|
||||||
|
-- values for the client (`c`) and the titlebar position (`args.position`),
|
||||||
|
-- the previous titlebar will be removed and replaced by the new one.
|
||||||
|
--
|
||||||
|
-- @DOC_awful_titlebar_constructor_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @tparam client c The client the titlebar will be attached to.
|
||||||
-- @tparam[opt={}] table args A table with extra arguments for the titlebar.
|
-- @tparam[opt={}] table args A table with extra arguments for the titlebar.
|
||||||
-- @tparam[opt=font.height*1.5] number args.size The height of the titlebar.
|
-- @tparam[opt=font.height*1.5] number args.size The size of the titlebar. Will
|
||||||
-- @tparam[opt=top] string args.position" values are `top`,
|
-- be interpreted as `height` for horizontal titlebars or as `width` for
|
||||||
-- `left`, `right` and `bottom`.
|
-- vertical titlebars.
|
||||||
-- @tparam[opt=top] string args.bg_normal
|
-- @tparam[opt="top"] string args.position Possible values are `"top"`,
|
||||||
-- @tparam[opt=top] string args.bg_focus
|
-- `"left"`, `"right"` and `"bottom"`.
|
||||||
-- @tparam[opt=top] string args.bgimage_normal
|
-- @tparam[opt] string args.bg_normal
|
||||||
-- @tparam[opt=top] string args.bgimage_focus
|
-- @tparam[opt] string args.bg_focus
|
||||||
-- @tparam[opt=top] string args.fg_normal
|
-- @tparam[opt] string args.bgimage_normal
|
||||||
-- @tparam[opt=top] string args.fg_focus
|
-- @tparam[opt] string args.bgimage_focus
|
||||||
-- @tparam[opt=top] string args.font
|
-- @tparam[opt] string args.fg_normal
|
||||||
|
-- @tparam[opt] string args.fg_focus
|
||||||
|
-- @tparam[opt] string args.font
|
||||||
-- @constructorfct awful.titlebar
|
-- @constructorfct awful.titlebar
|
||||||
|
-- @treturn wibox.drawable The newly created titlebar object.
|
||||||
local function new(c, args)
|
local function new(c, args)
|
||||||
args = args or {}
|
args = args or {}
|
||||||
local position = args.position or "top"
|
local position = args.position or "top"
|
||||||
|
@ -578,10 +591,10 @@ local function new(c, args)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Show a client's titlebar.
|
--- Show the client's titlebar.
|
||||||
-- @param c The client whose titlebar is modified
|
-- @param c The client whose titlebar is modified
|
||||||
-- @param[opt] position The position of the titlebar. Must be one of "left",
|
-- @tparam[opt="top"] string position The position of the titlebar. Must be one of `"left"`,
|
||||||
-- "right", "top", "bottom". Default is "top".
|
-- `"right"`, `"top"`, `"bottom"`.
|
||||||
-- @staticfct awful.titlebar.show
|
-- @staticfct awful.titlebar.show
|
||||||
-- @request client titlebars show granted Called when `awful.titlebar.show` is
|
-- @request client titlebars show granted Called when `awful.titlebar.show` is
|
||||||
-- called.
|
-- called.
|
||||||
|
@ -594,20 +607,20 @@ function titlebar.show(c, position)
|
||||||
new(c, args)
|
new(c, args)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Hide a client's titlebar.
|
--- Hide the client's titlebar.
|
||||||
-- @param c The client whose titlebar is modified
|
-- @param c The client whose titlebar is modified
|
||||||
-- @param[opt] position The position of the titlebar. Must be one of "left",
|
-- @tparam[opt="top"] string position The position of the titlebar. Must be one of `"left"`,
|
||||||
-- "right", "top", "bottom". Default is "top".
|
-- `"right"`, `"top"`, `"bottom"`.
|
||||||
-- @staticfct awful.titlebar.hide
|
-- @staticfct awful.titlebar.hide
|
||||||
function titlebar.hide(c, position)
|
function titlebar.hide(c, position)
|
||||||
position = position or "top"
|
position = position or "top"
|
||||||
get_titlebar_function(c, position)(c, 0)
|
get_titlebar_function(c, position)(c, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Toggle a client's titlebar, hiding it if it is visible, otherwise showing it.
|
--- Toggle the client's titlebar, hiding it if it is visible, otherwise showing it.
|
||||||
-- @param c The client whose titlebar is modified
|
-- @param c The client whose titlebar is modified
|
||||||
-- @param[opt] position The position of the titlebar. Must be one of "left",
|
-- @tparam[opt="top"] string position The position of the titlebar. Must be one of `"left"`,
|
||||||
-- "right", "top", "bottom". Default is "top".
|
-- `"right"`, `"top"`, `"bottom"`.
|
||||||
-- @staticfct awful.titlebar.toggle
|
-- @staticfct awful.titlebar.toggle
|
||||||
-- @request client titlebars toggle granted Called when `awful.titlebar.toggle` is
|
-- @request client titlebars toggle granted Called when `awful.titlebar.toggle` is
|
||||||
-- called.
|
-- called.
|
||||||
|
@ -649,9 +662,12 @@ local function update_on_signal(c, signal, widget)
|
||||||
table.insert(widgets, widget)
|
table.insert(widgets, widget)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a new titlewidget. A title widget displays the name of a client.
|
--- Create a new title widget.
|
||||||
|
--
|
||||||
|
-- A title widget displays the name of a client.
|
||||||
-- Please note that this returns a textbox and all of textbox' API is available.
|
-- Please note that this returns a textbox and all of textbox' API is available.
|
||||||
-- This way, you can e.g. modify the font that is used.
|
-- This way, you can e.g. modify the font that is used.
|
||||||
|
--
|
||||||
-- @param c The client for which a titlewidget should be created.
|
-- @param c The client for which a titlewidget should be created.
|
||||||
-- @return The title widget.
|
-- @return The title widget.
|
||||||
-- @constructorfct awful.titlebar.widget.titlewidget
|
-- @constructorfct awful.titlebar.widget.titlewidget
|
||||||
|
@ -667,9 +683,12 @@ function titlebar.widget.titlewidget(c)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a new icon widget. An icon widget displays the icon of a client.
|
--- Create a new icon widget.
|
||||||
|
--
|
||||||
|
-- An icon widget displays the icon of a client.
|
||||||
-- Please note that this returns an imagebox and all of the imagebox' API is
|
-- Please note that this returns an imagebox and all of the imagebox' API is
|
||||||
-- available. This way, you can e.g. disallow resizes.
|
-- available. This way, you can e.g. disallow resizes.
|
||||||
|
--
|
||||||
-- @param c The client for which an icon widget should be created.
|
-- @param c The client for which an icon widget should be created.
|
||||||
-- @return The icon widget.
|
-- @return The icon widget.
|
||||||
-- @constructorfct awful.titlebar.widget.iconwidget
|
-- @constructorfct awful.titlebar.widget.iconwidget
|
||||||
|
@ -677,13 +696,16 @@ function titlebar.widget.iconwidget(c)
|
||||||
return clienticon(c)
|
return clienticon(c)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a new button widget. A button widget displays an image and reacts to
|
--- Create a new button widget.
|
||||||
|
--
|
||||||
|
-- A button widget displays an image and reacts to
|
||||||
-- mouse clicks. Please note that the caller has to make sure that this widget
|
-- mouse clicks. Please note that the caller has to make sure that this widget
|
||||||
-- gets redrawn when needed by calling the returned widget's update() function.
|
-- gets redrawn when needed by calling the returned widget's `:update()` method.
|
||||||
-- The selector function should return a value describing a state. If the value
|
-- The selector function should return a value describing a state. If the value
|
||||||
-- is a boolean, either "active" or "inactive" are used. The actual image is
|
-- is a boolean, either `"active"` or `"inactive"` are used. The actual image is
|
||||||
-- then found in the theme as "titlebar_[name]_button_[normal/focus]_[state]".
|
-- then found in the theme as `titlebar_[name]_button_[normal/focus]_[state]`.
|
||||||
-- If that value does not exist, the focused state is ignored for the next try.
|
-- If that value does not exist, the focused state is ignored for the next try.
|
||||||
|
--
|
||||||
-- @param c The client for which a button is created.
|
-- @param c The client for which a button is created.
|
||||||
-- @tparam string name Name of the button, used for accessing the theme and
|
-- @tparam string name Name of the button, used for accessing the theme and
|
||||||
-- in the tooltip.
|
-- in the tooltip.
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
-- @author Sébastien Gross <seb•ɱɩɲʋʃ•awesome•ɑƬ•chezwam•ɖɵʈ•org>
|
-- @author Sébastien Gross <seb•ɱɩɲʋʃ•awesome•ɑƬ•chezwam•ɖɵʈ•org>
|
||||||
-- @copyright 2009 Sébastien Gross
|
-- @copyright 2009 Sébastien Gross
|
||||||
-- @popupmod awful.tooltip
|
-- @popupmod awful.tooltip
|
||||||
|
-- @supermodule wibox
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
|
|
||||||
local timer = require("gears.timer")
|
local timer = require("gears.timer")
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
-- Grab environment we need
|
-- Grab environment we need
|
||||||
|
local os = os
|
||||||
local assert = assert
|
local assert = assert
|
||||||
local load = loadstring or load -- luacheck: globals loadstring (compatibility with Lua 5.1)
|
local load = loadstring or load -- luacheck: globals loadstring (compatibility with Lua 5.1)
|
||||||
local loadfile = loadfile
|
local loadfile = loadfile
|
||||||
|
@ -18,7 +19,6 @@ local gstring = require("gears.string")
|
||||||
local grect = require("gears.geometry").rectangle
|
local grect = require("gears.geometry").rectangle
|
||||||
local gcolor = require("gears.color")
|
local gcolor = require("gears.color")
|
||||||
local gfs = require("gears.filesystem")
|
local gfs = require("gears.filesystem")
|
||||||
local watcher = require("gears.watcher")
|
|
||||||
local capi =
|
local capi =
|
||||||
{
|
{
|
||||||
awesome = awesome,
|
awesome = awesome,
|
||||||
|
@ -31,7 +31,8 @@ local util = {}
|
||||||
util.table = {}
|
util.table = {}
|
||||||
|
|
||||||
--- The default shell used when spawing processes.
|
--- The default shell used when spawing processes.
|
||||||
-- @tfield string awful.util.shell
|
-- @param string
|
||||||
|
util.shell = os.getenv("SHELL") or "/bin/sh"
|
||||||
|
|
||||||
--- Execute a system command and road the output.
|
--- Execute a system command and road the output.
|
||||||
-- This function implementation **has been removed** and no longer
|
-- This function implementation **has been removed** and no longer
|
||||||
|
@ -483,19 +484,6 @@ function util.round(x)
|
||||||
return gmath.round(x)
|
return gmath.round(x)
|
||||||
end
|
end
|
||||||
|
|
||||||
return setmetatable(util, {
|
return util
|
||||||
__index = function(_, key)
|
|
||||||
if key == "shell" then
|
|
||||||
return watcher._shell
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__newindex = function(_, key, value)
|
|
||||||
if key == "shell" then
|
|
||||||
watcher._shell = value
|
|
||||||
else
|
|
||||||
rawset(util, key, value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -0,0 +1,859 @@
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
--- Allows to use the wibox widget system to draw the wallpaper.
|
||||||
|
--
|
||||||
|
-- Rather than simply having a function to set an image
|
||||||
|
-- (stretched, centered or tiled) like most wallpaper tools, this module
|
||||||
|
-- leverage the full widget system to draw the wallpaper. Note that the result
|
||||||
|
-- is **not** interactive. If you want an interactive wallpaper, better use
|
||||||
|
-- a `wibox` object with the `below` property set to `true` and maximized
|
||||||
|
-- using `awful.placement.maximized`.
|
||||||
|
--
|
||||||
|
-- It is possible to create an `awful.wallpaper` object from any places, but
|
||||||
|
-- it is recommanded to do it from the `request::wallpaper` signal handler.
|
||||||
|
-- That signal is called everytime something which could affect the wallpaper
|
||||||
|
-- rendering changes, such as new screens.
|
||||||
|
--
|
||||||
|
-- Single image
|
||||||
|
-- ============
|
||||||
|
--
|
||||||
|
-- This is the default `rc.lua` wallpaper format. It fills the whole screen
|
||||||
|
-- and stretches the image while keeping the aspect ratio.
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_mazimized1_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- If the image aspect ratio doesn't match, the `bg` property can be used to
|
||||||
|
-- fill the empty area:
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_mazimized2_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- It is also possible to stretch the image:
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_mazimized3_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- Finally, it is also possible to use simpler "branding" in a corner using
|
||||||
|
-- `awful.placement`:
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_corner1_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- Tiled
|
||||||
|
-- =====
|
||||||
|
--
|
||||||
|
-- This example tiles an image:
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_tiled1_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- This one tiles a shape using the `wibox.widget.separator` widget:
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_tiled2_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- See the `wibox.container.tile` for more advanced tiling configuration
|
||||||
|
-- options.
|
||||||
|
--
|
||||||
|
-- Solid colors and gradients
|
||||||
|
-- ==========================
|
||||||
|
--
|
||||||
|
-- Solid colors can be set using the `bg` property mentionned above. It
|
||||||
|
-- is also possible to set gradients:
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_gradient1_EXAMPLE@
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_gradient2_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- Widgets
|
||||||
|
-- =======
|
||||||
|
--
|
||||||
|
-- It is possible to create a wallpaper using any widgets. However, keep
|
||||||
|
-- in mind that the wallpaper surface is not interactive, so some widgets
|
||||||
|
-- like the sliders will render, but will not behave correctly. Also, it
|
||||||
|
-- is not recommanded to update the wallpaper too often. This is very slow.
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_widget2_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- Cairo graphics API
|
||||||
|
-- ==================
|
||||||
|
--
|
||||||
|
-- AwesomeWM widgets are backed by Cairo. So it is always possible to get
|
||||||
|
-- access to the Cairo context directly to do some vector art:
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_widget1_EXAMPLE@
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- SVG vector images
|
||||||
|
-- =================
|
||||||
|
--
|
||||||
|
-- SVG are supported if `librsvg` is installed. Please note that `librsvg`
|
||||||
|
-- doesn't implement all filters you might find in the latest version of
|
||||||
|
-- your web browser. It is possible some advanced SVG will not look exactly
|
||||||
|
-- as they do in a web browser or even Inkscape. However, for most images,
|
||||||
|
-- it should look identical.
|
||||||
|
--
|
||||||
|
-- Our SVG support goes beyond simple rendering. It is possible to set a
|
||||||
|
-- custom CSS stylesheet (see `wibox.widget.imagebox.stylesheet`):
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_svg_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- Note that in the example above, it is raw SVG code, but it is also possible
|
||||||
|
-- to use a file path. If you have a `.svgz`, you need to uncompress it first
|
||||||
|
-- using `gunzip` or a software like Inkscape.
|
||||||
|
--
|
||||||
|
-- Multiple screen
|
||||||
|
-- ===============
|
||||||
|
--
|
||||||
|
-- The default `rc.lua` creates a new wallpaper everytime `request::wallpaper`
|
||||||
|
-- is emitted. This is well suited for having a single wallpaper per screen.
|
||||||
|
-- It is also much simpler to implement slideshows and add/remove screens.
|
||||||
|
--
|
||||||
|
-- However, it isn't wall suited for wallpaper rendered across multiple screens.
|
||||||
|
-- For this case, it is better to capture the return value of `awful.wallpaper {}`
|
||||||
|
-- as a global variable. Then manually call `add_screen` and `remove_screen` when
|
||||||
|
-- needed. A shortcut can be to do:
|
||||||
|
--
|
||||||
|
-- @DOC_text_awful_wallpaper_multi_screen_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- Slideshow
|
||||||
|
-- =========
|
||||||
|
--
|
||||||
|
-- Slideshows (changing the wallpaper after a few minutes) can be implemented
|
||||||
|
-- directly using a timer and callback, but it is more elegant to simply request
|
||||||
|
-- a new wallpaper, then get a random image from within the request handler. This
|
||||||
|
-- way, corner cases such as adding and removing screens are handled:
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_slideshow1_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
|
-- @copyright 2019 Emmanuel Lepage Vallee
|
||||||
|
-- @popupmod awful.wallpaper
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
require("awful._compat")
|
||||||
|
local gtable = require( "gears.table" )
|
||||||
|
local gobject = require( "gears.object" )
|
||||||
|
local gcolor = require( "gears.color" )
|
||||||
|
local gtimer = require( "gears.timer" )
|
||||||
|
local surface = require( "gears.surface" )
|
||||||
|
local base = require( "wibox.widget.base" )
|
||||||
|
local background = require( "wibox.container.background")
|
||||||
|
local beautiful = require( "beautiful" )
|
||||||
|
local cairo = require( "lgi" ).cairo
|
||||||
|
local draw = require( "wibox.widget" ).draw_to_cairo_context
|
||||||
|
local grect = require( "gears.geometry" ).rectangle
|
||||||
|
|
||||||
|
local capi = { screen = screen, root = root }
|
||||||
|
|
||||||
|
local module = {}
|
||||||
|
|
||||||
|
local function get_screen(s)
|
||||||
|
return s and capi.screen[s]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Screen as key, wallpaper as values.
|
||||||
|
local pending_repaint = setmetatable({}, {__mode = 'k'})
|
||||||
|
|
||||||
|
local backgrounds = setmetatable({}, {__mode = 'k'})
|
||||||
|
|
||||||
|
local panning_modes = {}
|
||||||
|
|
||||||
|
-- Get a list of all screen areas.
|
||||||
|
local function get_rectangles(screens, honor_workarea, honor_padding)
|
||||||
|
local ret = {}
|
||||||
|
|
||||||
|
for _, s in ipairs(screens) do
|
||||||
|
table.insert(ret, s:get_bounding_geometry {
|
||||||
|
honor_padding = honor_padding,
|
||||||
|
honor_workarea = honor_workarea
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Outer perimeter of all rectangles.
|
||||||
|
function panning_modes.outer(self)
|
||||||
|
local rectangles = get_rectangles(self.screens, self.honor_workarea, self.honor_padding)
|
||||||
|
local p1, p2 = {x = math.huge, y = math.huge}, {x = 0, y = 0}
|
||||||
|
|
||||||
|
for _, rect in ipairs(rectangles) do
|
||||||
|
p1.x, p1.y = math.min(p1.x, rect.x), math.min(p1.y, rect.y)
|
||||||
|
p2.x, p2.y = math.max(p2.x, rect.x + rect.width), math.max(p2.y, rect.y + rect.height)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Never try to paint this, it would freeze the system.
|
||||||
|
assert(p1.x ~= math.huge and p1.y ~= math.huge, "Setting wallpaper failed"..#self.screens)
|
||||||
|
|
||||||
|
return {
|
||||||
|
x = p1.x,
|
||||||
|
y = p1.y,
|
||||||
|
width = p2.x - p1.x,
|
||||||
|
height = p2.y - p1.y,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Horizontal inner perimeter of all rectangles.
|
||||||
|
function panning_modes.inner_horizontal(self)
|
||||||
|
local rectangles = get_rectangles(self.screens, self.honor_workarea, self.honor_padding)
|
||||||
|
local p1, p2 = {x = math.huge, y = 0}, {x = 0, y = math.huge}
|
||||||
|
|
||||||
|
for _, rect in ipairs(rectangles) do
|
||||||
|
p1.x, p1.y = math.min(p1.x, rect.x), math.max(p1.y, rect.y)
|
||||||
|
p2.x, p2.y = math.max(p2.x, rect.x + rect.width), math.min(p2.y, rect.y + rect.height)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Never try to paint this, it would freeze the system.
|
||||||
|
assert(p1.x ~= math.huge and p2.y ~= math.huge, "Setting wallpaper failed")
|
||||||
|
|
||||||
|
return {
|
||||||
|
x = p1.x,
|
||||||
|
y = p1.y,
|
||||||
|
width = p2.x - p1.x,
|
||||||
|
height = p2.y - p1.y,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Vertical inner perimeter of all rectangles.
|
||||||
|
function panning_modes.inner_vertical(self)
|
||||||
|
local rectangles = get_rectangles(self.screens, self.honor_workarea, self.honor_padding)
|
||||||
|
local p1, p2 = {x = 0, y = math.huge}, {x = math.huge, y = 0}
|
||||||
|
|
||||||
|
for _, rect in ipairs(rectangles) do
|
||||||
|
p1.x, p1.y = math.max(p1.x, rect.x), math.min(p1.y, rect.y)
|
||||||
|
p2.x, p2.y = math.min(p2.x, rect.x + rect.width), math.max(p2.y, rect.y + rect.height)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Never try to paint this, it would freeze the system.
|
||||||
|
assert(p1.y ~= math.huge and p2.a ~= math.huge, "Setting wallpaper failed")
|
||||||
|
|
||||||
|
return {
|
||||||
|
x = p1.x,
|
||||||
|
y = p1.y,
|
||||||
|
width = p2.x - p1.x,
|
||||||
|
height = p2.y - p1.y,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Best or vertical and horizontal "inner" modes.
|
||||||
|
function panning_modes.inner(self)
|
||||||
|
local vert = panning_modes.inner_vertical(self)
|
||||||
|
local hori = panning_modes.inner_horizontal(self)
|
||||||
|
|
||||||
|
if vert.width <= 0 or vert.height <= 0 then return hori end
|
||||||
|
if hori.width <= 0 or hori.height <= 0 then return vert end
|
||||||
|
|
||||||
|
if vert.width * vert.height > hori.width * hori.height then
|
||||||
|
return vert
|
||||||
|
else
|
||||||
|
return hori
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function paint()
|
||||||
|
if not next(pending_repaint) then return end
|
||||||
|
|
||||||
|
local root_width, root_height = capi.root.size()
|
||||||
|
|
||||||
|
-- Get the current wallpaper content.
|
||||||
|
local source = surface(root.wallpaper())
|
||||||
|
|
||||||
|
local target, cr
|
||||||
|
|
||||||
|
-- It's possible that a wallpaper for 1 screen is set using another tool, so make
|
||||||
|
-- sure we copy the current content.
|
||||||
|
if source then
|
||||||
|
target = source:create_similar(cairo.Content.COLOR, root_width, root_height)
|
||||||
|
cr = cairo.Context(target)
|
||||||
|
|
||||||
|
-- Copy the old wallpaper to the new one
|
||||||
|
cr:save()
|
||||||
|
cr.operator = cairo.Operator.SOURCE
|
||||||
|
cr:set_source_surface(source, 0, 0)
|
||||||
|
|
||||||
|
for s in screen do
|
||||||
|
cr:rectangle(
|
||||||
|
s.geometry.x,
|
||||||
|
s.geometry.y,
|
||||||
|
s.geometry.width,
|
||||||
|
s.geometry.height
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
cr:clip()
|
||||||
|
|
||||||
|
cr:paint()
|
||||||
|
cr:restore()
|
||||||
|
else
|
||||||
|
target = cairo.ImageSurface(cairo.Format.RGB32, root_width, root_height)
|
||||||
|
cr = cairo.Context(target)
|
||||||
|
end
|
||||||
|
|
||||||
|
local walls = {}
|
||||||
|
|
||||||
|
for _, wall in pairs(backgrounds) do
|
||||||
|
walls[wall] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Not supposed to happen, but there is enough API surface for
|
||||||
|
-- it to be a side effect of some signals. Calling the panning
|
||||||
|
-- mode callback with zero screen is not supported.
|
||||||
|
if not next(walls) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for wall in pairs(walls) do
|
||||||
|
|
||||||
|
local geo = type(wall._private.panning_area) == "function" and
|
||||||
|
wall._private.panning_area(wall) or
|
||||||
|
panning_modes[wall._private.panning_area](wall)
|
||||||
|
|
||||||
|
-- If false, this panning area isn't well suited for the screen geometry.
|
||||||
|
if geo.width > 0 or geo.height > 0 then
|
||||||
|
local uncovered_areas = grect.area_remove(get_rectangles(wall.screens, false, false), geo)
|
||||||
|
|
||||||
|
cr:save()
|
||||||
|
|
||||||
|
-- Prevent overwrite then there is multiple non-continuous screens.
|
||||||
|
for _, s in ipairs(wall.screens) do
|
||||||
|
cr:rectangle(
|
||||||
|
s.geometry.x,
|
||||||
|
s.geometry.y,
|
||||||
|
s.geometry.width,
|
||||||
|
s.geometry.height
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
cr:clip()
|
||||||
|
|
||||||
|
-- The older surface might contain garbage, optionally clean it.
|
||||||
|
if wall.uncovered_areas_color then
|
||||||
|
cr:set_source(gcolor(wall.uncovered_areas_color))
|
||||||
|
|
||||||
|
for _, area in ipairs(uncovered_areas) do
|
||||||
|
cr:rectangle(area.x, area.y, area.width, area.height)
|
||||||
|
cr:fill()
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if not wall._private.container then
|
||||||
|
wall._private.container = background()
|
||||||
|
wall._private.container.bg = wall._private.bg or beautiful.wallpaper_bg or "#000000"
|
||||||
|
wall._private.container.fg = wall._private.fg or beautiful.wallpaper_fg or "#ffffff"
|
||||||
|
wall._private.container.widget = wall.widget
|
||||||
|
end
|
||||||
|
|
||||||
|
local a_context = {
|
||||||
|
dpi = wall._private.context.dpi
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Pick the lowest DPI.
|
||||||
|
if not a_context.dpi then
|
||||||
|
a_context.dpi = math.huge
|
||||||
|
for _, s in ipairs(wall.screens) do
|
||||||
|
a_context.dpi = math.min(
|
||||||
|
s.dpi and s.dpi or s.preferred_dpi, a_context.dpi
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback.
|
||||||
|
if not a_context.dpi then
|
||||||
|
a_context.dpi = 96
|
||||||
|
end
|
||||||
|
|
||||||
|
cr:translate(geo.x, geo.y)
|
||||||
|
draw(wall._private.container, cr, geo.width, geo.height, a_context)
|
||||||
|
cr:restore()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set the wallpaper.
|
||||||
|
local pattern = cairo.Pattern.create_for_surface(target)
|
||||||
|
capi.root.wallpaper(pattern)
|
||||||
|
|
||||||
|
-- Limit some potential GC induced increase in memory usage.
|
||||||
|
-- But really, is someone is trying to apply wallpaper changes more
|
||||||
|
-- often than the GC is executed, they are doing it wrong.
|
||||||
|
target:finish()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
local mutex = false
|
||||||
|
|
||||||
|
-- Uploading the surface to X11 is *very* resource intensive. Given the updates
|
||||||
|
-- will often happen in batch (like startup), make sure to only do one "real"
|
||||||
|
-- update.
|
||||||
|
local function update()
|
||||||
|
if mutex then return end
|
||||||
|
|
||||||
|
mutex = true
|
||||||
|
|
||||||
|
gtimer.delayed_call(function()
|
||||||
|
-- Remove the mutex first in case `paint()` raises an exception.
|
||||||
|
mutex = false
|
||||||
|
paint()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
capi.screen.connect_signal("removed", function(s)
|
||||||
|
if not backgrounds[s] then return end
|
||||||
|
|
||||||
|
backgrounds[s]:remove_screen(s)
|
||||||
|
|
||||||
|
update()
|
||||||
|
end)
|
||||||
|
|
||||||
|
capi.screen.connect_signal("property::geometry", function(s)
|
||||||
|
if not backgrounds[s] then return end
|
||||||
|
|
||||||
|
backgrounds[s]:repaint()
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
--- The wallpaper widget.
|
||||||
|
--
|
||||||
|
-- When set, instead of using the `image_path` or `surface` properties, the
|
||||||
|
-- wallpaper will be defined as a normal `wibox` widget tree.
|
||||||
|
--
|
||||||
|
-- @property widget
|
||||||
|
-- @tparam wibox.widget widget
|
||||||
|
-- @see wibox.widget.imagebox
|
||||||
|
-- @see wibox.container.tile
|
||||||
|
|
||||||
|
--- The wallpaper DPI (dots per inch).
|
||||||
|
--
|
||||||
|
-- Each screen has a DPI. This value will be used by default, but sometime it
|
||||||
|
-- is useful to override the screen DPI and use a custom one. This makes
|
||||||
|
-- possible, for example, to draw the widgets bigger than they would otherwise
|
||||||
|
-- be.
|
||||||
|
--
|
||||||
|
-- If not DPI is defined, it will use the smallest DPI from any of the screen.
|
||||||
|
--
|
||||||
|
-- In this example, there is 3 screens with DPI of 100, 200 and 300. As you can
|
||||||
|
-- see, only the text size is affected. Many widgetds are DPI aware, but not all
|
||||||
|
-- of them. This is either because DPI isn't relevant to them or simply because it
|
||||||
|
-- isn't supported (like `wibox.widget.graph`).
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wallpaper_dpi1_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property dpi
|
||||||
|
-- @tparam[opt=screen.dpi] number dpi
|
||||||
|
-- @see screen
|
||||||
|
-- @see screen.dpi
|
||||||
|
|
||||||
|
--- The wallpaper screen.
|
||||||
|
--
|
||||||
|
-- Note that there can only be one wallpaper per screen. If there is more, one
|
||||||
|
-- will be chosen and all other ignored.
|
||||||
|
--
|
||||||
|
-- @property screen
|
||||||
|
-- @tparam screen screen
|
||||||
|
-- @see screens
|
||||||
|
-- @see add_screen
|
||||||
|
-- @see remove_screen
|
||||||
|
|
||||||
|
--- A list of screen for this wallpaper.
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_screens1_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- Some large wallpaper are made to span multiple screens.
|
||||||
|
-- @property screens
|
||||||
|
-- @tparam table screens
|
||||||
|
-- @see screen
|
||||||
|
-- @see add_screen
|
||||||
|
-- @see remove_screen
|
||||||
|
-- @see detach
|
||||||
|
|
||||||
|
--- The background color.
|
||||||
|
--
|
||||||
|
-- It will be used as the "fill" color if the `image` doesn't take all the
|
||||||
|
-- screen space. It will also be the default background for the `widget.
|
||||||
|
--
|
||||||
|
-- As usual with colors in `AwesomeWM`, it can also be a gradient or a pattern.
|
||||||
|
--
|
||||||
|
-- @property bg
|
||||||
|
-- @tparam gears.color bg
|
||||||
|
-- @see gears.color
|
||||||
|
|
||||||
|
--- The foreground color.
|
||||||
|
--
|
||||||
|
-- This will be used by the `widget` (if any).
|
||||||
|
--
|
||||||
|
-- As usual with colors in `AwesomeWM`, it can also be a gradient or a pattern.
|
||||||
|
--
|
||||||
|
-- @property fg
|
||||||
|
-- @tparam gears.color fg
|
||||||
|
-- @see gears.color
|
||||||
|
|
||||||
|
--- The default wallpaper background color.
|
||||||
|
-- @beautiful beautiful.wallpaper_bg
|
||||||
|
-- @tparam gears.color wallpaper_bg
|
||||||
|
-- @see bg
|
||||||
|
|
||||||
|
--- The default wallpaper foreground color.
|
||||||
|
--
|
||||||
|
-- This is useful when using widgets or text in the wallpaper. A wallpaper
|
||||||
|
-- created from a single image wont use this.
|
||||||
|
--
|
||||||
|
-- @beautiful beautiful.wallpaper_fg
|
||||||
|
-- @tparam gears.color wallpaper_fg
|
||||||
|
-- @see bg
|
||||||
|
|
||||||
|
--- Honor the workarea.
|
||||||
|
--
|
||||||
|
-- When set to `true`, the wallpaper will only fill the workarea space instead
|
||||||
|
-- of the entire screen. This means it wont be drawn below the `awful.wibar` or
|
||||||
|
-- docked clients. This is useful when using opaque bars. Note that it can cause
|
||||||
|
-- aspect ratio issues for the wallpaper `image` and add bars colored with the
|
||||||
|
-- `bg` color on the sides.
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_workarea1_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property honor_workarea
|
||||||
|
-- @tparam[opt=false] boolean honor_workarea
|
||||||
|
-- @see honor_padding
|
||||||
|
-- @see uncovered_areas
|
||||||
|
|
||||||
|
--- Honor the screen padding.
|
||||||
|
--
|
||||||
|
-- When set, this will look at the `screen.padding` property to restrict the
|
||||||
|
-- area where the wallpaper is rendered.
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wallpaper_padding1_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property honor_padding
|
||||||
|
-- @tparam boolean honor_padding
|
||||||
|
-- @see honor_workarea
|
||||||
|
-- @see uncovered_areas
|
||||||
|
|
||||||
|
--- Returns the list of screen(s) area which won't be covered by the wallpaper.
|
||||||
|
--
|
||||||
|
-- When `honor_workarea`, `honor_padding` or panning are used, some section of
|
||||||
|
-- the screen won't have a wallpaper. This returns a list of areas tables. Each
|
||||||
|
-- table has a `x`, `y`, `width` and `height` key.
|
||||||
|
--
|
||||||
|
-- @property uncovered_areas
|
||||||
|
-- @tparam table uncovered_areas
|
||||||
|
-- @see honor_workarea
|
||||||
|
-- @see honor_padding
|
||||||
|
-- @see uncovered_areas_color
|
||||||
|
|
||||||
|
--- The color for the uncovered areas.
|
||||||
|
--
|
||||||
|
-- Some application rely on the wallpaper for "fake" transparency. Even if an
|
||||||
|
-- area is hidden under a wibar (or other clients), its background can still
|
||||||
|
-- become visible. If you use such application and change your screen geometry
|
||||||
|
-- often enough, it is possible some areas would become filled with the remains
|
||||||
|
-- of previous wallpapers. This property allows to clean those areas with a solid
|
||||||
|
-- color or a gradient.
|
||||||
|
--
|
||||||
|
-- @property uncovered_areas_color
|
||||||
|
-- @tparam gears.color uncovered_areas_color
|
||||||
|
-- @see uncovered_areas
|
||||||
|
|
||||||
|
--- Defines where the wallpaper is placed when there is multiple screens.
|
||||||
|
--
|
||||||
|
-- When there is more than 1 screen, it is possible they don't have the same
|
||||||
|
-- resolution, position or orientation. Panning the wallpaper over them may look
|
||||||
|
-- better if a continuous rectangle is used rather than creating a virtual rectangle
|
||||||
|
-- around all screens.
|
||||||
|
--
|
||||||
|
-- The default algorithms are:
|
||||||
|
--
|
||||||
|
-- **outer:** *(default)*
|
||||||
|
--
|
||||||
|
-- Draw an imaginary rectangle around all screens.
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wallpaper_panning_outer_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- **inner:**
|
||||||
|
--
|
||||||
|
-- Take the largest area or either `inner_horizontal` or `inner_vertical`.
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wallpaper_panning_inner_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- **inner_horizontal:**
|
||||||
|
--
|
||||||
|
-- Take the smallest `x` value, the largest `x+width`, the smallest `y`
|
||||||
|
-- and the smallest `y+height`.
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wallpaper_panning_inner_horizontal_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- **inner_vertical:**
|
||||||
|
--
|
||||||
|
-- Take the smallest `y` value, the largest `y+height`, the smallest `x`
|
||||||
|
-- and the smallest `x+width`.
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wallpaper_panning_inner_vertical_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- **Custom function:**
|
||||||
|
--
|
||||||
|
-- It is also possible to define a custom function.
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wallpaper_panning_custom_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property panning_area
|
||||||
|
-- @tparam function|string panning_area
|
||||||
|
-- @see uncovered_areas
|
||||||
|
|
||||||
|
function module:set_panning_area(value)
|
||||||
|
value = value or "outer"
|
||||||
|
|
||||||
|
assert(type(value) == "function" or panning_modes[value], "Invalid panning mode: "..tostring(value))
|
||||||
|
|
||||||
|
self._private.panning_area = value
|
||||||
|
|
||||||
|
self:repaint()
|
||||||
|
|
||||||
|
self:emit_signal("property::panning_area", value)
|
||||||
|
end
|
||||||
|
|
||||||
|
function module:set_widget(w)
|
||||||
|
self._private.widget = base.make_widget_from_value(w)
|
||||||
|
|
||||||
|
if self._private.container then
|
||||||
|
self._private.container.widget = self._private.widget
|
||||||
|
end
|
||||||
|
|
||||||
|
self:repaint()
|
||||||
|
end
|
||||||
|
|
||||||
|
function module:get_widget()
|
||||||
|
return self._private.widget
|
||||||
|
end
|
||||||
|
|
||||||
|
function module:set_dpi(dpi)
|
||||||
|
self._private.context.dpi = dpi
|
||||||
|
self:repaint()
|
||||||
|
end
|
||||||
|
|
||||||
|
function module:get_dpi()
|
||||||
|
return self._private.context.dpi
|
||||||
|
end
|
||||||
|
|
||||||
|
function module:set_screen(s)
|
||||||
|
if not s then return end
|
||||||
|
|
||||||
|
self:_clear()
|
||||||
|
self:add_screen(s)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, prop in ipairs {"bg", "fg"} do
|
||||||
|
module["set_"..prop] = function(self, color)
|
||||||
|
if self._private.container then
|
||||||
|
self._private.container[prop] = color
|
||||||
|
end
|
||||||
|
|
||||||
|
self._private[prop] = color
|
||||||
|
|
||||||
|
self:repaint()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function module:get_uncovered_areas()
|
||||||
|
local geo = type(self._private.panning_area) == "function" and
|
||||||
|
self._private.panning_area(self) or
|
||||||
|
panning_modes[self._private.panning_area](self)
|
||||||
|
|
||||||
|
return grect.area_remove(get_rectangles(self.screens, false, false), geo)
|
||||||
|
end
|
||||||
|
|
||||||
|
function module:set_screens(screens)
|
||||||
|
local to_rem = {}
|
||||||
|
|
||||||
|
-- All screens.
|
||||||
|
-- The copy is needed because it's a metatable, `ipairs` doesn't work
|
||||||
|
-- correctly in all Lua versions.
|
||||||
|
if screens == capi.screen then
|
||||||
|
screens = {}
|
||||||
|
|
||||||
|
for s in capi.screen do
|
||||||
|
table.insert(screens, s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, s in ipairs(screens) do
|
||||||
|
to_rem[get_screen(s)] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, s in ipairs(self.screens) do
|
||||||
|
to_rem[get_screen(s)] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, s in ipairs(screens) do
|
||||||
|
s = get_screen(s)
|
||||||
|
self:add_screen(s)
|
||||||
|
to_rem[s] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for s, remove in pairs(to_rem) do
|
||||||
|
if remove then
|
||||||
|
self:remove_screen(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function module:get_screens()
|
||||||
|
return self._private.screens
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add another screen (enable panning).
|
||||||
|
--
|
||||||
|
-- **Before:**
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_add_screen1_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- **After:**
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_add_screen2_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- Also note that adding a non-continuous screen might not work well,
|
||||||
|
-- but will not automatically add the screens in between:
|
||||||
|
--
|
||||||
|
--@DOC_awful_wallpaper_add_screen3_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @method add_screen
|
||||||
|
-- @tparam screen screen The screen object.
|
||||||
|
-- @see remove_screen
|
||||||
|
function module:add_screen(s)
|
||||||
|
s = get_screen(s)
|
||||||
|
|
||||||
|
for _, s2 in ipairs(self._private.screens) do
|
||||||
|
if s == s2 then return end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(self._private.screens, s)
|
||||||
|
|
||||||
|
if backgrounds[s] and backgrounds[s] ~= self then
|
||||||
|
backgrounds[s]:remove_screen(s)
|
||||||
|
end
|
||||||
|
|
||||||
|
backgrounds[s] = self
|
||||||
|
|
||||||
|
self:repaint()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Detach the wallpaper from all screens.
|
||||||
|
--
|
||||||
|
-- Adding a new wallpaper to a screen will automatically
|
||||||
|
-- detach the older one. However there is some case when
|
||||||
|
-- it is useful to call this manually. For example, when
|
||||||
|
-- adding a new panned wallpaper, it is possible that 2
|
||||||
|
-- wallpaper will have an overlap.
|
||||||
|
--
|
||||||
|
-- @method detach
|
||||||
|
-- @see remove_screen
|
||||||
|
-- @see add_screen
|
||||||
|
function module:detach()
|
||||||
|
local screens = gtable.clone(self.screens)
|
||||||
|
|
||||||
|
for _, s in ipairs(screens) do
|
||||||
|
self:remove_screen(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function module:_clear()
|
||||||
|
self._private.screens = setmetatable({}, {__mode = "v"})
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Repaint the wallpaper.
|
||||||
|
--
|
||||||
|
-- By default, even if the widget changes, the wallpaper will **NOT** be
|
||||||
|
-- automatically repainted. Repainting the native X11 wallpaper is slow and
|
||||||
|
-- it would be too easy to accidentally cause a performance problem. If you
|
||||||
|
-- really need to repaint the wallpaper, call this method.
|
||||||
|
--
|
||||||
|
-- @method repaint
|
||||||
|
function module:repaint()
|
||||||
|
for _, s in ipairs(self._private.screens) do
|
||||||
|
pending_repaint[s] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove a screen.
|
||||||
|
--
|
||||||
|
-- Calling this will remove a screen, but will **not** repaint its area.
|
||||||
|
-- In this example, the wallpaper was spanning all 3 screens and the
|
||||||
|
-- first screen was removed:
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wallpaper_remove_screen1_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- As you can see, the content of screen 1 still looks like it is part of
|
||||||
|
-- the 3 screen wallpaper. The only use case for calling this method is if
|
||||||
|
-- you use a 3rd party tools to change the wallpaper.
|
||||||
|
--
|
||||||
|
-- If you wish to simply remove a screen and not have leftover content, it is
|
||||||
|
-- simpler to just create a new wallpaper for that screen:
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wallpaper_remove_screen2_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @method remove_screen
|
||||||
|
-- @tparam screen screen The screen to remove.
|
||||||
|
-- @see detach
|
||||||
|
-- @see add_screen
|
||||||
|
-- @see screens
|
||||||
|
function module:remove_screen(s)
|
||||||
|
s = get_screen(s)
|
||||||
|
|
||||||
|
for k, s2 in ipairs(self._private.screens) do
|
||||||
|
if s == s2 then
|
||||||
|
table.remove(self._private.screens, k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
backgrounds[s] = nil
|
||||||
|
|
||||||
|
self:repaint()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a wallpaper.
|
||||||
|
--
|
||||||
|
-- Note that all parameters are not required. Please refer to the module description and examples to understand parameters usages.
|
||||||
|
--
|
||||||
|
-- @constructorfct awful.wallpaper
|
||||||
|
-- @tparam table args
|
||||||
|
-- @tparam[opt] wibox.widget args.widget The wallpaper widget.
|
||||||
|
-- @tparam[opt] number args.dpi The wallpaper DPI (dots per inch).
|
||||||
|
-- @tparam[opt] screen args.screen The wallpaper screen.
|
||||||
|
-- @tparam[opt] table args.screens A list of screen for this wallpaper. Use this parameter as a remplacement for `args.screen` to manage multiscreen wallpaper. (Note: the expected table should be an array-like table `{screen1, screen2, ...}`)
|
||||||
|
-- @tparam[opt] gears.color args.bg The background color.
|
||||||
|
-- @tparam[opt] gears.color args.fg The foreground color.
|
||||||
|
-- @tparam[opt] gears.color args.uncovered_areas_color The color for the uncovered areas.
|
||||||
|
-- @tparam[opt] boolean args.honor_workarea Honor the workarea.
|
||||||
|
-- @tparam[opt] boolean args.honor_padding Honor the screen padding.
|
||||||
|
-- @tparam[opt] table args.uncovered_areas Returns the list of screen(s) area which won't be covered by the wallpaper.
|
||||||
|
-- @tparam[opt] function|string args.panning_area Defines where the wallpaper is placed when there is multiple screens.
|
||||||
|
|
||||||
|
local function new(_, args)
|
||||||
|
args = args or {}
|
||||||
|
local ret = gobject {
|
||||||
|
enable_auto_signals = true,
|
||||||
|
enable_properties = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
rawset(ret, "_private", {})
|
||||||
|
ret._private.context = {}
|
||||||
|
ret._private.panning_area = "outer"
|
||||||
|
|
||||||
|
gtable.crush(ret, module, true)
|
||||||
|
|
||||||
|
ret:_clear()
|
||||||
|
|
||||||
|
-- Set the screen or screens first to avoid a race condition
|
||||||
|
-- with the other setters.
|
||||||
|
local args_screen, args_screens = args.screen, args.screens
|
||||||
|
if args_screen then
|
||||||
|
ret.screen = args_screen
|
||||||
|
elseif args_screens then
|
||||||
|
ret.screens = args_screens
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Avoid crushing `screen` and `screens` twice.
|
||||||
|
args.screen, args.screens = nil, nil
|
||||||
|
gtable.crush(ret, args, false)
|
||||||
|
args.screen, args.screens = args_screen, args_screens
|
||||||
|
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(module, {__call = new})
|
|
@ -1,16 +1,19 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
--- Wibox module for awful.
|
--- The main AwesomeWM "bar" module.
|
||||||
|
--
|
||||||
-- This module allows you to easily create wibox and attach them to the edge of
|
-- This module allows you to easily create wibox and attach them to the edge of
|
||||||
-- a screen.
|
-- a screen.
|
||||||
--
|
--
|
||||||
--@DOC_awful_wibar_default_EXAMPLE@
|
--@DOC_awful_wibar_default_EXAMPLE@
|
||||||
--
|
--
|
||||||
-- You can even have vertical bars too.
|
-- You can even have vertical bars too.
|
||||||
|
--
|
||||||
--@DOC_awful_wibar_left_EXAMPLE@
|
--@DOC_awful_wibar_left_EXAMPLE@
|
||||||
--
|
--
|
||||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
-- @copyright 2016 Emmanuel Lepage Vallee
|
-- @copyright 2016 Emmanuel Lepage Vallee
|
||||||
-- @popupmod awful.wibar
|
-- @popupmod awful.wibar
|
||||||
|
-- @supermodule awful.popup
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
-- Grab environment we need
|
-- Grab environment we need
|
||||||
|
@ -27,6 +30,7 @@ local wibox = require("wibox")
|
||||||
local beautiful = require("beautiful")
|
local beautiful = require("beautiful")
|
||||||
local gdebug = require("gears.debug")
|
local gdebug = require("gears.debug")
|
||||||
local placement = require("awful.placement")
|
local placement = require("awful.placement")
|
||||||
|
local gtable = require("gears.table")
|
||||||
|
|
||||||
local function get_screen(s)
|
local function get_screen(s)
|
||||||
return s and capi.screen[s]
|
return s and capi.screen[s]
|
||||||
|
@ -38,16 +42,94 @@ local awfulwibar = { mt = {} }
|
||||||
-- It's an array so it is ordered.
|
-- It's an array so it is ordered.
|
||||||
local wiboxes = setmetatable({}, {__mode = "v"})
|
local wiboxes = setmetatable({}, {__mode = "v"})
|
||||||
|
|
||||||
|
local opposite_margin = {
|
||||||
|
top = "bottom",
|
||||||
|
bottom = "top",
|
||||||
|
left = "right",
|
||||||
|
right = "left"
|
||||||
|
}
|
||||||
|
|
||||||
|
local align_map = {
|
||||||
|
top = "left",
|
||||||
|
left = "top",
|
||||||
|
bottom = "right",
|
||||||
|
right = "bottom",
|
||||||
|
centered = "centered"
|
||||||
|
}
|
||||||
|
|
||||||
--- If the wibar needs to be stretched to fill the screen.
|
--- If the wibar needs to be stretched to fill the screen.
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wibar_stretch_EXAMPLE@
|
||||||
|
--
|
||||||
-- @property stretch
|
-- @property stretch
|
||||||
-- @tparam boolean stretch
|
-- @tparam boolean stretch
|
||||||
-- @propbeautiful
|
-- @propbeautiful
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
-- @see align
|
||||||
|
|
||||||
|
--- How to align non-stretched wibars.
|
||||||
|
--
|
||||||
|
-- Values are:
|
||||||
|
--
|
||||||
|
-- * `"top"`
|
||||||
|
-- * `"bottom"`
|
||||||
|
-- * `"left"`
|
||||||
|
-- * `"right"`
|
||||||
|
-- * `"centered"`
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wibar_align_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property align
|
||||||
|
-- @tparam string align
|
||||||
|
-- @propbeautiful
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see stretch
|
||||||
|
|
||||||
|
--- Margins on each side of the wibar.
|
||||||
|
--
|
||||||
|
-- It can either be a table with `top`, `bottom`, `left` and `right`
|
||||||
|
-- properties, or a single number that applies to all four sides.
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wibar_margins_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property margins
|
||||||
|
-- @tparam number|table margins
|
||||||
|
-- @propbeautiful
|
||||||
|
-- @propemits true false
|
||||||
|
|
||||||
--- If the wibar needs to be stretched to fill the screen.
|
--- If the wibar needs to be stretched to fill the screen.
|
||||||
|
--
|
||||||
-- @beautiful beautiful.wibar_stretch
|
-- @beautiful beautiful.wibar_stretch
|
||||||
-- @tparam boolean stretch
|
-- @tparam boolean stretch
|
||||||
|
|
||||||
|
--- Allow or deny the tiled clients to cover the wibar.
|
||||||
|
--
|
||||||
|
-- In the example below, we can see that the first screen workarea
|
||||||
|
-- shrunk by the height of the wibar while the second screen is
|
||||||
|
-- unchanged.
|
||||||
|
--
|
||||||
|
-- @DOC_screen_wibar_workarea_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property restrict_workarea
|
||||||
|
-- @tparam[opt=true] boolean restrict_workarea
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see client.struts
|
||||||
|
-- @see screen.workarea
|
||||||
|
|
||||||
|
--- If there is both vertical and horizontal wibar, give more space to vertical ones.
|
||||||
|
--
|
||||||
|
-- By default, if multiple wibars risk overlapping, it will be resolved
|
||||||
|
-- by giving more space to the horizontal one:
|
||||||
|
--
|
||||||
|
-- ![wibar position](../images/AUTOGEN_awful_wibar_position.svg)
|
||||||
|
--
|
||||||
|
-- If this variable is to to `true`, it will behave like:
|
||||||
|
--
|
||||||
|
-- @DOC_awful_wibar_position2_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @beautiful beautiful.wibar_favor_vertical
|
||||||
|
-- @tparam[opt=false] boolean favor_vertical
|
||||||
|
|
||||||
--- The wibar border width.
|
--- The wibar border width.
|
||||||
-- @beautiful beautiful.wibar_border_width
|
-- @beautiful beautiful.wibar_border_width
|
||||||
-- @tparam integer border_width
|
-- @tparam integer border_width
|
||||||
|
@ -96,6 +178,15 @@ local wiboxes = setmetatable({}, {__mode = "v"})
|
||||||
-- @beautiful beautiful.wibar_shape
|
-- @beautiful beautiful.wibar_shape
|
||||||
-- @tparam gears.shape shape
|
-- @tparam gears.shape shape
|
||||||
|
|
||||||
|
--- The wibar's margins.
|
||||||
|
-- @beautiful beautiful.wibar_margins
|
||||||
|
-- @tparam number|table margins
|
||||||
|
|
||||||
|
--- The wibar's alignments.
|
||||||
|
-- @beautiful beautiful.wibar_align
|
||||||
|
-- @tparam string align
|
||||||
|
|
||||||
|
|
||||||
-- Compute the margin on one side
|
-- Compute the margin on one side
|
||||||
local function get_margin(w, position, auto_stop)
|
local function get_margin(w, position, auto_stop)
|
||||||
local h_or_w = (position == "top" or position == "bottom") and "height" or "width"
|
local h_or_w = (position == "top" or position == "bottom") and "height" or "width"
|
||||||
|
@ -107,7 +198,15 @@ local function get_margin(w, position, auto_stop)
|
||||||
|
|
||||||
if v.position == position and v.screen == w.screen and v.visible then
|
if v.position == position and v.screen == w.screen and v.visible then
|
||||||
ret = ret + v[h_or_w]
|
ret = ret + v[h_or_w]
|
||||||
|
|
||||||
|
local wb_margins = v.margins
|
||||||
|
|
||||||
|
if wb_margins then
|
||||||
|
ret = ret + wb_margins[position] + wb_margins[opposite_margin[position]]
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
@ -119,32 +218,49 @@ local function get_margins(w)
|
||||||
local position = w.position
|
local position = w.position
|
||||||
assert(position)
|
assert(position)
|
||||||
|
|
||||||
local margins = {left=0, right=0, top=0, bottom=0}
|
local margins = gtable.clone(w._private.margins)
|
||||||
|
|
||||||
margins[position] = get_margin(w, position, true)
|
margins[position] = margins[position] + get_margin(w, position, true)
|
||||||
|
|
||||||
-- Avoid overlapping wibars
|
-- Avoid overlapping wibars
|
||||||
if position == "left" or position == "right" then
|
if (position == "left" or position == "right") and not beautiful.wibar_favor_vertical then
|
||||||
margins.top = get_margin(w, "top" )
|
margins.top = get_margin(w, "top" )
|
||||||
margins.bottom = get_margin(w, "bottom")
|
margins.bottom = get_margin(w, "bottom")
|
||||||
|
elseif (position == "top" or position == "bottom") and beautiful.wibar_favor_vertical then
|
||||||
|
margins.left = get_margin(w, "left" )
|
||||||
|
margins.right = get_margin(w, "right")
|
||||||
end
|
end
|
||||||
|
|
||||||
return margins
|
return margins
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create the placement function
|
-- Create the placement function
|
||||||
local function gen_placement(position, stretch)
|
local function gen_placement(position, align, stretch)
|
||||||
local maximize = (position == "right" or position == "left") and
|
local maximize = (position == "right" or position == "left") and
|
||||||
"maximize_vertically" or "maximize_horizontally"
|
"maximize_vertically" or "maximize_horizontally"
|
||||||
|
|
||||||
return placement[position] + (stretch and placement[maximize] or nil)
|
local corner = nil
|
||||||
|
|
||||||
|
if align ~= "centered" then
|
||||||
|
if position == "right" or position == "left" then
|
||||||
|
corner = placement[align .. "_" .. position]
|
||||||
|
or placement[align_map[align] .. "_" .. position]
|
||||||
|
else
|
||||||
|
corner = placement[position .. "_" .. align]
|
||||||
|
or placement[position .. "_" .. align_map[align]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
corner = corner or placement[position]
|
||||||
|
|
||||||
|
return corner + (stretch and placement[maximize] or nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Attach the placement function.
|
-- Attach the placement function.
|
||||||
local function attach(wb, align)
|
local function attach(wb, position)
|
||||||
gen_placement(align, wb._stretch)(wb, {
|
gen_placement(position, wb._private.align, wb._stretch)(wb, {
|
||||||
attach = true,
|
attach = true,
|
||||||
update_workarea = true,
|
update_workarea = wb._private.restrict_workarea,
|
||||||
margins = get_margins(wb)
|
margins = get_margins(wb)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
@ -172,15 +288,23 @@ end
|
||||||
-- * top
|
-- * top
|
||||||
-- * bottom
|
-- * bottom
|
||||||
--
|
--
|
||||||
|
-- @DOC_awful_wibar_position_EXAMPLE@
|
||||||
|
--
|
||||||
-- @property position
|
-- @property position
|
||||||
-- @tparam string position Either "left", right", "top" or "bottom"
|
-- @tparam string position Either "left", right", "top" or "bottom"
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
|
||||||
local function get_position(wb)
|
function awfulwibar.get_position(wb)
|
||||||
return wb._position or "top"
|
return wb._position or "top"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function set_position(wb, position, skip_reattach)
|
function awfulwibar.set_position(wb, position, screen)
|
||||||
|
if position == wb._position then return end
|
||||||
|
|
||||||
|
if screen then
|
||||||
|
gdebug.deprecate("Use `wb.screen = screen` instead of awful.wibar.set_position", {deprecated_in=4})
|
||||||
|
end
|
||||||
|
|
||||||
-- Detach first to avoid any uneeded callbacks
|
-- Detach first to avoid any uneeded callbacks
|
||||||
if wb.detach_callback then
|
if wb.detach_callback then
|
||||||
wb.detach_callback()
|
wb.detach_callback()
|
||||||
|
@ -191,14 +315,12 @@ local function set_position(wb, position, skip_reattach)
|
||||||
|
|
||||||
-- Move the wibar to the end of the list to avoid messing up the others in
|
-- Move the wibar to the end of the list to avoid messing up the others in
|
||||||
-- case there is stacked wibars on one side.
|
-- case there is stacked wibars on one side.
|
||||||
if wb._position then
|
for k, w in ipairs(wiboxes) do
|
||||||
for k, w in ipairs(wiboxes) do
|
if w == wb then
|
||||||
if w == wb then
|
table.remove(wiboxes, k)
|
||||||
table.remove(wiboxes, k)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
table.insert(wiboxes, wb)
|
|
||||||
end
|
end
|
||||||
|
table.insert(wiboxes, wb)
|
||||||
|
|
||||||
-- In case the position changed, it may be necessary to reset the size
|
-- In case the position changed, it may be necessary to reset the size
|
||||||
if (wb._position == "left" or wb._position == "right")
|
if (wb._position == "left" or wb._position == "right")
|
||||||
|
@ -217,7 +339,7 @@ local function set_position(wb, position, skip_reattach)
|
||||||
|
|
||||||
-- A way to skip reattach is required when first adding a wibar as it's not
|
-- A way to skip reattach is required when first adding a wibar as it's not
|
||||||
-- in the `wiboxes` table yet and can't be added until it's attached.
|
-- in the `wiboxes` table yet and can't be added until it's attached.
|
||||||
if not skip_reattach then
|
if not wb._private.skip_reattach then
|
||||||
-- Changing the position will also cause the other margins to be invalidated.
|
-- Changing the position will also cause the other margins to be invalidated.
|
||||||
-- For example, adding a wibar to the top will change the margins of any left
|
-- For example, adding a wibar to the top will change the margins of any left
|
||||||
-- or right wibars. To solve, this, they need to be re-attached.
|
-- or right wibars. To solve, this, they need to be re-attached.
|
||||||
|
@ -227,11 +349,11 @@ local function set_position(wb, position, skip_reattach)
|
||||||
wb:emit_signal("property::position", position)
|
wb:emit_signal("property::position", position)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_stretch(w)
|
function awfulwibar.get_stretch(w)
|
||||||
return w._stretch
|
return w._stretch
|
||||||
end
|
end
|
||||||
|
|
||||||
local function set_stretch(w, value)
|
function awfulwibar.set_stretch(w, value)
|
||||||
w._stretch = value
|
w._stretch = value
|
||||||
|
|
||||||
attach(w, w.position)
|
attach(w, w.position)
|
||||||
|
@ -239,10 +361,89 @@ local function set_stretch(w, value)
|
||||||
w:emit_signal("property::stretch", value)
|
w:emit_signal("property::stretch", value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function awfulwibar.get_restrict_workarea(w)
|
||||||
|
return w._private.restrict_workarea
|
||||||
|
end
|
||||||
|
|
||||||
|
function awfulwibar.set_restrict_workarea(w, value)
|
||||||
|
w._private.restrict_workarea = value
|
||||||
|
|
||||||
|
attach(w, w.position)
|
||||||
|
|
||||||
|
w:emit_signal("property::restrict_workarea", value)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function awfulwibar.set_margins(w, value)
|
||||||
|
if type(value) == "number" then
|
||||||
|
value = {
|
||||||
|
top = value,
|
||||||
|
bottom = value,
|
||||||
|
right = value,
|
||||||
|
left = value,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local old = gtable.crush({
|
||||||
|
left = 0,
|
||||||
|
right = 0,
|
||||||
|
top = 0,
|
||||||
|
bottom = 0
|
||||||
|
}, w._private.margins or {}, true)
|
||||||
|
|
||||||
|
value = gtable.crush(old, value or {}, true)
|
||||||
|
|
||||||
|
w._private.margins = value
|
||||||
|
|
||||||
|
attach(w, w.position)
|
||||||
|
|
||||||
|
w:emit_signal("property::margins", value)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Allow each margins to be set individually.
|
||||||
|
local function meta_margins(self)
|
||||||
|
return setmetatable({}, {
|
||||||
|
__index = self._private.margins,
|
||||||
|
__newindex = function(_, k, v)
|
||||||
|
self._private.margins[k] = v
|
||||||
|
awfulwibar.set_margins(self, self._private.margins)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function awfulwibar.get_margins(self)
|
||||||
|
return self._private.meta_margins
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function awfulwibar.get_align(self)
|
||||||
|
return self._private.align
|
||||||
|
end
|
||||||
|
|
||||||
|
function awfulwibar.set_align(self, value, screen)
|
||||||
|
if value == "center" then
|
||||||
|
gdebug.deprecate("awful.wibar.align(wb, 'center' is deprecated, use 'centered'", {deprecated_in=4})
|
||||||
|
value = "centered"
|
||||||
|
end
|
||||||
|
|
||||||
|
if screen then
|
||||||
|
gdebug.deprecate("awful.wibar.align 'screen' argument is deprecated", {deprecated_in=4})
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(align_map[value])
|
||||||
|
|
||||||
|
self._private.align = value
|
||||||
|
|
||||||
|
attach(self, self.position)
|
||||||
|
|
||||||
|
self:emit_signal("property::align", value)
|
||||||
|
end
|
||||||
|
|
||||||
--- Remove a wibar.
|
--- Remove a wibar.
|
||||||
-- @method remove
|
-- @method remove
|
||||||
|
|
||||||
local function remove(self)
|
function awfulwibar.remove(self)
|
||||||
self.visible = false
|
self.visible = false
|
||||||
|
|
||||||
if self.detach_callback then
|
if self.detach_callback then
|
||||||
|
@ -259,26 +460,6 @@ local function remove(self)
|
||||||
self._screen = nil
|
self._screen = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a wibox position if it has been set, or return top.
|
|
||||||
-- @param wb The wibox
|
|
||||||
-- @deprecated awful.wibar.get_position
|
|
||||||
-- @return The wibox position.
|
|
||||||
function awfulwibar.get_position(wb)
|
|
||||||
gdebug.deprecate("Use wb:get_position() instead of awful.wibar.get_position", {deprecated_in=4})
|
|
||||||
return get_position(wb)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Put a wibox on a screen at this position.
|
|
||||||
-- @param wb The wibox to attach.
|
|
||||||
-- @param position The position: top, bottom left or right.
|
|
||||||
-- @param screen This argument is deprecated, use wb.screen directly.
|
|
||||||
-- @deprecated awful.wibar.set_position
|
|
||||||
function awfulwibar.set_position(wb, position, screen) --luacheck: no unused args
|
|
||||||
gdebug.deprecate("Use wb:set_position(position) instead of awful.wibar.set_position", {deprecated_in=4})
|
|
||||||
|
|
||||||
set_position(wb, position)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Attach a wibox to a screen.
|
--- Attach a wibox to a screen.
|
||||||
--
|
--
|
||||||
-- This function has been moved to the `awful.placement` module. Calling this
|
-- This function has been moved to the `awful.placement` module. Calling this
|
||||||
|
@ -289,7 +470,7 @@ end
|
||||||
-- @param screen The screen to attach to
|
-- @param screen The screen to attach to
|
||||||
-- @see awful.placement
|
-- @see awful.placement
|
||||||
-- @deprecated awful.wibar.attach
|
-- @deprecated awful.wibar.attach
|
||||||
function awfulwibar.attach(wb, position, screen) --luacheck: no unused args
|
local function legacy_attach(wb, position, screen) --luacheck: no unused args
|
||||||
gdebug.deprecate("awful.wibar.attach is deprecated, use the 'attach' property"..
|
gdebug.deprecate("awful.wibar.attach is deprecated, use the 'attach' property"..
|
||||||
" of awful.placement. This method doesn't do anything anymore",
|
" of awful.placement. This method doesn't do anything anymore",
|
||||||
{deprecated_in=4}
|
{deprecated_in=4}
|
||||||
|
@ -318,7 +499,7 @@ end
|
||||||
-- directly.
|
-- directly.
|
||||||
-- @deprecated awful.wibar.align
|
-- @deprecated awful.wibar.align
|
||||||
-- @see awful.placement.align
|
-- @see awful.placement.align
|
||||||
function awfulwibar.align(wb, align, screen) --luacheck: no unused args
|
local function legacy_align(wb, align, screen) --luacheck: no unused args
|
||||||
if align == "center" then
|
if align == "center" then
|
||||||
gdebug.deprecate("awful.wibar.align(wb, 'center' is deprecated, use 'centered'", {deprecated_in=4})
|
gdebug.deprecate("awful.wibar.align(wb, 'center' is deprecated, use 'centered'", {deprecated_in=4})
|
||||||
align = "centered"
|
align = "centered"
|
||||||
|
@ -350,6 +531,9 @@ end
|
||||||
-- @tparam[opt=nil] table args
|
-- @tparam[opt=nil] table args
|
||||||
-- @tparam string args.position The position.
|
-- @tparam string args.position The position.
|
||||||
-- @tparam string args.stretch If the wibar need to be stretched to fill the screen.
|
-- @tparam string args.stretch If the wibar need to be stretched to fill the screen.
|
||||||
|
-- @tparam boolean args.restrict_workarea Allow or deny the tiled clients to cover the wibar.
|
||||||
|
-- @tparam string args.align How to align non-stretched wibars.
|
||||||
|
-- @tparam table|number args.margins The wibar margins.
|
||||||
--@DOC_wibox_constructor_COMMON@
|
--@DOC_wibox_constructor_COMMON@
|
||||||
-- @return The new wibar
|
-- @return The new wibar
|
||||||
-- @constructorfct awful.wibar
|
-- @constructorfct awful.wibar
|
||||||
|
@ -399,7 +583,7 @@ function awfulwibar.new(args)
|
||||||
-- The C code scans the table directly, so metatable magic cannot be used.
|
-- The C code scans the table directly, so metatable magic cannot be used.
|
||||||
for _, prop in ipairs {
|
for _, prop in ipairs {
|
||||||
"border_width", "border_color", "font", "opacity", "ontop", "cursor",
|
"border_width", "border_color", "font", "opacity", "ontop", "cursor",
|
||||||
"bgimage", "bg", "fg", "type", "stretch", "shape"
|
"bgimage", "bg", "fg", "type", "stretch", "shape", "margins", "align"
|
||||||
} do
|
} do
|
||||||
if (args[prop] == nil) and beautiful["wibar_"..prop] ~= nil then
|
if (args[prop] == nil) and beautiful["wibar_"..prop] ~= nil then
|
||||||
args[prop] = beautiful["wibar_"..prop]
|
args[prop] = beautiful["wibar_"..prop]
|
||||||
|
@ -408,25 +592,37 @@ function awfulwibar.new(args)
|
||||||
|
|
||||||
local w = wibox(args)
|
local w = wibox(args)
|
||||||
|
|
||||||
w.screen = screen
|
w._private.align = (args.align and align_map[args.align]) and args.align or "centered"
|
||||||
w._screen = screen --HACK When a screen is removed, then getbycoords wont work
|
|
||||||
w._stretch = args.stretch == nil and has_to_stretch or args.stretch
|
|
||||||
|
|
||||||
w.get_position = get_position
|
w._private.margins = {
|
||||||
w.set_position = set_position
|
left = 0,
|
||||||
|
right = 0,
|
||||||
|
top = 0,
|
||||||
|
bottom = 0
|
||||||
|
}
|
||||||
|
w._private.meta_margins = meta_margins(w)
|
||||||
|
|
||||||
w.get_stretch = get_stretch
|
w._private.restrict_workarea = true
|
||||||
w.set_stretch = set_stretch
|
|
||||||
w.remove = remove
|
|
||||||
|
|
||||||
if args.visible == nil then w.visible = true end
|
|
||||||
|
|
||||||
-- `w` needs to be inserted in `wiboxes` before reattach or its own offset
|
-- `w` needs to be inserted in `wiboxes` before reattach or its own offset
|
||||||
-- will not be taken into account by the "older" wibars when `reattach` is
|
-- will not be taken into account by the "older" wibars when `reattach` is
|
||||||
-- called. `skip_reattach` is required.
|
-- called. `skip_reattach` is required.
|
||||||
w:set_position(position, true)
|
w._private.skip_reattach = true
|
||||||
|
|
||||||
table.insert(wiboxes, w)
|
|
||||||
|
w.screen = screen
|
||||||
|
w._screen = screen --HACK When a screen is removed, then getbycoords wont work
|
||||||
|
w._stretch = args.stretch == nil and has_to_stretch or args.stretch
|
||||||
|
|
||||||
|
if args.visible == nil then w.visible = true end
|
||||||
|
|
||||||
|
gtable.crush(w, awfulwibar, true)
|
||||||
|
gtable.crush(w, args, false)
|
||||||
|
|
||||||
|
-- Now, let set_position behave normally.
|
||||||
|
w._private.skip_reattach = false
|
||||||
|
|
||||||
|
awfulwibar.set_margins(w, args.margins)
|
||||||
|
|
||||||
-- Force all the wibars to be moved
|
-- Force all the wibars to be moved
|
||||||
reattach(w)
|
reattach(w)
|
||||||
|
@ -454,6 +650,14 @@ function awfulwibar.mt:__call(...)
|
||||||
return awfulwibar.new(...)
|
return awfulwibar.new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function awfulwibar.mt:__index(_, k)
|
||||||
|
if k == "align" then
|
||||||
|
return legacy_align
|
||||||
|
elseif k == "attach" then
|
||||||
|
return legacy_attach
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--@DOC_wibox_COMMON@
|
--@DOC_wibox_COMMON@
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
--@DOC_object_COMMON@
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
-- @author Julien Danjou <julien@danjou.info>
|
-- @author Julien Danjou <julien@danjou.info>
|
||||||
-- @copyright 2008-2009 Julien Danjou
|
-- @copyright 2008-2009 Julien Danjou
|
||||||
-- @widgetmod awful.widget.button
|
-- @widgetmod awful.widget.button
|
||||||
|
-- @supermodule wibox.widget.imagebox
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -65,10 +66,6 @@ function button.mt:__call(...)
|
||||||
return button.new(...)
|
return button.new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(button, button.mt)
|
return setmetatable(button, button.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
---------------------------------------------------------------------------
|
||||||
--- Container showing the icon of a client.
|
--- Container showing the icon of a client.
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2017 Uli Schlachter
|
-- @copyright 2017 Uli Schlachter
|
||||||
-- @widgetmod awful.widget.clienticon
|
-- @widgetmod awful.widget.clienticon
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local base = require("wibox.widget.base")
|
local base = require("wibox.widget.base")
|
||||||
local surface = require("gears.surface")
|
local surface = require("gears.surface")
|
||||||
|
@ -123,10 +126,6 @@ client.connect_signal("property::icon", function(c)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(clienticon, {
|
return setmetatable(clienticon, {
|
||||||
__call = function(_, ...)
|
__call = function(_, ...)
|
||||||
return new(...)
|
return new(...)
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
-- @author Julien Danjou <julien@danjou.info>
|
-- @author Julien Danjou <julien@danjou.info>
|
||||||
-- @copyright 2008-2009 Julien Danjou
|
-- @copyright 2008-2009 Julien Danjou
|
||||||
-- @widgetmod awful.widget.launcher
|
-- @widgetmod awful.widget.launcher
|
||||||
|
-- @supermodule awful.widget.button
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -50,10 +51,6 @@ function launcher.mt:__call(...)
|
||||||
return launcher.new(...)
|
return launcher.new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(launcher, launcher.mt)
|
return setmetatable(launcher, launcher.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
-- @author Julien Danjou <julien@danjou.info>
|
-- @author Julien Danjou <julien@danjou.info>
|
||||||
-- @copyright 2009 Julien Danjou
|
-- @copyright 2009 Julien Danjou
|
||||||
-- @widgetmod awful.widget.layoutbox
|
-- @widgetmod awful.widget.layoutbox
|
||||||
|
-- @supermodule wibox.layout.fixed
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
-- @copyright 2010, 2018 Emmanuel Lepage Vallee
|
-- @copyright 2010, 2018 Emmanuel Lepage Vallee
|
||||||
-- @widgetmod awful.widget.layoutlist
|
-- @widgetmod awful.widget.layoutlist
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
|
|
||||||
local capi = {screen = screen, tag = tag}
|
local capi = {screen = screen, tag = tag}
|
||||||
|
@ -444,8 +445,4 @@ local function new(_, args)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(module, {__call = new})
|
return setmetatable(module, {__call = new})
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2017 Uli Schlachter
|
-- @copyright 2017 Uli Schlachter
|
||||||
-- @containermod awful.widget.only_on_screen
|
-- @containermod awful.widget.only_on_screen
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local type = type
|
local type = type
|
||||||
|
@ -129,10 +130,6 @@ capi.screen.connect_signal("property::outputs", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(only_on_screen, only_on_screen.mt)
|
return setmetatable(only_on_screen, only_on_screen.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
-- @copyright 2009 Julien Danjou
|
-- @copyright 2009 Julien Danjou
|
||||||
-- @copyright 2018 Aire-One
|
-- @copyright 2018 Aire-One
|
||||||
-- @widgetmod awful.widget.prompt
|
-- @widgetmod awful.widget.prompt
|
||||||
|
-- @supermodule wibox.container.background
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
--- The prompt foreground color.
|
--- The prompt foreground color.
|
||||||
|
@ -158,10 +159,6 @@ function widgetprompt.mt:__call(...)
|
||||||
return widgetprompt.new(...)
|
return widgetprompt.new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(widgetprompt, widgetprompt.mt)
|
return setmetatable(widgetprompt, widgetprompt.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -70,7 +70,8 @@ function gtk.get_theme_variables()
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
local _gtk_status, Gtk = pcall(function()
|
local _gtk_status, Gtk = pcall(function()
|
||||||
return require('lgi').Gtk
|
local lgi = require('lgi')
|
||||||
|
return lgi.require('Gtk', '3.0')
|
||||||
end)
|
end)
|
||||||
if not _gtk_status or not Gtk then
|
if not _gtk_status or not Gtk then
|
||||||
gears_debug.print_warning(
|
gears_debug.print_warning(
|
||||||
|
|
|
@ -1,628 +0,0 @@
|
||||||
--- Object oriented way to connect objects.
|
|
||||||
--
|
|
||||||
-- All AwesomeWM objects have a `emit_signal` method.
|
|
||||||
-- They allow to attach business logic to a property value change or random
|
|
||||||
-- types of events.
|
|
||||||
--
|
|
||||||
-- The default way to attach business logic to signals is to use `connect_signal`.
|
|
||||||
-- It allows to call a function when the signal is emitted. This remains the most
|
|
||||||
-- common way to perform a connection. However, it is very verbose to use that
|
|
||||||
-- construct alongside the declarative widget system. `gears.connection` is much
|
|
||||||
-- easier to integrate in such constructs:
|
|
||||||
--
|
|
||||||
-- @DOC_wibox_decl_doc_connection4_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- Limitations
|
|
||||||
-- ===========
|
|
||||||
--
|
|
||||||
-- * When used directly as a value to a declarative object
|
|
||||||
-- (`text = gears.connection{...}`), it is necessary to manually disconnect
|
|
||||||
-- the connectio if you want it to stop being auto-updated.
|
|
||||||
--
|
|
||||||
-- @author Emmanuel Lepage-Vallee <elv1313@gmail.com>
|
|
||||||
-- @copyright 2019-2020 Emmanuel Lepage-Vallee
|
|
||||||
-- @classmod gears.connection
|
|
||||||
|
|
||||||
local gobject = require("gears.object")
|
|
||||||
local gtable = require("gears.table")
|
|
||||||
local gtimer = require("gears.timer")
|
|
||||||
|
|
||||||
local module = {}
|
|
||||||
|
|
||||||
local function get_class(name)
|
|
||||||
if type(name) ~= "string" then return name end
|
|
||||||
|
|
||||||
return _G[name] or require(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function gen_get_children_by_id(self)
|
|
||||||
return function(id)
|
|
||||||
return (self._private.env_ids or {})[id] or {}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_env(_, cb)
|
|
||||||
local env, position = nil, nil
|
|
||||||
|
|
||||||
for i = 1, math.huge do
|
|
||||||
local name, val = debug.getupvalue(cb, i)
|
|
||||||
|
|
||||||
if name == "_ENV" then
|
|
||||||
env, position = val, i
|
|
||||||
break
|
|
||||||
elseif name == nil then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return env, position
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Expose the declarative tree `id`s to the callback as variables.
|
|
||||||
--
|
|
||||||
-- Note that this runs into a slight conflict with the ability to
|
|
||||||
-- place a widget in multiple trees. If it happens, then obviously
|
|
||||||
-- there will be `id` conflicts. That's solvable by only using
|
|
||||||
-- `get_children_by_id` in such situation. As of now, there is no
|
|
||||||
-- code to track `id`s across trees. So the callback will only have the
|
|
||||||
-- ids from the tree it was last added to.
|
|
||||||
local function extend_env(self)
|
|
||||||
local cb, ids = self._private.callbacks[1], self._private.env_ids
|
|
||||||
|
|
||||||
if (not cb) or (not ids) then return end
|
|
||||||
|
|
||||||
self._private.env_init = true
|
|
||||||
|
|
||||||
local env, position = get_env(self, cb)
|
|
||||||
|
|
||||||
if not env then return end
|
|
||||||
|
|
||||||
local gcbi = nil
|
|
||||||
|
|
||||||
local new_env = setmetatable({}, {
|
|
||||||
__index = function(_, key)
|
|
||||||
if key == "get_children_by_id" then
|
|
||||||
gcbi = gcbi or gen_get_children_by_id(self)
|
|
||||||
return gcbi
|
|
||||||
elseif ids[key] and #ids[key] == 1 then
|
|
||||||
return ids[key][1]
|
|
||||||
end
|
|
||||||
|
|
||||||
local v = env[key]
|
|
||||||
|
|
||||||
if v then return v end
|
|
||||||
|
|
||||||
return _G[key]
|
|
||||||
end,
|
|
||||||
__newindex = function(_, key, value)
|
|
||||||
_G[key] = value
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
debug.setupvalue(cb, position, new_env)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function set_target(self)
|
|
||||||
local p = self._private
|
|
||||||
|
|
||||||
local has_target = p.target
|
|
||||||
and p.initiate ~= false
|
|
||||||
|
|
||||||
local has_source = #p.sources >= 1
|
|
||||||
and #p.source_properties >= 1
|
|
||||||
|
|
||||||
extend_env(self)
|
|
||||||
|
|
||||||
for _, callback in ipairs(self._private.callbacks) do
|
|
||||||
local ret = callback(
|
|
||||||
p.sources[1],
|
|
||||||
p.target,
|
|
||||||
(p.sources[1] and p.source_properties[1]) and
|
|
||||||
p.sources[1][p.source_properties[1]] or nil
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.target_property and self._private.target then
|
|
||||||
self._private.target[self.target_property] = ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if p.target_method then
|
|
||||||
p.target[p.target_method]()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- There isn't enough information to initiate anything yet.
|
|
||||||
if not (has_target and has_source) then return end
|
|
||||||
|
|
||||||
if p.target_property then
|
|
||||||
p.target[p.target_property] = p.sources[1][p.source_properties[1]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- When all properties necessary to set the initial value are set.
|
|
||||||
local function initiate(self)
|
|
||||||
if self._private.initiating then return end
|
|
||||||
|
|
||||||
-- We don't know if properties will be overriden or if a callback/method
|
|
||||||
-- will be added. Better wait.
|
|
||||||
gtimer.delayed_call(function()
|
|
||||||
-- It might have been disabled since then.
|
|
||||||
if not self._private.enabled then return end
|
|
||||||
|
|
||||||
set_target(self)
|
|
||||||
self._private.initiating = false
|
|
||||||
end)
|
|
||||||
|
|
||||||
self._private.initiating = true
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:get_initiate()
|
|
||||||
return self._private.initiate
|
|
||||||
end
|
|
||||||
|
|
||||||
--- If the property should be set when the target object is defined.
|
|
||||||
--
|
|
||||||
-- It is **enabled** by default for convinience.
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_connection_initiate_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- @property initiate
|
|
||||||
-- @tparam[opt=true] boolean string initiate
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
function module:set_initiate(value)
|
|
||||||
if self._private.initiate == value then return end
|
|
||||||
self._private.initiate = value
|
|
||||||
self:emit_signal("property::initiate", value)
|
|
||||||
|
|
||||||
if value then
|
|
||||||
initiate(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Turn this connection on or off.
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_connection_enabled_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- @property enabled
|
|
||||||
-- @tparam boolean enabled
|
|
||||||
-- @see disconnect
|
|
||||||
-- @see reconnect
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
function module:get_enabled()
|
|
||||||
return self._private.enabled
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_enabled(value)
|
|
||||||
if value == self._private.enabled then return end
|
|
||||||
|
|
||||||
self._private.enabled = value
|
|
||||||
self:emit_signal("property::enabled", value)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- A list of source object signals.
|
|
||||||
--
|
|
||||||
-- @property signals
|
|
||||||
-- @tparam table signals
|
|
||||||
-- @propemits true false
|
|
||||||
-- @see signal
|
|
||||||
-- @see source_property
|
|
||||||
|
|
||||||
|
|
||||||
function module:get_signals()
|
|
||||||
return self._private.signals
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_signals(value)
|
|
||||||
self:disconnect()
|
|
||||||
self._private.signals = value
|
|
||||||
self:reconnect()
|
|
||||||
|
|
||||||
self:emit_signal("property::signal", value[1])
|
|
||||||
self:emit_signal("property::signals", value)
|
|
||||||
|
|
||||||
initiate(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:get_signal()
|
|
||||||
return self._private.signals < 2 and self._private.signals[1] or nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--- The (source) signal to monitor.
|
|
||||||
--
|
|
||||||
-- Note that `signals` and `source_property` are also provided to simplify
|
|
||||||
-- common use cases.
|
|
||||||
--
|
|
||||||
-- @property signal
|
|
||||||
-- @param string
|
|
||||||
-- @propemits true false
|
|
||||||
-- @see signals
|
|
||||||
-- @see source_property
|
|
||||||
|
|
||||||
function module:set_signal(signal)
|
|
||||||
self._private.signals = {signal}
|
|
||||||
end
|
|
||||||
|
|
||||||
--- The object for the right side object of the connection.
|
|
||||||
--
|
|
||||||
-- When used in a widget declarative tree, this is implicit and
|
|
||||||
-- is the parent object.
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_connection_target_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- @property target
|
|
||||||
-- @tparam gears.object target
|
|
||||||
-- @propemits true false
|
|
||||||
-- @see target_property
|
|
||||||
-- @see target_method
|
|
||||||
|
|
||||||
function module:get_target()
|
|
||||||
return self._private.target
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_target(target)
|
|
||||||
self._private.target = target
|
|
||||||
self:emit_signal("property::target", target)
|
|
||||||
initiate(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- The target object property to set when the source property changes.
|
|
||||||
--
|
|
||||||
-- @property target_property
|
|
||||||
-- @tparam string target_property
|
|
||||||
-- @propemits true false
|
|
||||||
-- @see target
|
|
||||||
-- @see target_method
|
|
||||||
-- @see source_property
|
|
||||||
|
|
||||||
function module:get_target_property()
|
|
||||||
return self._private.target_property
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_target_property(value)
|
|
||||||
self._private.target_property = value
|
|
||||||
self:emit_signal("property::target_property", value)
|
|
||||||
|
|
||||||
initiate(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Rather than use a property, call a method.
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_connection_method_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- @property target_method
|
|
||||||
-- @tparam string target_method
|
|
||||||
-- @propemits true false
|
|
||||||
-- @see target
|
|
||||||
-- @see target_property
|
|
||||||
|
|
||||||
function module:get_target_method()
|
|
||||||
return self._private.target_method
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_target_method(value)
|
|
||||||
self._private.target_method = value
|
|
||||||
self:emit_signal("property::target_method", value)
|
|
||||||
|
|
||||||
initiate(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Use a whole class rather than an object as source.
|
|
||||||
--
|
|
||||||
-- Many classes, like `client`, `tag`, `screen` and `naughty.notification`
|
|
||||||
-- provide class level signals. When any instance of those classes emit a
|
|
||||||
-- signal, it is forwarded to the class level signals.
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_connection_class_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- @property source_class
|
|
||||||
-- @tparam class|string source_class
|
|
||||||
-- @propemits true false
|
|
||||||
-- @see source
|
|
||||||
-- @see source_property
|
|
||||||
|
|
||||||
function module:set_source_class(class)
|
|
||||||
self:disconnect()
|
|
||||||
self._private.source_class = get_class(class)
|
|
||||||
self:reconnect()
|
|
||||||
self:emit_signal("property::source_class", self._private.source_class)
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:get_source_class()
|
|
||||||
return self._private.source_class
|
|
||||||
end
|
|
||||||
|
|
||||||
--- The source object (connection left hand side).
|
|
||||||
-- @property source
|
|
||||||
-- @tparam gears.object source
|
|
||||||
-- @propemits true false
|
|
||||||
-- @see sources
|
|
||||||
-- @see source_class
|
|
||||||
|
|
||||||
function module:get_source()
|
|
||||||
return self._private.sources[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_source(source)
|
|
||||||
|
|
||||||
self:disconnect()
|
|
||||||
self._private.sources = {source}
|
|
||||||
self:reconnect()
|
|
||||||
|
|
||||||
self:emit_signal("property::source", source)
|
|
||||||
self:emit_signal("property::sources", self._private.sources)
|
|
||||||
|
|
||||||
initiate(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- The source object(s)/class property.
|
|
||||||
--
|
|
||||||
-- @property source_property
|
|
||||||
-- @tparam string source_property
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
function module:get_source_property()
|
|
||||||
return #self._private.source_properties == 1 and
|
|
||||||
self._private.source_properties[1] or nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_source_property(prop)
|
|
||||||
self.source_properties = {prop}
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:get_source_properties()
|
|
||||||
return self._private.source_properties
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_source_properties(props)
|
|
||||||
self:disconnect()
|
|
||||||
self._private.source_properties = props
|
|
||||||
|
|
||||||
local signals = {}
|
|
||||||
|
|
||||||
self:reconnect()
|
|
||||||
|
|
||||||
for _, prop in ipairs(props) do
|
|
||||||
table.insert(signals, "property::"..prop)
|
|
||||||
end
|
|
||||||
|
|
||||||
self.signals = signals
|
|
||||||
|
|
||||||
self:emit_signal("property::source_property", props[1])
|
|
||||||
self:emit_signal("property::source_properties", props)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- A list of source objects (connection left hand side).
|
|
||||||
--
|
|
||||||
-- If many objects have the same signal, it's not necessary
|
|
||||||
-- to make multiple `gears.connection`. They can share the same.
|
|
||||||
--
|
|
||||||
-- @property sources
|
|
||||||
-- @tparam gears.object sources
|
|
||||||
-- @propemits true false
|
|
||||||
-- @see append_source_object
|
|
||||||
-- @see remove_source_object
|
|
||||||
|
|
||||||
function module:get_sources()
|
|
||||||
return self._private.sources
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_sources(sources)
|
|
||||||
if not sources then
|
|
||||||
sources = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
self:disconnect()
|
|
||||||
self._private.sources = sources
|
|
||||||
self:reconnect()
|
|
||||||
|
|
||||||
self:emit_signal("property::source", sources[1])
|
|
||||||
self:emit_signal("property::sources", sources)
|
|
||||||
|
|
||||||
initiate(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Add a source object.
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_connection_add_remove_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- @method append_source_object
|
|
||||||
-- @tparam gears.object obj The object.
|
|
||||||
-- @see sources
|
|
||||||
-- @see remove_source_object
|
|
||||||
-- @see has_source_object
|
|
||||||
-- @see source
|
|
||||||
-- @see sources
|
|
||||||
|
|
||||||
function module:append_source_object(obj)
|
|
||||||
if self:has_source_object(obj) then return end
|
|
||||||
|
|
||||||
table.insert(self._private.sources, obj)
|
|
||||||
self:emit_signal("property::sources", self._private.sources)
|
|
||||||
|
|
||||||
if #self._private.sources == 1 then
|
|
||||||
initiate(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Remove a source object.
|
|
||||||
--
|
|
||||||
-- @method remove_source_object
|
|
||||||
-- @tparam gears.object obj The object.
|
|
||||||
-- @see sources
|
|
||||||
-- @see append_source_object
|
|
||||||
-- @see has_source_object
|
|
||||||
-- @see source
|
|
||||||
-- @see sources
|
|
||||||
|
|
||||||
function module:remove_source_object(obj)
|
|
||||||
for k, o in ipairs(self._private.sources) do
|
|
||||||
if obj == o then
|
|
||||||
table.remove(self._private.sources, k)
|
|
||||||
self:emit_signal("property::sources", self._private.sources)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Return true when `obj` is already a source object.
|
|
||||||
--
|
|
||||||
-- @method module:has_source_object
|
|
||||||
-- @tparam gears.object obj The object.
|
|
||||||
-- @see append_source_object
|
|
||||||
-- @see remove_source_object
|
|
||||||
-- @see source
|
|
||||||
-- @see sources
|
|
||||||
|
|
||||||
function module:has_source_object(obj)
|
|
||||||
for _, o in ipairs(self._private.sources) do
|
|
||||||
if o == obj then return true end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--- A function called when the source changes.
|
|
||||||
--
|
|
||||||
--
|
|
||||||
-- @DOC_wibox_decl_doc_connection2_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- The callback arguments are:
|
|
||||||
--
|
|
||||||
-- callback = function(source, target, sig_arg1, ...)
|
|
||||||
-- /\ /\ /\ /\
|
|
||||||
-- | | | |
|
|
||||||
-- The client -| | | |
|
|
||||||
-- It will be the widget -| | |
|
|
||||||
-- Signal first argument, the client -| |
|
|
||||||
-- All other signal arguments -|
|
|
||||||
--
|
|
||||||
-- @property callback
|
|
||||||
-- @tparam function callback
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
function module:get_callback()
|
|
||||||
return self._private.callbacks[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_callback(cb)
|
|
||||||
self._private.callbacks = {cb}
|
|
||||||
|
|
||||||
self:emit_signal("property::callback", cb)
|
|
||||||
|
|
||||||
self._private.env_init = false
|
|
||||||
|
|
||||||
initiate(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- When used in a declarative tree, this will be the
|
|
||||||
-- object it is initiated from. The `key` can be a number,
|
|
||||||
-- in which case we do nothing. It can also be a string,
|
|
||||||
-- in which case it becomes `target_property`
|
|
||||||
function module:_set_declarative_handler(parent, key, ids)
|
|
||||||
self.target = parent
|
|
||||||
|
|
||||||
self._private.env_ids = ids
|
|
||||||
self._private.env_init = false
|
|
||||||
|
|
||||||
if type(key) == "string" then
|
|
||||||
self.target_property = key
|
|
||||||
end
|
|
||||||
|
|
||||||
initiate(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Disconnect this object.
|
|
||||||
--
|
|
||||||
-- @method disconnect
|
|
||||||
-- @see reconnect
|
|
||||||
|
|
||||||
function module:disconnect()
|
|
||||||
if self._private.source_class then
|
|
||||||
for _, sig in ipairs(self._private.signals) do
|
|
||||||
self._private.source_class.disconnect_signal(
|
|
||||||
sig, self._private._callback
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, src in ipairs(self._private.sources) do
|
|
||||||
for _, sig in ipairs(self._private.signals) do
|
|
||||||
src:disconnect_signal(sig, self._private._callback)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Reconnect this object.
|
|
||||||
--
|
|
||||||
-- @method reconnect
|
|
||||||
-- @see disconnect
|
|
||||||
|
|
||||||
function module:reconnect()
|
|
||||||
self:disconnect()
|
|
||||||
|
|
||||||
if self._private.source_class then
|
|
||||||
for _, sig in ipairs(self._private.signals) do
|
|
||||||
self._private.source_class.connect_signal(
|
|
||||||
sig, self._private._callback
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, src in ipairs(self._private.sources) do
|
|
||||||
for _, sig in ipairs(self._private.signals) do
|
|
||||||
src:connect_signal(sig, self._private._callback)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Create a new `gears.connection` object.
|
|
||||||
--
|
|
||||||
-- @constructorfct gears.connection
|
|
||||||
-- @tparam table args
|
|
||||||
-- @tparam boolean args.initiate If the property should be set when the target object is defined.
|
|
||||||
-- @tparam boolean args.enabled Turn this connection on or off.
|
|
||||||
-- @tparam boolean args.signals A list of source object signals.
|
|
||||||
-- @tparam string args.signal The (source) signal to monitor.
|
|
||||||
-- @tparam gears.object args.target The object for the right side object of the connection.
|
|
||||||
-- @tparam string args.target_property The target object property to set when the source property changes.
|
|
||||||
-- @tparam string args.target_method Rather than use a property, call a method.
|
|
||||||
-- @tparam class|string args.source_class Use a whole class rather than an object as source.
|
|
||||||
-- @tparam gears.object args.source The source object (connection left hand side).
|
|
||||||
-- @tparam string args.source_property The source object(s)/class property.
|
|
||||||
-- @tparam gears.object args.sources A list of source objects (connection left hand side).
|
|
||||||
-- @tparam function args.callback A function called when the source changes.
|
|
||||||
|
|
||||||
local function new(_, args)
|
|
||||||
local self = gobject {
|
|
||||||
enable_properties = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
rawset(self, "_private", {
|
|
||||||
enabled = true,
|
|
||||||
signals = {},
|
|
||||||
initiate = true,
|
|
||||||
sources = {},
|
|
||||||
callbacks = {},
|
|
||||||
source_properties = {},
|
|
||||||
target = nil,
|
|
||||||
_callback = function()
|
|
||||||
if not self._private.enabled then return end
|
|
||||||
|
|
||||||
set_target(self)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
gtable.crush(self, module, true )
|
|
||||||
gtable.crush(self, args , false)
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(module, {__call = new})
|
|
|
@ -172,28 +172,39 @@ end
|
||||||
-- @tparam string path The directory to search.
|
-- @tparam string path The directory to search.
|
||||||
-- @tparam[opt] table exts Specific extensions to limit the search to. eg:`{ "jpg", "png" }`
|
-- @tparam[opt] table exts Specific extensions to limit the search to. eg:`{ "jpg", "png" }`
|
||||||
-- If ommited, all files are considered.
|
-- If ommited, all files are considered.
|
||||||
|
-- @tparam[opt=false] boolean absolute_path Return the absolute path instead of the filename.
|
||||||
-- @treturn string|nil A randomly selected filename from the specified path (with
|
-- @treturn string|nil A randomly selected filename from the specified path (with
|
||||||
-- a specified extension if required) or nil if no suitable file is found.
|
-- a specified extension if required) or nil if no suitable file is found. If `absolute_path`
|
||||||
|
-- is set, then a path is returned instead of a file name.
|
||||||
-- @staticfct gears.filesystem.get_random_file_from_dir
|
-- @staticfct gears.filesystem.get_random_file_from_dir
|
||||||
function filesystem.get_random_file_from_dir(path, exts)
|
function filesystem.get_random_file_from_dir(path, exts, absolute_path)
|
||||||
local files, valid_exts = {}, {}
|
local files, valid_exts = {}, {}
|
||||||
|
|
||||||
-- Transforms { "jpg", ... } into { [jpg] = #, ... }
|
-- Transforms { "jpg", ... } into { [jpg] = #, ... }
|
||||||
if exts then for i, j in ipairs(exts) do valid_exts[j:lower()] = i end end
|
if exts then for i, j in ipairs(exts) do valid_exts[j:lower():gsub("^[.]", "")] = i end end
|
||||||
|
|
||||||
-- Build a table of files from the path with the required extensions
|
-- Build a table of files from the path with the required extensions
|
||||||
local file_list = Gio.File.new_for_path(path):enumerate_children("standard::*", 0)
|
local file_list = Gio.File.new_for_path(path):enumerate_children("standard::*", 0)
|
||||||
|
|
||||||
|
-- This will happen when the directory doesn't exist.
|
||||||
|
if not file_list then return nil end
|
||||||
|
|
||||||
for file in function() return file_list:next_file() end do
|
for file in function() return file_list:next_file() end do
|
||||||
if file:get_file_type() == "REGULAR" then
|
if file:get_file_type() == "REGULAR" then
|
||||||
local file_name = file:get_display_name()
|
local file_name = file:get_display_name()
|
||||||
|
|
||||||
if not exts or valid_exts[file_name:lower():match(".+%.(.*)$") or ""] then
|
if not exts or valid_exts[file_name:lower():match(".+%.(.*)$") or ""] then
|
||||||
table.insert(files, file_name)
|
table.insert(files, file_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if #files == 0 then return nil end
|
||||||
|
|
||||||
-- Return a randomly selected filename from the file table
|
-- Return a randomly selected filename from the file table
|
||||||
return #files > 0 and files[math.random(#files)] or nil
|
local file = files[math.random(#files)]
|
||||||
|
|
||||||
|
return absolute_path and (path:gsub("[/]*$", "") .. "/" .. file) or file
|
||||||
end
|
end
|
||||||
|
|
||||||
return filesystem
|
return filesystem
|
||||||
|
|
|
@ -23,9 +23,6 @@ return
|
||||||
string = require("gears.string");
|
string = require("gears.string");
|
||||||
sort = require("gears.sort");
|
sort = require("gears.sort");
|
||||||
filesystem = require("gears.filesystem");
|
filesystem = require("gears.filesystem");
|
||||||
reactive = require("gears.reactive");
|
|
||||||
connection = require("gears.connection");
|
|
||||||
watcher = require("gears.watcher");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -46,9 +46,11 @@ function object.add_signal()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Connect to a signal.
|
--- Connect to a signal.
|
||||||
|
--
|
||||||
--@DOC_text_gears_object_signal_EXAMPLE@
|
--@DOC_text_gears_object_signal_EXAMPLE@
|
||||||
-- @tparam string name The name of the signal
|
--
|
||||||
-- @tparam function func The callback to call when the signal is emitted
|
-- @tparam string name The name of the signal.
|
||||||
|
-- @tparam function func The callback to call when the signal is emitted.
|
||||||
-- @method connect_signal
|
-- @method connect_signal
|
||||||
function object:connect_signal(name, func)
|
function object:connect_signal(name, func)
|
||||||
assert(type(func) == "function", "callback must be a function, got: " .. type(func))
|
assert(type(func) == "function", "callback must be a function, got: " .. type(func))
|
||||||
|
@ -92,10 +94,16 @@ local function make_the_gc_obey(func)
|
||||||
return func
|
return func
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Connect to a signal weakly. This allows the callback function to be garbage
|
--- Connect to a signal weakly.
|
||||||
-- collected and automatically disconnects the signal when that happens.
|
--
|
||||||
-- @tparam string name The name of the signal
|
-- This allows the callback function to be garbage collected and
|
||||||
-- @tparam function func The callback to call when the signal is emitted
|
-- automatically disconnects the signal when that happens.
|
||||||
|
-- **Warning:**
|
||||||
|
-- Only use this function if you really, really, really know what you
|
||||||
|
-- are doing.
|
||||||
|
--
|
||||||
|
-- @tparam string name The name of the signal.
|
||||||
|
-- @tparam function func The callback to call when the signal is emitted.
|
||||||
-- @method weak_connect_signal
|
-- @method weak_connect_signal
|
||||||
function object:weak_connect_signal(name, func)
|
function object:weak_connect_signal(name, func)
|
||||||
assert(type(func) == "function", "callback must be a function, got: " .. type(func))
|
assert(type(func) == "function", "callback must be a function, got: " .. type(func))
|
||||||
|
@ -104,9 +112,9 @@ function object:weak_connect_signal(name, func)
|
||||||
sig.weak[func] = make_the_gc_obey(func)
|
sig.weak[func] = make_the_gc_obey(func)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Disonnect to a signal.
|
--- Disonnect from a signal.
|
||||||
-- @tparam string name The name of the signal
|
-- @tparam string name The name of the signal.
|
||||||
-- @tparam function func The callback that should be disconnected
|
-- @tparam function func The callback that should be disconnected.
|
||||||
-- @method disconnect_signal
|
-- @method disconnect_signal
|
||||||
function object:disconnect_signal(name, func)
|
function object:disconnect_signal(name, func)
|
||||||
local sig = find_signal(self, name)
|
local sig = find_signal(self, name)
|
||||||
|
@ -118,8 +126,8 @@ end
|
||||||
--
|
--
|
||||||
-- @tparam string name The name of the signal
|
-- @tparam string name The name of the signal
|
||||||
-- @param ... Extra arguments for the callback functions. Each connected
|
-- @param ... Extra arguments for the callback functions. Each connected
|
||||||
-- function receives the object as first argument and then any extra arguments
|
-- function receives the object as first argument and then any extra
|
||||||
-- that are given to emit_signal()
|
-- arguments that are given to emit_signal()
|
||||||
-- @method emit_signal
|
-- @method emit_signal
|
||||||
function object:emit_signal(name, ...)
|
function object:emit_signal(name, ...)
|
||||||
local sig = find_signal(self, name)
|
local sig = find_signal(self, name)
|
||||||
|
|
|
@ -1,656 +0,0 @@
|
||||||
--- Utility module to convert functions to Excel like objects.
|
|
||||||
--
|
|
||||||
-- When converting a function into a `gears.reactive` object, all
|
|
||||||
-- properties accessed by the function are monitored for changes.
|
|
||||||
--
|
|
||||||
-- When such a change is detected, the `property::value` signal is emitted. When
|
|
||||||
-- used within a widget declarative construct, the property it is attached
|
|
||||||
-- to will also be automatically updated.
|
|
||||||
--
|
|
||||||
-- Theory
|
|
||||||
-- ======
|
|
||||||
--
|
|
||||||
-- ![Client geometry](../images/gears_reactive.svg)
|
|
||||||
--
|
|
||||||
-- To use this module and, more importantly, to understand how to write something
|
|
||||||
-- that actually works based on it, some background is required. Most AwesomeWM
|
|
||||||
-- objects are based on the `gears.object` module or it's `C` equivalent. Those
|
|
||||||
-- objects have "properties". Behind the scene, they have getters, setters and
|
|
||||||
-- a signal to notify the value changed.
|
|
||||||
--
|
|
||||||
-- `gears.reactive` adds a firewall like sandbox around the function. It
|
|
||||||
-- intercept any `gears.object` instance trying to cross the sandbox boundary
|
|
||||||
-- and replace them with proxy objects. Those proxies have built-in
|
|
||||||
-- introspection code to detect how they are used. This is then converted into
|
|
||||||
-- a list of objects and signals to monitor. Once one of the monitored object
|
|
||||||
-- emits one of the monitored signal, then the whole function is re-evaluated.
|
|
||||||
-- Each time the function is evaluated, the "target" properties are updated. The
|
|
||||||
-- reactive function result or any call to external function from within goes
|
|
||||||
-- through the firewall again and any proxies are removed.
|
|
||||||
--
|
|
||||||
-- That design has one big limitation. It cannot detect any changes which are
|
|
||||||
-- not directly part of `gears.object` instance. You cannot use random tables
|
|
||||||
-- and expect the function to be called when it's content change. To work
|
|
||||||
-- around this, it is recommanded to make "hub" objects to store the data used
|
|
||||||
-- within the reactive function.
|
|
||||||
--
|
|
||||||
-- Recommanded usage
|
|
||||||
-- =================
|
|
||||||
--
|
|
||||||
-- The best way to use `gears.reactive` is when the value used within the
|
|
||||||
-- expressions are part of other objects. It can be a `gears.watcher`, but
|
|
||||||
-- it can also be a custom object:
|
|
||||||
--
|
|
||||||
-- @DOC_wibox_widget_declarative_reactive_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- Limitations
|
|
||||||
-- ===========
|
|
||||||
--
|
|
||||||
-- `gears.reactive` is pushing Lua way beyond what it has been designed for.
|
|
||||||
-- Because of this, there is some limitations.
|
|
||||||
--
|
|
||||||
-- * This module will **NOT** try to track the change of other
|
|
||||||
-- functions and methods called by the expression. It is **NOT** recursive
|
|
||||||
-- and only the top level properties are tracked. This is a feature, not a
|
|
||||||
-- bug. If it was recursive, this list of limitations or gotchas would be
|
|
||||||
-- endless.
|
|
||||||
-- * This only works with `gears.object` and Core API objects which implement
|
|
||||||
-- the `property::*****` signals correctly. If it is a regular Lua table
|
|
||||||
-- or the property signals are incorrectly used, the value changes cannot
|
|
||||||
-- be detected. If you find something that should work, but doesn't in
|
|
||||||
-- one of the AwesomeWM API, [please report a bug](https://github.com/awesomeWM/awesome/issues/new).
|
|
||||||
-- * More generally, when making a custom `gears.object` with custom setters,
|
|
||||||
-- it is the programmer responsibility to emit the signal. It is also
|
|
||||||
-- required to only emit those signals when the property actually changes to
|
|
||||||
-- avoid an unecessary re-evaluations.
|
|
||||||
-- * Changing the type of the variables accessed by the reactive function
|
|
||||||
-- (its "upvalues") after the reactive expression has been created wont
|
|
||||||
-- be detected. It will cause missed updates and, potentially, hard to debug
|
|
||||||
-- Lua errors within the proxy code itself.
|
|
||||||
-- * Internally, the engine tries its best to prevent the internal proxy objects
|
|
||||||
-- to leak out the sandbox. However this cannot be perfect, at least not
|
|
||||||
-- without adding limitation elsewhere. It is probably worth reporting a bug if
|
|
||||||
-- you encounter such an issue. But set your expectations, not all corner case
|
|
||||||
-- can be fixed.
|
|
||||||
-- * Don't use rawset in the expression.
|
|
||||||
-- * If the return value is a table, only the first 3 nesting levels are sanitized.
|
|
||||||
-- Avoid using nested tables in the returned value if you can. `gears.object`
|
|
||||||
-- instances *should* be fine.
|
|
||||||
-- * There is currently no way to disable a reactive expression once it's
|
|
||||||
-- been defined. This may change eventually.
|
|
||||||
-- * Rio Lua 5.1 (not LuaJIT 2.1) is currently not support. If you need it,
|
|
||||||
-- [please report a bug](https://github.com/awesomeWM/awesome/issues/new).
|
|
||||||
--
|
|
||||||
-- @author Emmanuel Lepage-Vallee <elv1313@gmail.com>
|
|
||||||
-- @copyright 2017-2020 Emmanuel Lepage-Vallee
|
|
||||||
-- @classmod gears.reactive
|
|
||||||
|
|
||||||
-- This file is provided under the BSD 2 clause license since it is better than
|
|
||||||
-- any existing implementation (most of which only support Lua 5.1).
|
|
||||||
--
|
|
||||||
-- Copyright 2020 Emmanuel Lepage-Vallee <elv1313@gmail.com>
|
|
||||||
--
|
|
||||||
-- Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
-- are permitted provided that the following conditions are met:
|
|
||||||
--
|
|
||||||
-- 1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
-- list of conditions and the following disclaimer.
|
|
||||||
--
|
|
||||||
-- 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
-- this list of conditions and the following disclaimer in the documentation
|
|
||||||
-- and/or other materials provided with the distribution.
|
|
||||||
--
|
|
||||||
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
||||||
-- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
||||||
-- IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
local gobject = require("gears.object")
|
|
||||||
local gtable = require("gears.table")
|
|
||||||
local gtimer = require("gears.timer")
|
|
||||||
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
|
||||||
|
|
||||||
-- It's only possible to know when something changes if it has signals.
|
|
||||||
local function is_gears_object(input)
|
|
||||||
return type(input) == "table" and rawget(input, "emit_signal")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If the sandbox proxies were to leave the reactive expressions, bad
|
|
||||||
-- things would happen. Catch functions and constructors to make sure
|
|
||||||
-- we strip away the proxy.
|
|
||||||
local function is_callable(object)
|
|
||||||
local t = type(object)
|
|
||||||
|
|
||||||
if t == "function" then return true end
|
|
||||||
|
|
||||||
if not (t == "table" or t == "userdata") then return false end
|
|
||||||
|
|
||||||
local mt = getmetatable(object)
|
|
||||||
|
|
||||||
return mt and mt.__call ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the upvalue current value or cached value.
|
|
||||||
local function get_upvalue(proxy_md)
|
|
||||||
if type(proxy_md) ~= "table" then return proxy_md end
|
|
||||||
|
|
||||||
return proxy_md._reactive.getter and
|
|
||||||
proxy_md._reactive.getter() or proxy_md._reactive.input
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Attempt to remove the proxy references. There might be loops
|
|
||||||
-- and stackoverflows, so it will only go 3 level deep. With some
|
|
||||||
-- luck, the result wont be a table. Even then, hopefully it wont
|
|
||||||
-- contain "raw" sub-tables. If it contains `gears.object`, it's
|
|
||||||
-- safe since they will be proxied.
|
|
||||||
local sanitize_return = nil
|
|
||||||
sanitize_return = function(input, depth)
|
|
||||||
if (depth or 0) > 3 then return input end
|
|
||||||
|
|
||||||
-- The first `input` is always a table, so it is safe to call `pairs`.
|
|
||||||
for k, v in pairs(input) do
|
|
||||||
local t = type(v)
|
|
||||||
|
|
||||||
if t == "table" then
|
|
||||||
if v._reactive then
|
|
||||||
-- No need to go deeper unless the user used rawset.
|
|
||||||
input[k] = get_upvalue(v)
|
|
||||||
else
|
|
||||||
-- It's an unproxied raw table, dig further.
|
|
||||||
sanitize_return(v, (depth or 1) + 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return input
|
|
||||||
end
|
|
||||||
|
|
||||||
-- The actual code that connects the gears.object signals.
|
|
||||||
local function add_key(self, key)
|
|
||||||
if type(key) ~= "string" or not is_gears_object(get_upvalue(self)) then return end
|
|
||||||
|
|
||||||
local input, objs = get_upvalue(self), self._reactive.origin.objects
|
|
||||||
|
|
||||||
objs[input] = objs[input] or {}
|
|
||||||
|
|
||||||
if objs[input][key] then return end
|
|
||||||
|
|
||||||
objs[input][key] = true
|
|
||||||
|
|
||||||
-- Prefer the `weak` version to avoid memory leaks. If nothing is holding a
|
|
||||||
-- reference, then the value wont change. Yes, there is some hairy corner cases.
|
|
||||||
-- Yes, the expression proxy itself is holding a strong reference so this wont
|
|
||||||
-- get used very often.
|
|
||||||
if input.weak_connect_signal then
|
|
||||||
input:weak_connect_signal("property::"..key, self._reactive.origin.callback)
|
|
||||||
else
|
|
||||||
input:connect_signal("property::"..key, self._reactive.origin.callback)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function gen_proxy_md(input, origin, getter)
|
|
||||||
assert(input and origin)
|
|
||||||
return {
|
|
||||||
input = (not getter) and input or nil,
|
|
||||||
getter = getter,
|
|
||||||
children = {},
|
|
||||||
properties = {},
|
|
||||||
origin = origin
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local create_proxy = nil
|
|
||||||
|
|
||||||
-- Un-proxy the function input and proxy its output.
|
|
||||||
local function proxy_call(self, ...)
|
|
||||||
local newargs, newrets = {}, {}
|
|
||||||
|
|
||||||
-- Remove the proxies before calling the function.
|
|
||||||
for _, v in ipairs{...} do
|
|
||||||
if type(v) == "table" and v._reactive then
|
|
||||||
v = get_upvalue(v)
|
|
||||||
end
|
|
||||||
table.insert(newargs, v)
|
|
||||||
end
|
|
||||||
|
|
||||||
local rets
|
|
||||||
|
|
||||||
if #newargs > 0 then
|
|
||||||
rets = {get_upvalue(self)(unpack(newargs))}
|
|
||||||
else
|
|
||||||
rets = {get_upvalue(self)(nil)}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add new proxies to make sure changes are detected if something
|
|
||||||
-- like `a_function().foo` is used. Since we still have legacy accessor
|
|
||||||
-- implemented as method (t:clients(), c:tags(), etc), this is actually
|
|
||||||
-- likely to happen.
|
|
||||||
for _, v in ipairs(rets) do
|
|
||||||
local ret, _ = create_proxy(v, self._reactive.origin)
|
|
||||||
table.insert(newrets, ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
return unpack(newrets)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Build a tree of proxies or return the primitives.
|
|
||||||
local function proxy_index(self, key)
|
|
||||||
local up = get_upvalue(self)
|
|
||||||
|
|
||||||
-- Since it would otherwise print a cryptic error.
|
|
||||||
assert(
|
|
||||||
type(up) == "table",
|
|
||||||
"Trying to index a "..type(up).." value in a `gears.reactive` expression."
|
|
||||||
)
|
|
||||||
|
|
||||||
local upk = up[key]
|
|
||||||
|
|
||||||
-- Connect.
|
|
||||||
add_key(self, key)
|
|
||||||
|
|
||||||
local ret, is_proxy = create_proxy(upk, self._reactive.origin)
|
|
||||||
|
|
||||||
-- Always query the non-proxy upvalue. We cannot detect if they
|
|
||||||
-- change.
|
|
||||||
if is_proxy then
|
|
||||||
rawset(self, key, ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set valuers trough the proxy.
|
|
||||||
local function proxy_newindex(self, key, value)
|
|
||||||
rawset(self, key, create_proxy(value, self._reactive.origin))
|
|
||||||
|
|
||||||
-- Strip the proxy before setting the value on the original object.
|
|
||||||
if type(value) == "table" and value._reactive then
|
|
||||||
value = get_upvalue(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
local v = get_upvalue(self)
|
|
||||||
|
|
||||||
v[key] = value
|
|
||||||
|
|
||||||
-- Connect.
|
|
||||||
if is_gears_object(v) and not self._reactive.origin.objects[self][key] then
|
|
||||||
add_key(self, key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- It's possible that multiple proxy points to the same value.
|
|
||||||
-- While we could make a large map of all proxies, it's simpler
|
|
||||||
-- to just implement __eq/__lt/__le and be done.
|
|
||||||
local function proxy_equal(a, b)
|
|
||||||
a, b = get_upvalue(a), get_upvalue(b)
|
|
||||||
|
|
||||||
return a == b
|
|
||||||
end
|
|
||||||
|
|
||||||
local function proxy_lesser(a, b)
|
|
||||||
a, b = get_upvalue(a), get_upvalue(b)
|
|
||||||
|
|
||||||
return a < b
|
|
||||||
end
|
|
||||||
|
|
||||||
local function proxy_lesser_equal(a, b)
|
|
||||||
a, b = get_upvalue(a), get_upvalue(b)
|
|
||||||
|
|
||||||
return a <= b
|
|
||||||
end
|
|
||||||
|
|
||||||
local function proxy_tostring(o)
|
|
||||||
return tostring(get_upvalue(o))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Wrap tables and functions into a proxy object.
|
|
||||||
create_proxy = function(input, origin, getter)
|
|
||||||
local t = type(input)
|
|
||||||
local is_call = is_callable(input)
|
|
||||||
|
|
||||||
-- Everything but the tables are immutable.
|
|
||||||
if t ~= "table" and not is_call then return input, false end
|
|
||||||
|
|
||||||
-- Remove any foreign proxy.
|
|
||||||
if t ~= "function" and input._reactive and input._reactive.origin ~= origin then
|
|
||||||
input = get_upvalue(input)
|
|
||||||
end
|
|
||||||
|
|
||||||
return setmetatable({_reactive = gen_proxy_md(input, origin, getter)}, {
|
|
||||||
__index = proxy_index,
|
|
||||||
__newindex = proxy_newindex,
|
|
||||||
__call = proxy_call,
|
|
||||||
__eq = proxy_equal,
|
|
||||||
__lt = proxy_lesser,
|
|
||||||
__le = proxy_lesser_equal,
|
|
||||||
__tostring = proxy_tostring
|
|
||||||
}), true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a "fake" upvalue ref because otherwise the sandbox could
|
|
||||||
-- "leak" and set upvalue in actual code outside of the function.
|
|
||||||
-- By "leak", I mean the proxy would become visible from outside
|
|
||||||
-- of the sandbox by the reactive expression "siblings" sharing the
|
|
||||||
-- same environment.
|
|
||||||
local function copy_upvalue_reference(loaded, idx, value)
|
|
||||||
-- Make sure it has a fresh upvalue reference. Something that
|
|
||||||
-- we are sure cannot be shared with something else.
|
|
||||||
local function fake_upvalue_env()
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
|
|
||||||
debug.upvaluejoin(loaded, idx, fake_upvalue_env, 1) --luacheck: ignore
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a meta-getter for the original `fct` upvalue at index `idx`.
|
|
||||||
-- This will allow all the other code to dynamically get a "current" version
|
|
||||||
-- of the upvalue rather than something from the cache. It will also avoid
|
|
||||||
-- having to store a strong reference to the original value.
|
|
||||||
local function create_upvalue_getter(fct, idx)
|
|
||||||
local placeholder = nil
|
|
||||||
|
|
||||||
local function fake_upvalue_getter()
|
|
||||||
return placeholder
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Using this, each time `fake_upvalue_getter` is called, it
|
|
||||||
-- will return the current upvalue. This means we don't have to
|
|
||||||
-- cache it. Thus, the cache cannot get outdated. The drawback if
|
|
||||||
-- that if the type changes from an object to a primitive, it
|
|
||||||
-- will explode.
|
|
||||||
debug.upvaluejoin(fake_upvalue_getter, 1, fct, idx) --luacheck: ignore
|
|
||||||
|
|
||||||
return fake_upvalue_getter
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a copy of the function and replace it's environment.
|
|
||||||
local function sandbox(fct, env, vars, values)
|
|
||||||
-- We must serialize the function for several reasons. First of all, it
|
|
||||||
-- might get wrapped into multiple `gears.reactive` objects (which we should
|
|
||||||
-- handle differently, but currently allow) or share the "upvalue environemnt"
|
|
||||||
-- (this variables from the upper execution context and stack frames).
|
|
||||||
--
|
|
||||||
-- For example, if 2 function both access the variables "foo" and "bar"
|
|
||||||
-- from the global context, they might end up with the same execution
|
|
||||||
-- environment. If we didn't create a copy, calling `debug.upvaluejoin`
|
|
||||||
-- woulc affect both functions.
|
|
||||||
local dump = string.dump(fct)
|
|
||||||
local loaded = load(dump, nil, nil, env)
|
|
||||||
|
|
||||||
-- It doesn't seem possible to "just remove" the upvalues. It's not possible
|
|
||||||
-- to have the catch-all in the metatable. It would have been nicer since this
|
|
||||||
-- code is redundant with the metatable (which are still needed for _G and _ENV
|
|
||||||
-- content).
|
|
||||||
for name, k in pairs(vars) do
|
|
||||||
if is_callable(values[name]) or type(values[name]) == "table" then
|
|
||||||
-- For table, functions and objects, use a proxy upvalue.
|
|
||||||
copy_upvalue_reference(
|
|
||||||
loaded,
|
|
||||||
k,
|
|
||||||
create_proxy(values[name], env.__origin, create_upvalue_getter(fct ,k))
|
|
||||||
)
|
|
||||||
else
|
|
||||||
-- For string, booleans, numbers and function, use the real upvalue.
|
|
||||||
-- This means if it is changed by something else, the sandboxed
|
|
||||||
-- copy sees the change.
|
|
||||||
debug.upvaluejoin(loaded, k, fct, k) --luacheck: ignore
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return loaded
|
|
||||||
end
|
|
||||||
|
|
||||||
-- `getfenv` and `setfenv` would simplify this a lot, but are not
|
|
||||||
-- present in newer versions of Lua. Technically, we could have 3
|
|
||||||
-- implementation optimized for each Lua versions, but this one
|
|
||||||
-- seems to be portable (until now...). So while the performance
|
|
||||||
-- isn't as good as it could be, it's maintainable.
|
|
||||||
local function getfenv_compat(fct, root)
|
|
||||||
local vars, soft_env = {}, {}
|
|
||||||
|
|
||||||
local origin = {
|
|
||||||
objects = {},
|
|
||||||
callback = function()
|
|
||||||
root:emit_signal("property::value")
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
for i = 1, math.huge do
|
|
||||||
local name, val = debug.getupvalue(fct, i)
|
|
||||||
|
|
||||||
if name == nil then break end
|
|
||||||
|
|
||||||
soft_env[name] = val
|
|
||||||
|
|
||||||
if not vars[name] then
|
|
||||||
vars[name] = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create the sandbox.
|
|
||||||
local self = {__origin = origin}
|
|
||||||
local sandboxed = sandbox(fct, self, vars, soft_env)
|
|
||||||
|
|
||||||
return setmetatable(self, {
|
|
||||||
__index = function(_, key)
|
|
||||||
if _G[key] then
|
|
||||||
return create_proxy(_G[key], origin)
|
|
||||||
end
|
|
||||||
|
|
||||||
return soft_env[key]
|
|
||||||
end,
|
|
||||||
__newindex = function(_, key, value)
|
|
||||||
-- This `if` might be dead code.
|
|
||||||
if vars[key] then
|
|
||||||
debug.setupvalue(sandboxed, vars[key], value)
|
|
||||||
|
|
||||||
-- Do not try to disconnect the old one. It would make the code too complex.
|
|
||||||
if (not soft_env[key]) or get_upvalue(soft_env[key]) ~= value then
|
|
||||||
soft_env[key] = create_proxy(value, origin)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
rawset(vars, key, create_proxy(value, origin))
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function()
|
|
||||||
return unpack(sanitize_return({sandboxed()}))
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local module = {}
|
|
||||||
|
|
||||||
local function real_set_value(self, force)
|
|
||||||
if self._private.delayed_started and not force then return end
|
|
||||||
|
|
||||||
local function value_transaction()
|
|
||||||
-- If `get_value` was called, then this transaction is no longer
|
|
||||||
-- pending.
|
|
||||||
if not self._private.delayed_started then return end
|
|
||||||
|
|
||||||
-- Reset the delay in case the setter causes the expression to
|
|
||||||
-- change.
|
|
||||||
self._private.delayed_started = false
|
|
||||||
|
|
||||||
self._private.value = self._private.origin()
|
|
||||||
self._private.evaluated = true
|
|
||||||
|
|
||||||
for _, target in pairs(self._private.targets) do
|
|
||||||
local obj, property = target[1], target[2]
|
|
||||||
obj[property] = self._private.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self._private.delayed and not force then
|
|
||||||
gtimer.delayed_call(value_transaction)
|
|
||||||
self._private.delayed_started = true
|
|
||||||
else
|
|
||||||
self._private.delayed_started = true
|
|
||||||
value_transaction()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- A function which will be evaluated each time its content changes.
|
|
||||||
--
|
|
||||||
-- @property expression
|
|
||||||
-- @param function
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
--- The current value of the expression.
|
|
||||||
-- @property value
|
|
||||||
-- @propemits false false
|
|
||||||
|
|
||||||
--- Only evaluate once per event loop iteration.
|
|
||||||
--
|
|
||||||
-- In most case this is a simple performance win, but there is some
|
|
||||||
-- case where you might want the expression to be evaluated each time
|
|
||||||
-- one of the upvalue "really" change rather than batch them. This
|
|
||||||
-- option is enabled by default.
|
|
||||||
--
|
|
||||||
-- @property delayed
|
|
||||||
-- @tparam[opt=true] boolean delayed
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
function module:get_delayed()
|
|
||||||
return self._private.delayed
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_delayed(value)
|
|
||||||
if value == self._private.delayed then return end
|
|
||||||
|
|
||||||
self._private.delayed = value
|
|
||||||
self:emit_signal("property::delayed", value)
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_expression(value)
|
|
||||||
self:disconnect()
|
|
||||||
self._private.origin = getfenv_compat(value, self)
|
|
||||||
|
|
||||||
self:connect_signal("property::value", real_set_value)
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:get_value()
|
|
||||||
-- This will call `real_set_value`.
|
|
||||||
if (not self._private.evaluated) and self._private.origin then
|
|
||||||
self:refresh()
|
|
||||||
end
|
|
||||||
|
|
||||||
if self._private.delayed_started then
|
|
||||||
real_set_value(self, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self._private.value
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_value()
|
|
||||||
assert(false, "A value cannot be set on a `gears.reactive` instance.")
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Disconnect all expression signals.
|
|
||||||
--
|
|
||||||
-- @method disconnect
|
|
||||||
|
|
||||||
function module:disconnect()
|
|
||||||
if self._private.origin then
|
|
||||||
for obj, properties in pairs(self._private.origin.__origin.objects) do
|
|
||||||
for property in pairs(properties) do
|
|
||||||
obj:disconnect_signal("property::"..property, self._private.origin.__origin.callback)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Recompute the expression.
|
|
||||||
--
|
|
||||||
-- When the expression uses a non-object upvalue, the changes cannot
|
|
||||||
-- be auto-retected. Calling `:refresh()` will immediatly recompute the
|
|
||||||
-- expression.
|
|
||||||
--
|
|
||||||
-- @method refresh
|
|
||||||
|
|
||||||
function module:refresh()
|
|
||||||
if self._private.origin then
|
|
||||||
self:emit_signal("property::value")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add a new target property and object.
|
|
||||||
function module:_add_target(object, property)
|
|
||||||
local hash = tostring(object)..property
|
|
||||||
if self._private.targets[hash] then return end
|
|
||||||
|
|
||||||
self._private.targets[hash] = {object, property}
|
|
||||||
|
|
||||||
self:emit_signal("target_added", object, property)
|
|
||||||
|
|
||||||
if self._private.evaluated then
|
|
||||||
object[property] = self._private.value
|
|
||||||
else
|
|
||||||
real_set_value(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Emitted when a new property is attached to this reactive expression.
|
|
||||||
--
|
|
||||||
-- @signal target_added
|
|
||||||
-- @tparam gears.object object The object (often the widget).
|
|
||||||
-- @tparam string property The property name.
|
|
||||||
|
|
||||||
function module:_set_declarative_handler(parent, key)
|
|
||||||
-- Lua "properties", aka table.foo must be strings.
|
|
||||||
assert(
|
|
||||||
type(key) == "string",
|
|
||||||
"gears.reactive can only be used ob object properties"
|
|
||||||
)
|
|
||||||
|
|
||||||
self:_add_target(parent, key)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Create a new `gears.reactive` object.
|
|
||||||
-- @constructorfct gears.reactive
|
|
||||||
-- @tparam table args
|
|
||||||
-- @tparam function args.expression A function which accesses other `gears.object`.
|
|
||||||
-- @tparam gears.object args.object Any AwesomeWM object.
|
|
||||||
-- @tparam string args.property The name of the property to track.
|
|
||||||
|
|
||||||
local function new(_, args)
|
|
||||||
-- It *can* be done. Actually, it is much easier to implement this module
|
|
||||||
-- on 5.1 since `setfenv` is much simpler than `debug.upvaluejoin`. However,
|
|
||||||
-- unless someone asks, then why support 2 incompatible code paths. Luajit 2.1
|
|
||||||
-- supports `debug.upvaluejoin`.
|
|
||||||
assert(
|
|
||||||
debug.upvaluejoin, --luacheck: ignore
|
|
||||||
"Sorry, `gears.reactive` doesn't support Lua 5.1 at this time"
|
|
||||||
)
|
|
||||||
|
|
||||||
if type(args) == "function" then
|
|
||||||
args = {expression = args}
|
|
||||||
end
|
|
||||||
|
|
||||||
local self = gobject {
|
|
||||||
enable_properties = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
rawset(self, "_private", {
|
|
||||||
targets = {},
|
|
||||||
delayed = true
|
|
||||||
})
|
|
||||||
|
|
||||||
gtable.crush(self, module, true)
|
|
||||||
|
|
||||||
gtable.crush(self, args, false)
|
|
||||||
|
|
||||||
self:connect_signal("property::value", real_set_value)
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(module, {__call = new})
|
|
|
@ -39,27 +39,31 @@ function gtable.join(...)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Override elements in the first table by the one in the second.
|
--- Override elements in the target table with values from the source table.
|
||||||
--
|
--
|
||||||
-- Note that this method doesn't copy entries found in `__index`.
|
-- Note that this method doesn't copy entries found in `__index`.
|
||||||
|
-- Nested tables are copied by reference and not recursed into.
|
||||||
--
|
--
|
||||||
-- @tparam table t the table to be overriden
|
-- @tparam table target The target table. Values from `source` will be copied
|
||||||
-- @tparam table set the table used to override members of `t`
|
-- into this table.
|
||||||
-- @tparam[opt=false] bool raw Use rawset (avoid the metatable)
|
-- @tparam table source The source table. Its values will be copied into
|
||||||
-- @treturn table t (for convenience)
|
-- `target`.
|
||||||
|
-- @tparam[opt=false] bool raw If `true`, values will be assigned with `rawset`.
|
||||||
|
-- This will bypass metamethods on `target`.
|
||||||
|
-- @treturn table The target table.
|
||||||
-- @staticfct gears.table.crush
|
-- @staticfct gears.table.crush
|
||||||
function gtable.crush(t, set, raw)
|
function gtable.crush(target, source, raw)
|
||||||
if raw then
|
if raw then
|
||||||
for k, v in pairs(set) do
|
for k, v in pairs(source) do
|
||||||
rawset(t, k, v)
|
rawset(target, k, v)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for k, v in pairs(set) do
|
for k, v in pairs(source) do
|
||||||
t[k] = v
|
target[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return t
|
return target
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Pack all elements with an integer key into a new table.
|
--- Pack all elements with an integer key into a new table.
|
||||||
|
@ -165,6 +169,23 @@ function gtable.keys(t)
|
||||||
return keys
|
return keys
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the number of keys in a table, both integer and string indicies.
|
||||||
|
--
|
||||||
|
-- This is functionally equivalent, but faster than `#gears.table.keys(t)`.
|
||||||
|
--
|
||||||
|
-- @DOC_text_gears_table_count_keys_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @tparam table t The table for which to count the keys.
|
||||||
|
-- @treturn number The number of keys in the table.
|
||||||
|
-- @staticfct gears.table.count_keys
|
||||||
|
function gtable.count_keys(t)
|
||||||
|
local count = 0
|
||||||
|
for _ in pairs(t) do
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
--- Filter a table's keys for certain content type.
|
--- Filter a table's keys for certain content type.
|
||||||
--
|
--
|
||||||
-- @tparam table t The table to retrieve the keys for.
|
-- @tparam table t The table to retrieve the keys for.
|
||||||
|
@ -208,7 +229,8 @@ end
|
||||||
--- Clone a table.
|
--- Clone a table.
|
||||||
--
|
--
|
||||||
-- @tparam table t The table to clone.
|
-- @tparam table t The table to clone.
|
||||||
-- @tparam[opt=true] bool deep Create a deep clone?
|
-- @tparam[opt=true] bool deep If `true`, recurse into nested tables to create
|
||||||
|
-- a deep clone.
|
||||||
-- @treturn table A clone of `t`.
|
-- @treturn table A clone of `t`.
|
||||||
-- @staticfct gears.table.clone
|
-- @staticfct gears.table.clone
|
||||||
function gtable.clone(t, deep)
|
function gtable.clone(t, deep)
|
||||||
|
@ -230,14 +252,16 @@ end
|
||||||
-- `first_index` has to be specified.
|
-- `first_index` has to be specified.
|
||||||
--
|
--
|
||||||
-- @tparam table t The input table.
|
-- @tparam table t The input table.
|
||||||
-- @param value A value from the table.
|
-- @param value The start value. Must be an element of the input table `t`.
|
||||||
-- @tparam[opt=1] number step_size How many element forward (or backward) to pick.
|
-- @tparam[opt=1] number step_size The amount to increment the index by.
|
||||||
-- @tparam[opt=nil] function filter An optional function. When it returns
|
-- When this is negative, the function will cycle through the table backwards.
|
||||||
-- `false`, the element are skipped until a match if found. It takes the value
|
-- @tparam[opt=nil] function filter An optional filter function. It receives a
|
||||||
-- as its sole parameter.
|
-- value from the table as parameter and should return a boolean. If it
|
||||||
|
-- returns `false`, the value is skipped and `cycle_value` tries the next one.
|
||||||
-- @tparam[opt=1] number start_at Where to start the lookup from.
|
-- @tparam[opt=1] number start_at Where to start the lookup from.
|
||||||
-- @return The value. If no element match, then `nil` is returned.
|
-- @return The next eligible value. If no value matches, `nil` is returned.
|
||||||
-- @treturn number|nil The element (if any) key.
|
-- @treturn number|nil If a value is found, this is its index within the input
|
||||||
|
-- table.
|
||||||
-- @staticfct gears.table.cycle_value
|
-- @staticfct gears.table.cycle_value
|
||||||
function gtable.cycle_value(t, value, step_size, filter, start_at)
|
function gtable.cycle_value(t, value, step_size, filter, start_at)
|
||||||
local k = gtable.hasitem(t, value, true, start_at)
|
local k = gtable.hasitem(t, value, true, start_at)
|
||||||
|
@ -287,17 +311,23 @@ function gtable.iterate(t, filter, start)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Merge items from one table to another one.
|
--- Merge items from the source table into the target table.
|
||||||
--
|
--
|
||||||
-- @tparam table t The container table
|
-- Note that this only considers the array part of `source` (same semantics
|
||||||
-- @tparam table set The mixin table.
|
-- as `ipairs`).
|
||||||
-- @treturn table (for convenience).
|
-- Nested tables are copied by reference and not recursed into.
|
||||||
|
--
|
||||||
|
-- @tparam table target The target table. Values from `source` will be copied
|
||||||
|
-- into this table.
|
||||||
|
-- @tparam table source The source table. Its values will be copied into
|
||||||
|
-- `target`.
|
||||||
|
-- @treturn table The target table.
|
||||||
-- @staticfct gears.table.merge
|
-- @staticfct gears.table.merge
|
||||||
function gtable.merge(t, set)
|
function gtable.merge(target, source)
|
||||||
for _, v in ipairs(set) do
|
for _, v in ipairs(source) do
|
||||||
table.insert(t, v)
|
table.insert(target, v)
|
||||||
end
|
end
|
||||||
return t
|
return target
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Update the `target` table with entries from the `new` table.
|
--- Update the `target` table with entries from the `new` table.
|
||||||
|
@ -384,7 +414,7 @@ end
|
||||||
|
|
||||||
--- Map a function to a table.
|
--- Map a function to a table.
|
||||||
--
|
--
|
||||||
-- The function is applied to each value on the table, returning a modified
|
-- The function is applied to each value in the table, returning a modified
|
||||||
-- table.
|
-- table.
|
||||||
--
|
--
|
||||||
-- @tparam function f The function to be applied to each value in the table.
|
-- @tparam function f The function to be applied to each value in the table.
|
||||||
|
@ -400,3 +430,5 @@ function gtable.map(f, tbl)
|
||||||
end
|
end
|
||||||
|
|
||||||
return gtable
|
return gtable
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -59,7 +59,6 @@ local traceback = debug.traceback
|
||||||
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
||||||
local glib = require("lgi").GLib
|
local glib = require("lgi").GLib
|
||||||
local object = require("gears.object")
|
local object = require("gears.object")
|
||||||
local gtable = require("gears.table")
|
|
||||||
local protected_call = require("gears.protected_call")
|
local protected_call = require("gears.protected_call")
|
||||||
local gdebug = require("gears.debug")
|
local gdebug = require("gears.debug")
|
||||||
|
|
||||||
|
@ -88,11 +87,11 @@ local timer = { mt = {} }
|
||||||
-- @method start
|
-- @method start
|
||||||
-- @emits start
|
-- @emits start
|
||||||
function timer:start()
|
function timer:start()
|
||||||
if self._private.source_id ~= nil then
|
if self.data.source_id ~= nil then
|
||||||
gdebug.print_error(traceback("timer already started"))
|
gdebug.print_error(traceback("timer already started"))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self._private.source_id = glib.timeout_add(glib.PRIORITY_DEFAULT, self._private.timeout * 1000, function()
|
self.data.source_id = glib.timeout_add(glib.PRIORITY_DEFAULT, self.data.timeout * 1000, function()
|
||||||
protected_call(self.emit_signal, self, "timeout")
|
protected_call(self.emit_signal, self, "timeout")
|
||||||
return true
|
return true
|
||||||
end)
|
end)
|
||||||
|
@ -100,15 +99,17 @@ function timer:start()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Stop the timer.
|
--- Stop the timer.
|
||||||
|
--
|
||||||
|
-- Does nothing if the timer isn't running.
|
||||||
|
--
|
||||||
-- @method stop
|
-- @method stop
|
||||||
-- @emits stop
|
-- @emits stop
|
||||||
function timer:stop()
|
function timer:stop()
|
||||||
if self._private.source_id == nil then
|
if self.data.source_id == nil then
|
||||||
gdebug.print_error(traceback("timer not started"))
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
glib.source_remove(self._private.source_id)
|
glib.source_remove(self.data.source_id)
|
||||||
self._private.source_id = nil
|
self.data.source_id = nil
|
||||||
self:emit_signal("stop")
|
self:emit_signal("stop")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -119,7 +120,7 @@ end
|
||||||
-- @emits start
|
-- @emits start
|
||||||
-- @emits stop
|
-- @emits stop
|
||||||
function timer:again()
|
function timer:again()
|
||||||
if self._private.source_id ~= nil then
|
if self.data.source_id ~= nil then
|
||||||
self:stop()
|
self:stop()
|
||||||
end
|
end
|
||||||
self:start()
|
self:start()
|
||||||
|
@ -135,28 +136,24 @@ end
|
||||||
-- @param number
|
-- @param number
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
|
||||||
function timer:get_timeout()
|
local timer_instance_mt = {
|
||||||
return self._private.timeout
|
__index = function(self, property)
|
||||||
end
|
if property == "timeout" then
|
||||||
|
return self.data.timeout
|
||||||
|
elseif property == "started" then
|
||||||
|
return self.data.source_id ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
function timer:get_started()
|
return timer[property]
|
||||||
return self._private.source_id ~= nil
|
end,
|
||||||
end
|
|
||||||
|
|
||||||
function timer:set_started(value)
|
__newindex = function(self, property, value)
|
||||||
if value == self:get_started() then return end
|
if property == "timeout" then
|
||||||
|
self.data.timeout = tonumber(value)
|
||||||
if value then
|
self:emit_signal("property::timeout", value)
|
||||||
self:start()
|
end
|
||||||
else
|
|
||||||
self:stop()
|
|
||||||
end
|
end
|
||||||
end
|
}
|
||||||
|
|
||||||
function timer:set_timeout(value)
|
|
||||||
self._private.timeout = tonumber(value)
|
|
||||||
self:emit_signal("property::timeout", value)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Create a new timer object.
|
--- Create a new timer object.
|
||||||
-- @tparam table args Arguments.
|
-- @tparam table args Arguments.
|
||||||
|
@ -170,33 +167,10 @@ end
|
||||||
-- @constructorfct gears.timer
|
-- @constructorfct gears.timer
|
||||||
function timer.new(args)
|
function timer.new(args)
|
||||||
args = args or {}
|
args = args or {}
|
||||||
local ret = object {
|
local ret = object()
|
||||||
enable_properties = true,
|
|
||||||
enable_auto_signals = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
gtable.crush(ret, timer, true)
|
ret.data = { timeout = 0 } --TODO v5 rename to ._private
|
||||||
|
setmetatable(ret, timer_instance_mt)
|
||||||
rawset(ret, "_private", { timeout = 0 })
|
|
||||||
|
|
||||||
-- Preserve backward compatibility with Awesome 4.0-4.3 use of "data"
|
|
||||||
-- rather then "_private".
|
|
||||||
rawset(ret, "data", setmetatable({}, {
|
|
||||||
__index = function(_, key)
|
|
||||||
gdebug.deprecate(
|
|
||||||
"gears.timer.data is deprecated, use normal properties",
|
|
||||||
{deprecated_in=5}
|
|
||||||
)
|
|
||||||
return ret._private[key]
|
|
||||||
end,
|
|
||||||
__newindex = function(_, key, value)
|
|
||||||
gdebug.deprecate(
|
|
||||||
"gears.timer.data is deprecated, use normal properties",
|
|
||||||
{deprecated_in=5}
|
|
||||||
)
|
|
||||||
ret._private[key] = value
|
|
||||||
end
|
|
||||||
}))
|
|
||||||
|
|
||||||
for k, v in pairs(args) do
|
for k, v in pairs(args) do
|
||||||
ret[k] = v
|
ret[k] = v
|
||||||
|
@ -220,13 +194,17 @@ function timer.new(args)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a timeout for calling some callback function.
|
--- Create a simple timer for calling the `callback` function continuously.
|
||||||
-- When the callback function returns true, it will be called again after the
|
--
|
||||||
-- same timeout. If false is returned, no more calls will be done. If the
|
-- This is a small wrapper around `gears.timer`, that creates a timer based on
|
||||||
-- callback function causes an error, no more calls are done.
|
-- `callback`.
|
||||||
|
-- The timer will run continuously and call `callback` every `timeout` seconds.
|
||||||
|
-- It is stopped when `callback` returns `false`, when `callback` throws an
|
||||||
|
-- error or when the `:stop()` method is called on the return value.
|
||||||
|
--
|
||||||
-- @tparam number timeout Timeout in seconds (e.g. 1.5).
|
-- @tparam number timeout Timeout in seconds (e.g. 1.5).
|
||||||
-- @tparam function callback Function to run.
|
-- @tparam function callback Function to run.
|
||||||
-- @treturn timer The timer object that was set up.
|
-- @treturn timer The new timer object.
|
||||||
-- @staticfct gears.timer.start_new
|
-- @staticfct gears.timer.start_new
|
||||||
-- @see gears.timer.weak_start_new
|
-- @see gears.timer.weak_start_new
|
||||||
function timer.start_new(timeout, callback)
|
function timer.start_new(timeout, callback)
|
||||||
|
@ -241,14 +219,18 @@ function timer.start_new(timeout, callback)
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a timeout for calling some callback function.
|
--- Create a simple timer for calling the `callback` function continuously.
|
||||||
-- This function is almost identical to `gears.timer.start_new`. The only difference
|
--
|
||||||
-- is that this does not prevent the callback function from being garbage
|
-- This function is almost identical to `gears.timer.start_new`. The only
|
||||||
-- collected. After the callback function was collected, the timer returned
|
-- difference is that this does not prevent the callback function from being
|
||||||
-- will automatically be stopped.
|
-- garbage collected.
|
||||||
|
-- In addition to the conditions in `gears.timer.start_new`,
|
||||||
|
-- this timer will also stop if `callback` was garbage collected since the
|
||||||
|
-- previous run.
|
||||||
|
--
|
||||||
-- @tparam number timeout Timeout in seconds (e.g. 1.5).
|
-- @tparam number timeout Timeout in seconds (e.g. 1.5).
|
||||||
-- @tparam function callback Function to start.
|
-- @tparam function callback Function to start.
|
||||||
-- @treturn timer The timer object that was set up.
|
-- @treturn timer The new timer object.
|
||||||
-- @staticfct gears.timer.weak_start_new
|
-- @staticfct gears.timer.weak_start_new
|
||||||
-- @see gears.timer.start_new
|
-- @see gears.timer.start_new
|
||||||
function timer.weak_start_new(timeout, callback)
|
function timer.weak_start_new(timeout, callback)
|
||||||
|
@ -275,7 +257,7 @@ function timer.run_delayed_calls_now()
|
||||||
delayed_calls = {}
|
delayed_calls = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Call the given function at the end of the current main loop iteration
|
--- Call the given function at the end of the current GLib event loop iteration.
|
||||||
-- @tparam function callback The function that should be called
|
-- @tparam function callback The function that should be called
|
||||||
-- @param ... Arguments to the callback function
|
-- @param ... Arguments to the callback function
|
||||||
-- @staticfct gears.timer.delayed_call
|
-- @staticfct gears.timer.delayed_call
|
||||||
|
|
|
@ -1,22 +1,7 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
-- Functions for setting the wallpaper.
|
-- Functions for setting the wallpaper.
|
||||||
--
|
--
|
||||||
-- There are two levels of functionality provided by this module:
|
-- This module is deprecated, please use `awful.wallpaper`.
|
||||||
--
|
|
||||||
-- The low-level functionality consists of two functions.
|
|
||||||
-- @{set} an already-prepared wallpaper on all screens and @{prepare_context}
|
|
||||||
-- prepares things to draw a new wallpaper.
|
|
||||||
--
|
|
||||||
-- The low-level API can for example be used to set solid red as a wallpaper
|
|
||||||
-- (see @{gears.color} for details on the supported syntax):
|
|
||||||
--
|
|
||||||
-- gears.wallpaper.set("#ff0000")
|
|
||||||
--
|
|
||||||
-- Ontop of these low-level functions, the remaining functions implement more
|
|
||||||
-- useful functionality. For example, given a screen object `s`, an image can be
|
|
||||||
-- set as the wallpaper as follows:
|
|
||||||
--
|
|
||||||
-- gears.wallpaper.maximized("path/to/image.png", s)
|
|
||||||
--
|
--
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2012 Uli Schlachter
|
-- @copyright 2012 Uli Schlachter
|
||||||
|
@ -51,8 +36,10 @@ end
|
||||||
-- @param s The screen to set the wallpaper on or nil for all screens
|
-- @param s The screen to set the wallpaper on or nil for all screens
|
||||||
-- @return[1] The available geometry (table with entries width and height)
|
-- @return[1] The available geometry (table with entries width and height)
|
||||||
-- @return[1] A cairo context that the wallpaper should be drawn to.
|
-- @return[1] A cairo context that the wallpaper should be drawn to.
|
||||||
-- @staticfct gears.wallpaper.prepare_context
|
-- @deprecated gears.wallpaper.prepare_context
|
||||||
function wallpaper.prepare_context(s)
|
function wallpaper.prepare_context(s)
|
||||||
|
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
|
||||||
|
|
||||||
s = get_screen(s)
|
s = get_screen(s)
|
||||||
|
|
||||||
local root_width, root_height = root.size()
|
local root_width, root_height = root.size()
|
||||||
|
@ -110,8 +97,10 @@ end
|
||||||
-- @param pattern The wallpaper that should be set. This can be a cairo surface,
|
-- @param pattern The wallpaper that should be set. This can be a cairo surface,
|
||||||
-- a description for gears.color or a cairo pattern.
|
-- a description for gears.color or a cairo pattern.
|
||||||
-- @see gears.color
|
-- @see gears.color
|
||||||
-- @staticfct gears.wallpaper.set
|
-- @deprecated gears.wallpaper.set
|
||||||
function wallpaper.set(pattern)
|
function wallpaper.set(pattern)
|
||||||
|
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
|
||||||
|
|
||||||
if cairo.Surface:is_type_of(pattern) then
|
if cairo.Surface:is_type_of(pattern) then
|
||||||
pattern = cairo.Pattern.create_for_surface(pattern)
|
pattern = cairo.Pattern.create_for_surface(pattern)
|
||||||
end
|
end
|
||||||
|
@ -121,7 +110,7 @@ function wallpaper.set(pattern)
|
||||||
if not cairo.Pattern:is_type_of(pattern) then
|
if not cairo.Pattern:is_type_of(pattern) then
|
||||||
error("wallpaper.set() called with an invalid argument")
|
error("wallpaper.set() called with an invalid argument")
|
||||||
end
|
end
|
||||||
root.wallpaper(pattern._native)
|
root.wallpaper(pattern)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set a centered wallpaper.
|
--- Set a centered wallpaper.
|
||||||
|
@ -132,8 +121,10 @@ end
|
||||||
-- gears.color. The default is black.
|
-- gears.color. The default is black.
|
||||||
-- @param scale The scale factor for the wallpaper. Default is 1 (original size).
|
-- @param scale The scale factor for the wallpaper. Default is 1 (original size).
|
||||||
-- @see gears.color
|
-- @see gears.color
|
||||||
-- @staticfct gears.wallpaper.centered
|
-- @deprecated gears.wallpaper.centered
|
||||||
function wallpaper.centered(surf, s, background, scale)
|
function wallpaper.centered(surf, s, background, scale)
|
||||||
|
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
|
||||||
|
|
||||||
local geom, cr = wallpaper.prepare_context(s)
|
local geom, cr = wallpaper.prepare_context(s)
|
||||||
local original_surf = surf
|
local original_surf = surf
|
||||||
surf = surface.load_uncached(surf)
|
surf = surface.load_uncached(surf)
|
||||||
|
@ -172,8 +163,10 @@ end
|
||||||
-- @param s The screen whose wallpaper should be set. Can be nil, in which case
|
-- @param s The screen whose wallpaper should be set. Can be nil, in which case
|
||||||
-- all screens are set.
|
-- all screens are set.
|
||||||
-- @param offset This can be set to a table with entries x and y.
|
-- @param offset This can be set to a table with entries x and y.
|
||||||
-- @staticfct gears.wallpaper.tiled
|
-- @deprecated gears.wallpaper.tiled
|
||||||
function wallpaper.tiled(surf, s, offset)
|
function wallpaper.tiled(surf, s, offset)
|
||||||
|
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
|
||||||
|
|
||||||
local _, cr = wallpaper.prepare_context(s)
|
local _, cr = wallpaper.prepare_context(s)
|
||||||
|
|
||||||
if offset then
|
if offset then
|
||||||
|
@ -202,8 +195,10 @@ end
|
||||||
-- @param ignore_aspect If this is true, the image's aspect ratio is ignored.
|
-- @param ignore_aspect If this is true, the image's aspect ratio is ignored.
|
||||||
-- The default is to honor the aspect ratio.
|
-- The default is to honor the aspect ratio.
|
||||||
-- @param offset This can be set to a table with entries x and y.
|
-- @param offset This can be set to a table with entries x and y.
|
||||||
-- @staticfct gears.wallpaper.maximized
|
-- @deprecated gears.wallpaper.maximized
|
||||||
function wallpaper.maximized(surf, s, ignore_aspect, offset)
|
function wallpaper.maximized(surf, s, ignore_aspect, offset)
|
||||||
|
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
|
||||||
|
|
||||||
local geom, cr = wallpaper.prepare_context(s)
|
local geom, cr = wallpaper.prepare_context(s)
|
||||||
local original_surf = surf
|
local original_surf = surf
|
||||||
surf = surface.load_uncached(surf)
|
surf = surface.load_uncached(surf)
|
||||||
|
@ -243,8 +238,10 @@ end
|
||||||
-- @param background The background color that should be used. Gets handled via
|
-- @param background The background color that should be used. Gets handled via
|
||||||
-- gears.color. The default is black.
|
-- gears.color. The default is black.
|
||||||
-- @see gears.color
|
-- @see gears.color
|
||||||
-- @staticfct gears.wallpaper.fit
|
-- @deprecated gears.wallpaper.fit
|
||||||
function wallpaper.fit(surf, s, background)
|
function wallpaper.fit(surf, s, background)
|
||||||
|
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
|
||||||
|
|
||||||
local geom, cr = wallpaper.prepare_context(s)
|
local geom, cr = wallpaper.prepare_context(s)
|
||||||
local original_surf = surf
|
local original_surf = surf
|
||||||
surf = surface.load_uncached(surf)
|
surf = surface.load_uncached(surf)
|
||||||
|
|
|
@ -1,733 +0,0 @@
|
||||||
--- Fetch information at a specific interval.
|
|
||||||
--
|
|
||||||
-- @author Emmanuel Lepage-Vallee <elv1313@gmail.com>
|
|
||||||
-- @copyright 2020 Emmanuel Lepage-Vallee
|
|
||||||
-- @classmod gears.watcher
|
|
||||||
|
|
||||||
local capi = {awesome = awesome}
|
|
||||||
local protected_call = require("gears.protected_call")
|
|
||||||
local gtimer = require("gears.timer")
|
|
||||||
local gtable = require("gears.table")
|
|
||||||
local lgi = require("lgi")
|
|
||||||
local Gio = lgi.Gio
|
|
||||||
local GLib = lgi.GLib
|
|
||||||
|
|
||||||
local module = {}
|
|
||||||
|
|
||||||
-- This is awful.util.shell
|
|
||||||
module._shell = os.getenv("SHELL") or "/bin/sh"
|
|
||||||
|
|
||||||
local end_of_file
|
|
||||||
do
|
|
||||||
-- API changes, bug fixes and lots of fun. Figure out how a EOF is signalled.
|
|
||||||
local input
|
|
||||||
if not pcall(function()
|
|
||||||
-- No idea when this API changed, but some versions expect a string,
|
|
||||||
-- others a table with some special(?) entries
|
|
||||||
input = Gio.DataInputStream.new(Gio.MemoryInputStream.new_from_data(""))
|
|
||||||
end) then
|
|
||||||
input = Gio.DataInputStream.new(Gio.MemoryInputStream.new_from_data({}))
|
|
||||||
end
|
|
||||||
local line, length = input:read_line()
|
|
||||||
if not line then
|
|
||||||
-- Fixed in 2016: NULL on the C side is transformed to nil in Lua
|
|
||||||
end_of_file = function(arg)
|
|
||||||
return not arg
|
|
||||||
end
|
|
||||||
elseif tostring(line) == "" and #line ~= length then
|
|
||||||
-- "Historic" behaviour for end-of-file:
|
|
||||||
-- - NULL is turned into an empty string
|
|
||||||
-- - The length variable is not initialized
|
|
||||||
-- It's highly unlikely that the uninitialized variable has value zero.
|
|
||||||
-- Use this hack to detect EOF.
|
|
||||||
end_of_file = function(arg1, arg2)
|
|
||||||
return #arg1 ~= arg2
|
|
||||||
end
|
|
||||||
else
|
|
||||||
assert(tostring(line) == "", "Cannot determine how to detect EOF")
|
|
||||||
-- The above uninitialized variable was fixed and thus length is
|
|
||||||
-- always 0 when line is NULL in C. We cannot tell apart an empty line and
|
|
||||||
-- EOF in this case.
|
|
||||||
require("gears.debug").print_warning("Cannot reliably detect EOF on an "
|
|
||||||
.. "GIOInputStream with this LGI version")
|
|
||||||
end_of_file = function(arg)
|
|
||||||
return tostring(arg) == ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module._end_of_file = end_of_file
|
|
||||||
|
|
||||||
function module._read_lines(input_stream, line_callback, done_callback, close)
|
|
||||||
local stream = Gio.DataInputStream.new(input_stream)
|
|
||||||
local function done()
|
|
||||||
if close then
|
|
||||||
stream:close()
|
|
||||||
end
|
|
||||||
stream:set_buffer_size(0)
|
|
||||||
if done_callback then
|
|
||||||
protected_call(done_callback)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local start_read, finish_read
|
|
||||||
start_read = function()
|
|
||||||
stream:read_line_async(GLib.PRIORITY_DEFAULT, nil, finish_read)
|
|
||||||
end
|
|
||||||
finish_read = function(obj, res)
|
|
||||||
local line, length = obj:read_line_finish(res)
|
|
||||||
if type(length) ~= "number" then
|
|
||||||
-- Error
|
|
||||||
print("Error in awful.spawn.read_lines:", tostring(length))
|
|
||||||
done()
|
|
||||||
elseif end_of_file(line, length) then
|
|
||||||
-- End of file
|
|
||||||
done()
|
|
||||||
else
|
|
||||||
-- Read a line
|
|
||||||
-- This needs tostring() for older lgi versions which returned
|
|
||||||
-- "GLib.Bytes" instead of Lua strings (I guess)
|
|
||||||
protected_call(line_callback, tostring(line))
|
|
||||||
|
|
||||||
-- Read the next line
|
|
||||||
start_read()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
start_read()
|
|
||||||
end
|
|
||||||
|
|
||||||
function module._with_line_callback(cmd, callbacks)
|
|
||||||
local stdout_callback, stderr_callback, done_callback, exit_callback =
|
|
||||||
callbacks.stdout, callbacks.stderr, callbacks.output_done, callbacks.exit
|
|
||||||
local have_stdout, have_stderr = stdout_callback ~= nil, stderr_callback ~= nil
|
|
||||||
local pid, _, stdin, stdout, stderr = capi.awesome.spawn(cmd,
|
|
||||||
false, false, have_stdout, have_stderr, exit_callback)
|
|
||||||
if type(pid) == "string" then
|
|
||||||
-- Error
|
|
||||||
return pid
|
|
||||||
end
|
|
||||||
|
|
||||||
local done_before = false
|
|
||||||
local function step_done()
|
|
||||||
if have_stdout and have_stderr and not done_before then
|
|
||||||
done_before = true
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if done_callback then
|
|
||||||
done_callback()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if have_stdout then
|
|
||||||
module._read_lines(Gio.UnixInputStream.new(stdout, true),
|
|
||||||
stdout_callback, step_done, true)
|
|
||||||
end
|
|
||||||
if have_stderr then
|
|
||||||
module._read_lines(Gio.UnixInputStream.new(stderr, true),
|
|
||||||
stderr_callback, step_done, true)
|
|
||||||
end
|
|
||||||
assert(stdin == nil)
|
|
||||||
return pid
|
|
||||||
end
|
|
||||||
|
|
||||||
function module._easy_async(cmd, callback)
|
|
||||||
local stdout = ''
|
|
||||||
local stderr = ''
|
|
||||||
local exitcode, exitreason
|
|
||||||
local function parse_stdout(str)
|
|
||||||
stdout = stdout .. str .. "\n"
|
|
||||||
end
|
|
||||||
local function parse_stderr(str)
|
|
||||||
stderr = stderr .. str .. "\n"
|
|
||||||
end
|
|
||||||
local function done_callback()
|
|
||||||
return callback(stdout, stderr, exitreason, exitcode)
|
|
||||||
end
|
|
||||||
local exit_callback_fired = false
|
|
||||||
local output_done_callback_fired = false
|
|
||||||
local function exit_callback(reason, code)
|
|
||||||
exitcode = code
|
|
||||||
exitreason = reason
|
|
||||||
exit_callback_fired = true
|
|
||||||
if output_done_callback_fired then
|
|
||||||
return done_callback()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local function output_done_callback()
|
|
||||||
output_done_callback_fired = true
|
|
||||||
if exit_callback_fired then
|
|
||||||
return done_callback()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return module._with_line_callback(
|
|
||||||
cmd, {
|
|
||||||
stdout=parse_stdout,
|
|
||||||
stderr=parse_stderr,
|
|
||||||
exit=exit_callback,
|
|
||||||
output_done=output_done_callback
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function module._read_async(path, callback, fail_callback)
|
|
||||||
local cancel = Gio.Cancellable()
|
|
||||||
|
|
||||||
Gio.File.new_for_path(path):load_contents_async(cancel, function(file, task)
|
|
||||||
local content = file:load_contents_finish(task)
|
|
||||||
if content then
|
|
||||||
callback(path, content)
|
|
||||||
elseif fail_callback then
|
|
||||||
fail_callback(path)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
return cancel
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Posix files and commands always end with a newline.
|
|
||||||
-- It is nearly always unwanted, so we strip it by default.
|
|
||||||
local function remove_posix_extra_newline(content)
|
|
||||||
-- Remove the trailing `\n`
|
|
||||||
if content:sub(-1) == '\n' then
|
|
||||||
content = content:sub(1, -2)
|
|
||||||
end
|
|
||||||
|
|
||||||
return content
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Make sure we sort transactions in a way obsolete ones are
|
|
||||||
-- not used.
|
|
||||||
|
|
||||||
local function add_transaction(self, transaction)
|
|
||||||
table.insert(self._private.transactions, transaction)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function remove_transaction(self, transaction)
|
|
||||||
|
|
||||||
-- Too late, abort.
|
|
||||||
for _, cancel in pairs(transaction.pending) do
|
|
||||||
cancel:cancel()
|
|
||||||
end
|
|
||||||
|
|
||||||
for k, t in ipairs(self._private.transactions) do
|
|
||||||
if t == transaction then
|
|
||||||
table.remove(self._private.transactions, k)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Keys can also be labels or object, # wont work.
|
|
||||||
local function count_files(files)
|
|
||||||
local ret = 0
|
|
||||||
|
|
||||||
for _ in pairs(files) do
|
|
||||||
ret = ret + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
-- When there is multiple files, we need to wait
|
|
||||||
-- until are of them are read.
|
|
||||||
local function gen_file_transaction(self)
|
|
||||||
if count_files(self.files) == 0 then return nil end
|
|
||||||
|
|
||||||
local ret = {
|
|
||||||
counter = count_files(self.files),
|
|
||||||
files = gtable.clone(self.files, false),
|
|
||||||
filter = self.filter,
|
|
||||||
labels = {},
|
|
||||||
content = {},
|
|
||||||
failed = {},
|
|
||||||
pending = {},
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
local function finished(file, content)
|
|
||||||
assert(not ret.content[file])
|
|
||||||
self:emit_signal("file::acquired", file, content)
|
|
||||||
ret.pending[file] = nil
|
|
||||||
|
|
||||||
ret.counter = ret.counter - 1
|
|
||||||
ret.content[file] = content
|
|
||||||
|
|
||||||
if ret.counter > 0 then return end
|
|
||||||
|
|
||||||
self:emit_signal("files::acquired", ret.content, ret.failed)
|
|
||||||
|
|
||||||
-- Make the final table using the stored keys.
|
|
||||||
local contents = {}
|
|
||||||
|
|
||||||
for path, ctn in pairs(ret.content) do
|
|
||||||
if self.strip_newline then
|
|
||||||
ctn = remove_posix_extra_newline(ctn)
|
|
||||||
end
|
|
||||||
|
|
||||||
contents[self._private.file_keys[path]] = ctn
|
|
||||||
|
|
||||||
if self.labels_as_properties and type(ret.labels[path]) == "string" then
|
|
||||||
local val
|
|
||||||
|
|
||||||
if ret.filter then
|
|
||||||
val = ret.filter(ret.labels[path], ctn)
|
|
||||||
else
|
|
||||||
val = ctn
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Make sure the signals are not fired for nothing.
|
|
||||||
if val ~= self[ret.labels[path]] then
|
|
||||||
self[ret.labels[path]] = val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local ctn = count_files(ret.files) == 1 and contents[next(contents)] or contents
|
|
||||||
|
|
||||||
if ret.filter and not self.labels_as_properties then
|
|
||||||
self._private.value = ret.filter(ctn, ret.failed)
|
|
||||||
else
|
|
||||||
self._private.value = ctn
|
|
||||||
end
|
|
||||||
|
|
||||||
self:emit_signal("property::value", self._private.value)
|
|
||||||
|
|
||||||
remove_transaction(self, ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function read_error(file)
|
|
||||||
ret.pending[file] = nil
|
|
||||||
table.insert(ret.failed, file)
|
|
||||||
self:emit_signal("file::failed", file)
|
|
||||||
end
|
|
||||||
|
|
||||||
for label, file in pairs(ret.files) do
|
|
||||||
ret.labels[file] = label
|
|
||||||
|
|
||||||
local cancel = module._read_async(file, finished, read_error)
|
|
||||||
|
|
||||||
ret.pending[file] = cancel
|
|
||||||
end
|
|
||||||
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
local modes_start, modes_abort = {}, {}
|
|
||||||
|
|
||||||
modes_start["none"] = function() --[[nop]] end
|
|
||||||
modes_abort["none"] = function() --[[nop]] end
|
|
||||||
|
|
||||||
modes_start["files"] = function(self)
|
|
||||||
if not self._private.init then return end
|
|
||||||
|
|
||||||
local t = gen_file_transaction(self)
|
|
||||||
|
|
||||||
add_transaction(self, t)
|
|
||||||
end
|
|
||||||
|
|
||||||
modes_abort["files"] = function(self)
|
|
||||||
for _, t in ipairs(self._private.transactions) do
|
|
||||||
remove_transaction(self, t)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
modes_start["command"] = function(self)
|
|
||||||
if not self._private.init then return end
|
|
||||||
|
|
||||||
local com = self._private.command
|
|
||||||
|
|
||||||
if self._private.shell then
|
|
||||||
assert(
|
|
||||||
type(com) == "string",
|
|
||||||
"When using `gears.watcher` with `shell = true`, "..
|
|
||||||
"the command must be a string"
|
|
||||||
)
|
|
||||||
|
|
||||||
com = {module._shell, '-c', com}
|
|
||||||
end
|
|
||||||
|
|
||||||
module._easy_async(com, function(lines, stderr, err, errcode)
|
|
||||||
if self.strip_newline then
|
|
||||||
lines = remove_posix_extra_newline(lines)
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.filter then
|
|
||||||
self._private.value = self.filter(lines, stderr, err, errcode)
|
|
||||||
else
|
|
||||||
self._private.value = lines
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
modes_abort["command"] = function()
|
|
||||||
--TODO
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:_set_declarative_handler(parent, key, ids)
|
|
||||||
assert(type(key) == "string", "A watcher can only be attached to properties")
|
|
||||||
|
|
||||||
table.insert(self._private.targets, {
|
|
||||||
ids = ids,
|
|
||||||
parent = parent,
|
|
||||||
property = key
|
|
||||||
})
|
|
||||||
|
|
||||||
if self._private.value then
|
|
||||||
parent[key] = self._private.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Abort the current transactions.
|
|
||||||
--
|
|
||||||
-- If files are currently being read or commands executed,
|
|
||||||
-- abort it. This does prevent new transaction from being
|
|
||||||
-- started. Use `:stop()` for that.
|
|
||||||
--
|
|
||||||
-- @method abort
|
|
||||||
-- @see stop
|
|
||||||
|
|
||||||
function module:abort()
|
|
||||||
modes_abort[self._private.mode](self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Emitted when a file is read.
|
|
||||||
--
|
|
||||||
-- @signal file::acquired
|
|
||||||
-- @tparam string path The path.
|
|
||||||
-- @tparam string content The file content.
|
|
||||||
-- @see files::acquired
|
|
||||||
-- @see file::failed
|
|
||||||
|
|
||||||
--- When reading a file failed.
|
|
||||||
-- @signal file::failed
|
|
||||||
-- @tparam string path The path.
|
|
||||||
|
|
||||||
--- Emitted when all files are read.
|
|
||||||
--
|
|
||||||
-- @signal files::acquired
|
|
||||||
-- @tparam table contents Path as keys and content as values.
|
|
||||||
-- @tparam table failed The list of files which could not be read.
|
|
||||||
|
|
||||||
--- A file path.
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_watcher_simple_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- @property file
|
|
||||||
-- @tparam string file
|
|
||||||
-- @propemits true false
|
|
||||||
-- @see files
|
|
||||||
|
|
||||||
--- A list or map of files.
|
|
||||||
--
|
|
||||||
-- It is often necessary to query multiple files. When reading from `proc`,
|
|
||||||
-- some data is split across multiple files, such as the battery charge.
|
|
||||||
--
|
|
||||||
-- This property accepts 2 format. One uses is a plain table of paths while
|
|
||||||
-- the other is a label->path map. Depending on what is being read, both make
|
|
||||||
-- sense.
|
|
||||||
--
|
|
||||||
-- **Simple path list:**
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_watcher_files1_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- **With labels:**
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_watcher_files2_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- @property files
|
|
||||||
-- @tparam table files
|
|
||||||
-- @propemits true false
|
|
||||||
-- @see labels_as_properties
|
|
||||||
|
|
||||||
function module:get_file()
|
|
||||||
return self._private.files[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_file(path)
|
|
||||||
self:set_files({path})
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:get_files()
|
|
||||||
return self._private.files
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_files(paths)
|
|
||||||
self:abort()
|
|
||||||
|
|
||||||
self._private.files = paths or {}
|
|
||||||
self._private.mode = "files"
|
|
||||||
|
|
||||||
self:emit_signal("property::files", self._private.files )
|
|
||||||
self:emit_signal("property::file" , select(2, next(self._private.files)))
|
|
||||||
|
|
||||||
-- It is possible to give names to each files. For modules like
|
|
||||||
-- battery widgets, which require reading multiple long paths from
|
|
||||||
-- `proc`, it makes the user code more readable.
|
|
||||||
self._private.file_keys = {}
|
|
||||||
|
|
||||||
for k, v in pairs(self._private.files) do
|
|
||||||
self._private.file_keys[v] = type(k) == "number" and v or k
|
|
||||||
end
|
|
||||||
|
|
||||||
modes_start["files"](self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Add a file to the files list.
|
|
||||||
--
|
|
||||||
-- @method append_file
|
|
||||||
-- @tparam string path The path.
|
|
||||||
-- @tparam[opt] string The key.
|
|
||||||
|
|
||||||
function module:append_file(path, key)
|
|
||||||
self:abort()
|
|
||||||
|
|
||||||
if self._private.files[path] then return end
|
|
||||||
|
|
||||||
key = key or (#self._private.files + 1)
|
|
||||||
|
|
||||||
self._private.mode = "files"
|
|
||||||
|
|
||||||
self._private.files[key] = path
|
|
||||||
self._private.file_keys[path] = key
|
|
||||||
|
|
||||||
self:emit_signal("property::files", self._private.files )
|
|
||||||
self:emit_signal("property::file" , select(2, next(self._private.files)))
|
|
||||||
|
|
||||||
modes_start["files"](self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Remove a file to the files list.
|
|
||||||
--
|
|
||||||
-- @method remove_file
|
|
||||||
-- @tparam string path The path or the key.
|
|
||||||
|
|
||||||
--- A filter to post-process the file or command.
|
|
||||||
--
|
|
||||||
-- It can be used, for example, to convert the string to a number or turn
|
|
||||||
-- the various file content into the final value. The filter argument
|
|
||||||
-- depend on various `gears.watcher` properties (as documented below).
|
|
||||||
--
|
|
||||||
-- **The callback parameters for a single file:**
|
|
||||||
-- (1) The file content (string)
|
|
||||||
--
|
|
||||||
-- **The callback parameters for a multiple files (paths):**
|
|
||||||
-- (1) Tables with the paths as keys and string content as value.
|
|
||||||
--
|
|
||||||
-- **The callback parameters for a multiple files (labels):**
|
|
||||||
-- (1) Tables with the keys as keys and string content as value.
|
|
||||||
--
|
|
||||||
-- **The callback when `labels_as_properties` is true:
|
|
||||||
-- (1) The label name
|
|
||||||
-- (2) The content
|
|
||||||
--
|
|
||||||
-- **The callback parameters for a command:**
|
|
||||||
-- (1) Stdout as first parameter
|
|
||||||
-- (2) Stderr as second parameter
|
|
||||||
-- (3) Exitreason
|
|
||||||
-- (4) Exitcode
|
|
||||||
--
|
|
||||||
-- @property filter
|
|
||||||
-- @tparam function filter
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
function module:get_filter()
|
|
||||||
return self._private.filter
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_filter(filter)
|
|
||||||
self:abort()
|
|
||||||
|
|
||||||
self._private.filter = filter
|
|
||||||
|
|
||||||
self:emit_signal("property::filter", filter)
|
|
||||||
|
|
||||||
modes_start[self._private.mode](self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- A command.
|
|
||||||
--
|
|
||||||
-- If you plan to use pipes or any shell features, do not
|
|
||||||
-- forget to also set `shell` to `true`.
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_watcher_command1_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- @property command
|
|
||||||
-- @tparam table|string command
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
function module:get_command()
|
|
||||||
return self._private.command
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_command(command)
|
|
||||||
self._private.command = command
|
|
||||||
self._private.mode = "command"
|
|
||||||
self:emit_signal("property::command")
|
|
||||||
modes_start["command"](self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Use a shell when calling the command.
|
|
||||||
--
|
|
||||||
-- This means you can use `|`, `&&`, `$?` in your command.
|
|
||||||
--
|
|
||||||
-- @DOC_text_gears_watcher_command2_EXAMPLE@
|
|
||||||
---
|
|
||||||
-- @property shell
|
|
||||||
-- @tparam[opt=false] boolean|string shell
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
function module:get_shell()
|
|
||||||
return self._private.shell or false
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_shell(shell)
|
|
||||||
self._private.shell = shell
|
|
||||||
self:emit_signal("property::shell")
|
|
||||||
end
|
|
||||||
|
|
||||||
--- In files mode, when paths have labels, apply them as properties.
|
|
||||||
--
|
|
||||||
-- @DOC_wibox_widget_declarative_watcher_EXAMPLE@
|
|
||||||
--
|
|
||||||
-- @property labels_as_properties
|
|
||||||
-- @tparam[opt=false] boolean labels_as_properties
|
|
||||||
-- @see files
|
|
||||||
|
|
||||||
--- The interval between the content refresh.
|
|
||||||
--
|
|
||||||
-- (in seconds)
|
|
||||||
--
|
|
||||||
-- @property interval
|
|
||||||
-- @tparam number interval
|
|
||||||
-- @see gears.timer.timeout
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
-- There is not get_timeout/set_timeout, so we can't make aliases.
|
|
||||||
module.get_interval = gtimer.get_timeout
|
|
||||||
module.set_interval = gtimer.set_timeout
|
|
||||||
|
|
||||||
--- The current value of the watcher.
|
|
||||||
--
|
|
||||||
-- If there is no filter, this will be a string. If a filter is used,
|
|
||||||
-- then it is whatever it returns.
|
|
||||||
--
|
|
||||||
-- @property value
|
|
||||||
-- @propemits false false
|
|
||||||
|
|
||||||
function module:get_value()
|
|
||||||
return self._private.value
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Strip the extra trailing newline.
|
|
||||||
--
|
|
||||||
-- All posix compliant text file and commands end with a newline.
|
|
||||||
-- Most of the time, this is inconvinient, so `gears.watcher` removes
|
|
||||||
-- them by default. Set this to `false` if this isn't the desired
|
|
||||||
-- behavior.
|
|
||||||
--
|
|
||||||
-- @property strip_newline
|
|
||||||
-- @tparam[opt=true] boolean strip_newline
|
|
||||||
-- @propemits true false
|
|
||||||
|
|
||||||
function module:get_strip_newline()
|
|
||||||
return self._private.newline
|
|
||||||
end
|
|
||||||
|
|
||||||
function module:set_strip_newline(value)
|
|
||||||
self._private.newline = value
|
|
||||||
self:emit_signal("property::strip_newline", value)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Start the timer.
|
|
||||||
-- @method start
|
|
||||||
-- @emits start
|
|
||||||
-- @baseclass gears.timer
|
|
||||||
-- @see stop
|
|
||||||
|
|
||||||
--- Stop the timer.
|
|
||||||
-- @method stop
|
|
||||||
-- @emits stop
|
|
||||||
-- @baseclass gears.timer
|
|
||||||
-- @see abort
|
|
||||||
-- @see start
|
|
||||||
|
|
||||||
--- The timer is started.
|
|
||||||
-- @property started
|
|
||||||
-- @tparam boolean started
|
|
||||||
-- @propemits false false
|
|
||||||
-- @baseclass gears.timer
|
|
||||||
|
|
||||||
--- Restart the timer.
|
|
||||||
-- This is equivalent to stopping the timer if it is running and then starting
|
|
||||||
-- it.
|
|
||||||
-- @method again
|
|
||||||
-- @baseclass gears.timer
|
|
||||||
-- @emits start
|
|
||||||
-- @emits stop
|
|
||||||
|
|
||||||
--- Create a new `gears.watcher` object.
|
|
||||||
--
|
|
||||||
-- @constructorfct gears.watcher
|
|
||||||
-- @tparam table args
|
|
||||||
-- @tparam string args.file A file path.
|
|
||||||
-- @tparam table args.files A list or map of files.
|
|
||||||
-- @tparam function args.filter A filter to post-process the file or command.
|
|
||||||
-- @tparam table|string args.command A command (without a shell).
|
|
||||||
-- @tparam[opt=false] boolean args.shell Use a shell when calling the command.
|
|
||||||
-- @param args.initial_value The value to use before the first "real" value is acquired.
|
|
||||||
-- @tparam number args.interval The interval between the content refresh.
|
|
||||||
-- @tparam boolean args.labels_as_properties Set the file labels as properties.
|
|
||||||
-- @tparam boolean args.started The timer is started.
|
|
||||||
|
|
||||||
local function new(_, args)
|
|
||||||
local ret = gtimer()
|
|
||||||
ret.timeout = 5000
|
|
||||||
|
|
||||||
local newargs = gtable.clone(args or {}, false)
|
|
||||||
|
|
||||||
ret._private.mode = "none"
|
|
||||||
ret._private.transactions = {}
|
|
||||||
ret._private.targets = {}
|
|
||||||
ret._private.files = {}
|
|
||||||
|
|
||||||
if newargs.autostart == nil then
|
|
||||||
newargs.autostart = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if newargs.strip_newline == nil then
|
|
||||||
newargs.strip_newline = true
|
|
||||||
end
|
|
||||||
|
|
||||||
gtable.crush(ret, module , true )
|
|
||||||
gtable.crush(ret, newargs, false)
|
|
||||||
|
|
||||||
ret.shell = args.shell
|
|
||||||
-- ret:set_shell(args.shell)
|
|
||||||
|
|
||||||
local function value_callback()
|
|
||||||
for _, target in ipairs(ret._private.targets) do
|
|
||||||
target.parent[target.property] = ret.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function update_callback()
|
|
||||||
modes_start[ret._private.mode](ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
ret:connect_signal("property::value", value_callback)
|
|
||||||
ret:connect_signal("timeout", update_callback)
|
|
||||||
|
|
||||||
if args.initial_value then
|
|
||||||
ret._private.value = args.initial_value
|
|
||||||
end
|
|
||||||
|
|
||||||
ret._private.init = true
|
|
||||||
|
|
||||||
if newargs.autostart then
|
|
||||||
ret:start()
|
|
||||||
modes_start[ret._private.mode](ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(module, {__call = new})
|
|
|
@ -70,12 +70,15 @@ end
|
||||||
-- @beautiful beautiful.menubar_bg_focus
|
-- @beautiful beautiful.menubar_bg_focus
|
||||||
-- @param color
|
-- @param color
|
||||||
|
|
||||||
|
--- Menubar font.
|
||||||
|
-- @beautiful beautiful.menubar_font
|
||||||
|
-- @param[opt=beautiful.font] font
|
||||||
|
|
||||||
|
|
||||||
-- menubar
|
-- menubar
|
||||||
local menubar = { menu_entries = {} }
|
local menubar = { menu_entries = {} }
|
||||||
menubar.menu_gen = require("menubar.menu_gen")
|
menubar.menu_gen = require("menubar.menu_gen")
|
||||||
menubar.utils = require("menubar.utils")
|
menubar.utils = require("menubar.utils")
|
||||||
local compute_text_width = menubar.utils.compute_text_width
|
|
||||||
|
|
||||||
-- Options section
|
-- Options section
|
||||||
|
|
||||||
|
@ -119,10 +122,9 @@ menubar.right_label = "▶▶"
|
||||||
-- @tfield[opt="◀◀"] string left_label
|
-- @tfield[opt="◀◀"] string left_label
|
||||||
menubar.left_label = "◀◀"
|
menubar.left_label = "◀◀"
|
||||||
|
|
||||||
-- awful.widget.common.list_update adds three times a margin of dpi(4)
|
-- awful.widget.common.list_update adds spacing of dpi(4) between items.
|
||||||
-- for each item:
|
-- @tfield number list_spacing
|
||||||
-- @tfield number list_interspace
|
local list_spacing = theme.xresources.apply_dpi(4)
|
||||||
local list_interspace = theme.xresources.apply_dpi(4) * 3
|
|
||||||
|
|
||||||
--- Allows user to specify custom parameters for prompt.run function
|
--- Allows user to specify custom parameters for prompt.run function
|
||||||
-- (like colors). This will merge with the default parameters, overriding affected values.
|
-- (like colors). This will merge with the default parameters, overriding affected values.
|
||||||
|
@ -149,7 +151,7 @@ end
|
||||||
|
|
||||||
--- Get how the menu item should be displayed.
|
--- Get how the menu item should be displayed.
|
||||||
-- @param o The menu item.
|
-- @param o The menu item.
|
||||||
-- @return item name, item background color, background image, item icon.
|
-- @return item name, item background color, background image, item icon, item args.
|
||||||
local function label(o)
|
local function label(o)
|
||||||
local fg_color = theme.menubar_fg_normal or theme.menu_fg_normal or theme.fg_normal
|
local fg_color = theme.menubar_fg_normal or theme.menu_fg_normal or theme.fg_normal
|
||||||
local bg_color = theme.menubar_bg_normal or theme.menu_bg_normal or theme.bg_normal
|
local bg_color = theme.menubar_bg_normal or theme.menu_bg_normal or theme.bg_normal
|
||||||
|
@ -160,7 +162,8 @@ local function label(o)
|
||||||
return colortext(gstring.xml_escape(o.name), fg_color),
|
return colortext(gstring.xml_escape(o.name), fg_color),
|
||||||
bg_color,
|
bg_color,
|
||||||
nil,
|
nil,
|
||||||
o.icon
|
o.icon,
|
||||||
|
o.icon and {icon_size=instance.geometry.height}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function load_count_table()
|
local function load_count_table()
|
||||||
|
@ -225,6 +228,11 @@ end
|
||||||
-- @tparam number|screen scr Screen
|
-- @tparam number|screen scr Screen
|
||||||
-- @return table List of items for current page.
|
-- @return table List of items for current page.
|
||||||
local function get_current_page(all_items, query, scr)
|
local function get_current_page(all_items, query, scr)
|
||||||
|
|
||||||
|
local compute_text_width = function(text, s)
|
||||||
|
return wibox.widget.textbox.get_markup_geometry(text, s, instance.font)['width']
|
||||||
|
end
|
||||||
|
|
||||||
scr = get_screen(scr)
|
scr = get_screen(scr)
|
||||||
if not instance.prompt.width then
|
if not instance.prompt.width then
|
||||||
instance.prompt.width = compute_text_width(instance.prompt.prompt, scr)
|
instance.prompt.width = compute_text_width(instance.prompt.prompt, scr)
|
||||||
|
@ -235,16 +243,19 @@ local function get_current_page(all_items, query, scr)
|
||||||
if not menubar.right_label_width then
|
if not menubar.right_label_width then
|
||||||
menubar.right_label_width = compute_text_width(menubar.right_label, scr)
|
menubar.right_label_width = compute_text_width(menubar.right_label, scr)
|
||||||
end
|
end
|
||||||
|
local border_width = theme.menubar_border_width or theme.menu_border_width or 0
|
||||||
local available_space = instance.geometry.width - menubar.right_margin -
|
local available_space = instance.geometry.width - menubar.right_margin -
|
||||||
menubar.right_label_width - menubar.left_label_width -
|
menubar.right_label_width - menubar.left_label_width -
|
||||||
compute_text_width(query, scr) - instance.prompt.width
|
compute_text_width(query..' ', scr) - instance.prompt.width - border_width * 2
|
||||||
|
-- space character is added as input cursor placeholder
|
||||||
|
|
||||||
local width_sum = 0
|
local width_sum = 0
|
||||||
local current_page = {}
|
local current_page = {}
|
||||||
for i, item in ipairs(all_items) do
|
for i, item in ipairs(all_items) do
|
||||||
item.width = item.width or
|
item.width = item.width or (
|
||||||
compute_text_width(item.name, scr) +
|
compute_text_width(label(item), scr) +
|
||||||
(item.icon and instance.geometry.height or 0) + list_interspace
|
(item.icon and (instance.geometry.height + list_spacing) or 0) + list_spacing * 2
|
||||||
|
)
|
||||||
if width_sum + item.width > available_space then
|
if width_sum + item.width > available_space then
|
||||||
if current_item < i then
|
if current_item < i then
|
||||||
table.insert(current_page, { name = menubar.right_label, icon = nil })
|
table.insert(current_page, { name = menubar.right_label, icon = nil })
|
||||||
|
@ -449,6 +460,7 @@ function menubar.show(scr)
|
||||||
local bg_color = theme.menubar_bg_normal or theme.menu_bg_normal or theme.bg_normal
|
local bg_color = theme.menubar_bg_normal or theme.menu_bg_normal or theme.bg_normal
|
||||||
local border_width = theme.menubar_border_width or theme.menu_border_width or 0
|
local border_width = theme.menubar_border_width or theme.menu_border_width or 0
|
||||||
local border_color = theme.menubar_border_color or theme.menu_border_color
|
local border_color = theme.menubar_border_color or theme.menu_border_color
|
||||||
|
local font = theme.menubar_font or theme.font or "Monospace 10"
|
||||||
|
|
||||||
if not instance then
|
if not instance then
|
||||||
-- Add to each category the name of its key in all_categories
|
-- Add to each category the name of its key in all_categories
|
||||||
|
@ -467,11 +479,13 @@ function menubar.show(scr)
|
||||||
fg = fg_color,
|
fg = fg_color,
|
||||||
border_width = border_width,
|
border_width = border_width,
|
||||||
border_color = border_color,
|
border_color = border_color,
|
||||||
|
font = font,
|
||||||
},
|
},
|
||||||
widget = common_args.w,
|
widget = common_args.w,
|
||||||
prompt = awful.widget.prompt(),
|
prompt = awful.widget.prompt(),
|
||||||
query = nil,
|
query = nil,
|
||||||
count_table = nil,
|
count_table = nil,
|
||||||
|
font = font,
|
||||||
}
|
}
|
||||||
local layout = wibox.layout.fixed.horizontal()
|
local layout = wibox.layout.fixed.horizontal()
|
||||||
layout:add(instance.prompt)
|
layout:add(instance.prompt)
|
||||||
|
@ -490,7 +504,7 @@ function menubar.show(scr)
|
||||||
local geometry = menubar.geometry
|
local geometry = menubar.geometry
|
||||||
instance.geometry = {x = geometry.x or scrgeom.x,
|
instance.geometry = {x = geometry.x or scrgeom.x,
|
||||||
y = geometry.y or scrgeom.y,
|
y = geometry.y or scrgeom.y,
|
||||||
height = geometry.height or gmath.round(theme.get_font_height() * 1.5),
|
height = geometry.height or gmath.round(theme.get_font_height(font) * 1.5),
|
||||||
width = (geometry.width or scrgeom.width) - border_width * 2}
|
width = (geometry.width or scrgeom.width) - border_width * 2}
|
||||||
instance.wibox:geometry(instance.geometry)
|
instance.wibox:geometry(instance.geometry)
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ local glib = lgi.GLib
|
||||||
local w_textbox = require("wibox.widget.textbox")
|
local w_textbox = require("wibox.widget.textbox")
|
||||||
local gdebug = require("gears.debug")
|
local gdebug = require("gears.debug")
|
||||||
local protected_call = require("gears.protected_call")
|
local protected_call = require("gears.protected_call")
|
||||||
local gstring = require("gears.string")
|
|
||||||
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
||||||
|
|
||||||
local utils = {}
|
local utils = {}
|
||||||
|
@ -410,6 +409,8 @@ function utils.parse_dir(dir_path, callback)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- luacov: disable
|
||||||
|
|
||||||
function utils.compute_textbox_width(textbox, s)
|
function utils.compute_textbox_width(textbox, s)
|
||||||
gdebug.deprecate("Use 'width, _ = textbox:get_preferred_size(s)' directly.", {deprecated_in=4})
|
gdebug.deprecate("Use 'width, _ = textbox:get_preferred_size(s)' directly.", {deprecated_in=4})
|
||||||
s = screen[s or mouse.screen]
|
s = screen[s or mouse.screen]
|
||||||
|
@ -417,16 +418,13 @@ function utils.compute_textbox_width(textbox, s)
|
||||||
return w
|
return w
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Compute text width.
|
function utils.compute_text_width(text, s, font)
|
||||||
-- @tparam str text Text.
|
gdebug.deprecate("Use 'width = textbox.get_markup_geometry(text, s, font)['width']'.", {deprecated_in=4})
|
||||||
-- @tparam number|screen s Screen
|
return w_textbox.get_markup_geometry(text, s, font)['width']
|
||||||
-- @treturn int Text width.
|
|
||||||
-- @staticfct menubar.utils.compute_text_width
|
|
||||||
function utils.compute_text_width(text, s)
|
|
||||||
local w, _ = w_textbox(gstring.xml_escape(text)):get_preferred_size(s)
|
|
||||||
return w
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- luacov: enable
|
||||||
|
|
||||||
return utils
|
return utils
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -57,7 +57,13 @@ local action = {}
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
|
||||||
--- When a notification is invoked.
|
--- When a notification is invoked.
|
||||||
|
--
|
||||||
|
-- Note that it is possible to call `:invoke()` without a notification object.
|
||||||
|
-- It is possible the `notification` parameter will be nil.
|
||||||
|
--
|
||||||
-- @signal invoked
|
-- @signal invoked
|
||||||
|
-- @tparam naughty.action action The action.
|
||||||
|
-- @tparam naughty.notification|nil notification The notification, if known.
|
||||||
|
|
||||||
function action:get_selected()
|
function action:get_selected()
|
||||||
return self._private.selected
|
return self._private.selected
|
||||||
|
|
|
@ -191,7 +191,13 @@ naughty.notifications = { suspended = { }, _expired = {{}} }
|
||||||
|
|
||||||
naughty._active = {}
|
naughty._active = {}
|
||||||
|
|
||||||
screen.connect_for_each_screen(function(s)
|
local function get_screen(s)
|
||||||
|
return s and capi.screen[s]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function init_screen(s)
|
||||||
|
if naughty.notifications[s] then return end
|
||||||
|
|
||||||
naughty.notifications[s] = {
|
naughty.notifications[s] = {
|
||||||
top_left = {},
|
top_left = {},
|
||||||
top_middle = {},
|
top_middle = {},
|
||||||
|
@ -201,7 +207,9 @@ screen.connect_for_each_screen(function(s)
|
||||||
bottom_right = {},
|
bottom_right = {},
|
||||||
middle = {},
|
middle = {},
|
||||||
}
|
}
|
||||||
end)
|
end
|
||||||
|
|
||||||
|
screen.connect_for_each_screen(init_screen)
|
||||||
|
|
||||||
capi.screen.connect_signal("removed", function(scr)
|
capi.screen.connect_signal("removed", function(scr)
|
||||||
-- Allow the notifications to be moved to another screen.
|
-- Allow the notifications to be moved to another screen.
|
||||||
|
@ -214,15 +222,18 @@ capi.screen.connect_signal("removed", function(scr)
|
||||||
naughty.emit_signal("request::screen", n, "removed", {})
|
naughty.emit_signal("request::screen", n, "removed", {})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for _, n in ipairs(naughty._active) do
|
||||||
|
if n._private.args and get_screen(n._private.args.screen) == scr then
|
||||||
|
n._private.args.screen = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Destroy all notifications on this screen
|
-- Destroy all notifications on this screen
|
||||||
naughty.destroy_all_notifications({scr})
|
naughty.destroy_all_notifications({scr})
|
||||||
naughty.notifications[scr] = nil
|
naughty.notifications[scr] = nil
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local function get_screen(s)
|
|
||||||
return s and capi.screen[s]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function remove_from_index(n)
|
local function remove_from_index(n)
|
||||||
for _, positions in pairs(naughty.notifications) do
|
for _, positions in pairs(naughty.notifications) do
|
||||||
for _, ns in pairs(positions) do
|
for _, ns in pairs(positions) do
|
||||||
|
@ -646,6 +657,10 @@ local function register(notification, args)
|
||||||
|
|
||||||
assert(s)
|
assert(s)
|
||||||
|
|
||||||
|
if not naughty.notifications[s] then
|
||||||
|
init_screen(get_screen(s))
|
||||||
|
end
|
||||||
|
|
||||||
-- insert the notification to the table
|
-- insert the notification to the table
|
||||||
table.insert(naughty._active, notification)
|
table.insert(naughty._active, notification)
|
||||||
table.insert(naughty.notifications[s][notification.position], notification)
|
table.insert(naughty.notifications[s][notification.position], notification)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
-- @copyright 2017 Emmanuel Lepage Vallee
|
-- @copyright 2017 Emmanuel Lepage Vallee
|
||||||
-- @popupmod naughty.layout.box
|
-- @popupmod naughty.layout.box
|
||||||
|
-- @supermodule awful.popup
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
|
|
||||||
local capi = {screen=screen}
|
local capi = {screen=screen}
|
||||||
|
|
|
@ -62,6 +62,12 @@ screen.connect_for_each_screen(function(s)
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
capi.screen.connect_signal("removed", function(s)
|
||||||
|
timer.delayed_call(function()
|
||||||
|
current_notifications[s] = nil
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
--- Sum heights of notifications at position
|
--- Sum heights of notifications at position
|
||||||
--
|
--
|
||||||
-- @param s Screen to use
|
-- @param s Screen to use
|
||||||
|
@ -128,7 +134,7 @@ local function get_offset(s, position, idx, width, height)
|
||||||
local find_old_to_replace = function()
|
local find_old_to_replace = function()
|
||||||
for i = 1, idx-1 do
|
for i = 1, idx-1 do
|
||||||
local n = current_notifications[s][position][i]
|
local n = current_notifications[s][position][i]
|
||||||
if n.timeout > 0 then
|
if n and n.timeout > 0 then
|
||||||
return n
|
return n
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -337,7 +337,10 @@ local function new(_, args)
|
||||||
update_style(wdg)
|
update_style(wdg)
|
||||||
|
|
||||||
wdg._private.default_buttons = gtable.join(
|
wdg._private.default_buttons = gtable.join(
|
||||||
abutton({ }, 1, function(a) a:invoke(args.notification) end)
|
abutton({ }, 1, function(a)
|
||||||
|
local notif = wdg._private.notification or args.notification
|
||||||
|
a:invoke(notif)
|
||||||
|
end)
|
||||||
)
|
)
|
||||||
|
|
||||||
return wdg
|
return wdg
|
||||||
|
|
|
@ -570,6 +570,27 @@ function notification:set_timeout(timeout)
|
||||||
self:emit_signal("property::timeout", timeout)
|
self:emit_signal("property::timeout", timeout)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function notification:get_message()
|
||||||
|
-- This property was called "text" in older versions.
|
||||||
|
-- Some modules like `lain` abused of the presets (they
|
||||||
|
-- had little choice at the time) to set the message on
|
||||||
|
-- an existing popup.
|
||||||
|
local p = rawget(self, "preset") or {}
|
||||||
|
local message = self._private.message or p.message or ""
|
||||||
|
|
||||||
|
if message == "" and p.text and p.text ~= "" then
|
||||||
|
gdebug.deprecate(
|
||||||
|
"Using the preset configuration to set the notification "..
|
||||||
|
"message is not supported. Please use `n.message = 'text'`.",
|
||||||
|
{deprecated_in=5}
|
||||||
|
)
|
||||||
|
|
||||||
|
return p.text
|
||||||
|
end
|
||||||
|
|
||||||
|
return message
|
||||||
|
end
|
||||||
|
|
||||||
function notification:set_text(txt)
|
function notification:set_text(txt)
|
||||||
gdebug.deprecate(
|
gdebug.deprecate(
|
||||||
"The `text` attribute is deprecated, use `message`",
|
"The `text` attribute is deprecated, use `message`",
|
||||||
|
@ -861,7 +882,15 @@ local function select_legacy_preset(n, args)
|
||||||
))
|
))
|
||||||
|
|
||||||
for k, v in pairs(n.preset) do
|
for k, v in pairs(n.preset) do
|
||||||
n._private[k] = v
|
-- Don't keep a strong reference to the screen, Lua 5.1 GC wont be
|
||||||
|
-- smart enough to unwind the mess of circular weak references.
|
||||||
|
if k ~= "screen" then
|
||||||
|
n._private[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if n.preset.screen then
|
||||||
|
n._private.weak_screen[1] = capi.screen[n.preset.screen]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -160,6 +160,15 @@ end
|
||||||
-- @staticfct ruled.notification.apply
|
-- @staticfct ruled.notification.apply
|
||||||
function module.apply(n)
|
function module.apply(n)
|
||||||
local callbacks, props = {}, {}
|
local callbacks, props = {}, {}
|
||||||
|
|
||||||
|
if n.preset then
|
||||||
|
for k, v in pairs(n.preset) do
|
||||||
|
if not n._private[v] then
|
||||||
|
props[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
for _, v in ipairs(nrules._matching_source) do
|
for _, v in ipairs(nrules._matching_source) do
|
||||||
v.callback(nrules, n, props, callbacks)
|
v.callback(nrules, n, props, callbacks)
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
-- @copyright 2013 Emmanuel Lepage Vallee
|
-- @copyright 2013 Emmanuel Lepage Vallee
|
||||||
-- @containermod wibox.container.arcchart
|
-- @containermod wibox.container.arcchart
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -357,10 +358,6 @@ function arcchart.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(arcchart, arcchart.mt)
|
return setmetatable(arcchart, arcchart.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2010 Uli Schlachter
|
-- @copyright 2010 Uli Schlachter
|
||||||
-- @containermod wibox.container.background
|
-- @containermod wibox.container.background
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local base = require("wibox.widget.base")
|
local base = require("wibox.widget.base")
|
||||||
|
@ -484,10 +485,6 @@ function background.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(background, background.mt)
|
return setmetatable(background, background.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
-- @author Lukáš Hrázký
|
-- @author Lukáš Hrázký
|
||||||
-- @copyright 2012 Lukáš Hrázký
|
-- @copyright 2012 Lukáš Hrázký
|
||||||
-- @containermod wibox.container.constraint
|
-- @containermod wibox.container.constraint
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -176,10 +177,6 @@ function constraint.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(constraint, constraint.mt)
|
return setmetatable(constraint, constraint.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -17,6 +17,7 @@ return setmetatable({
|
||||||
radialprogressbar = require("wibox.container.radialprogressbar");
|
radialprogressbar = require("wibox.container.radialprogressbar");
|
||||||
arcchart = require("wibox.container.arcchart");
|
arcchart = require("wibox.container.arcchart");
|
||||||
place = require("wibox.container.place");
|
place = require("wibox.container.place");
|
||||||
|
tile = require("wibox.container.tile");
|
||||||
}, {__call = function(_, args) return base.make_widget_declarative(args) end})
|
}, {__call = function(_, args) return base.make_widget_declarative(args) end})
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2010 Uli Schlachter
|
-- @copyright 2010 Uli Schlachter
|
||||||
-- @containermod wibox.container.margin
|
-- @containermod wibox.container.margin
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local pairs = pairs
|
local pairs = pairs
|
||||||
|
@ -238,10 +239,6 @@ function margin.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(margin, margin.mt)
|
return setmetatable(margin, margin.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
-- @author dodo
|
-- @author dodo
|
||||||
-- @copyright 2012 dodo
|
-- @copyright 2012 dodo
|
||||||
-- @containermod wibox.container.mirror
|
-- @containermod wibox.container.mirror
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local type = type
|
local type = type
|
||||||
|
@ -129,10 +130,6 @@ function mirror.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(mirror, mirror.mt)
|
return setmetatable(mirror, mirror.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
-- @copyright 2016 Emmanuel Lepage Vallee
|
-- @copyright 2016 Emmanuel Lepage Vallee
|
||||||
-- @containermod wibox.container.place
|
-- @containermod wibox.container.place
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -22,13 +23,8 @@ local align_fct = {
|
||||||
}
|
}
|
||||||
align_fct.top, align_fct.bottom = align_fct.left, align_fct.right
|
align_fct.top, align_fct.bottom = align_fct.left, align_fct.right
|
||||||
|
|
||||||
-- Layout this layout
|
-- Shared with some subclasses like the `tiled` and `scaled` modules.
|
||||||
function place:layout(context, width, height)
|
function place:_layout(context, width, height)
|
||||||
|
|
||||||
if not self._private.widget then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local w, h = base.fit_widget(self, context, self._private.widget, width, height)
|
local w, h = base.fit_widget(self, context, self._private.widget, width, height)
|
||||||
|
|
||||||
if self._private.content_fill_horizontal then
|
if self._private.content_fill_horizontal then
|
||||||
|
@ -44,6 +40,21 @@ function place:layout(context, width, height)
|
||||||
|
|
||||||
local x, y = align_fct[halign](w, width), align_fct[valign](h, height)
|
local x, y = align_fct[halign](w, width), align_fct[valign](h, height)
|
||||||
|
|
||||||
|
-- Sub pixels makes everything blurry. This is now what people expect.
|
||||||
|
x, y = math.floor(x), math.floor(y)
|
||||||
|
|
||||||
|
return x, y, w, h
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Layout this layout
|
||||||
|
function place:layout(context, width, height)
|
||||||
|
|
||||||
|
if not self._private.widget then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local x, y, w, h = self:_layout(context, width, height)
|
||||||
|
|
||||||
return { base.place_widget_at(self._private.widget, x, y, w, h) }
|
return { base.place_widget_at(self._private.widget, x, y, w, h) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -96,6 +107,8 @@ end
|
||||||
-- * *center* (default)
|
-- * *center* (default)
|
||||||
-- * *bottom*
|
-- * *bottom*
|
||||||
--
|
--
|
||||||
|
--@DOC_wibox_container_place_valign_EXAMPLE@
|
||||||
|
--
|
||||||
-- @property valign
|
-- @property valign
|
||||||
-- @tparam[opt="center"] string valign
|
-- @tparam[opt="center"] string valign
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
@ -108,6 +121,8 @@ end
|
||||||
-- * *center* (default)
|
-- * *center* (default)
|
||||||
-- * *right*
|
-- * *right*
|
||||||
--
|
--
|
||||||
|
--@DOC_wibox_container_place_halign_EXAMPLE@
|
||||||
|
--
|
||||||
-- @property halign
|
-- @property halign
|
||||||
-- @tparam[opt="center"] string halign
|
-- @tparam[opt="center"] string halign
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
@ -158,6 +173,8 @@ end
|
||||||
|
|
||||||
--- Stretch the contained widget so it takes all the vertical space.
|
--- Stretch the contained widget so it takes all the vertical space.
|
||||||
--
|
--
|
||||||
|
--@DOC_wibox_container_place_content_fill_vertical_EXAMPLE@
|
||||||
|
--
|
||||||
-- @property content_fill_vertical
|
-- @property content_fill_vertical
|
||||||
-- @tparam[opt=false] boolean content_fill_vertical
|
-- @tparam[opt=false] boolean content_fill_vertical
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
@ -170,6 +187,8 @@ end
|
||||||
|
|
||||||
--- Stretch the contained widget so it takes all the horizontal space.
|
--- Stretch the contained widget so it takes all the horizontal space.
|
||||||
--
|
--
|
||||||
|
--@DOC_wibox_container_place_content_fill_horizontal_EXAMPLE@
|
||||||
|
--
|
||||||
-- @property content_fill_horizontal
|
-- @property content_fill_horizontal
|
||||||
-- @tparam[opt=false] boolean content_fill_horizontal
|
-- @tparam[opt=false] boolean content_fill_horizontal
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
@ -203,10 +222,6 @@ function place.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(place, place.mt)
|
return setmetatable(place, place.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
-- @copyright 2013 Emmanuel Lepage Vallee
|
-- @copyright 2013 Emmanuel Lepage Vallee
|
||||||
-- @containermod wibox.container.radialprogressbar
|
-- @containermod wibox.container.radialprogressbar
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -281,10 +282,6 @@ function radialprogressbar.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(radialprogressbar, radialprogressbar.mt)
|
return setmetatable(radialprogressbar, radialprogressbar.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2010 Uli Schlachter
|
-- @copyright 2010 Uli Schlachter
|
||||||
-- @containermod wibox.container.rotate
|
-- @containermod wibox.container.rotate
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local error = error
|
local error = error
|
||||||
|
@ -149,10 +150,6 @@ function rotate.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(rotate, rotate.mt)
|
return setmetatable(rotate, rotate.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
-- @author Uli Schlachter (based on ideas from Saleur Geoffrey)
|
-- @author Uli Schlachter (based on ideas from Saleur Geoffrey)
|
||||||
-- @copyright 2015 Uli Schlachter
|
-- @copyright 2015 Uli Schlachter
|
||||||
-- @containermod wibox.container.scroll
|
-- @containermod wibox.container.scroll
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local cache = require("gears.cache")
|
local cache = require("gears.cache")
|
||||||
|
@ -549,10 +550,6 @@ function scroll.step_functions.waiting_nonlinear_back_and_forth(elapsed, size, v
|
||||||
return (size - visible_size) * state
|
return (size - visible_size) * state
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return scroll
|
return scroll
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -0,0 +1,212 @@
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- Replicate the content of the widget over and over.
|
||||||
|
--
|
||||||
|
-- This contained is intended to be used for wallpapers. It currently doesn't
|
||||||
|
-- support mouse input in the replicated tiles.
|
||||||
|
--
|
||||||
|
--@DOC_wibox_container_defaults_tile_EXAMPLE@
|
||||||
|
-- @author Emmanuel Lepage-Vallee
|
||||||
|
-- @copyright 2021 Emmanuel Lepage-Vallee
|
||||||
|
-- @containermod wibox.container.tile
|
||||||
|
-- @supermodule wibox.container.place
|
||||||
|
local place = require("wibox.container.place")
|
||||||
|
local cairo = require("lgi").cairo
|
||||||
|
local widget = require("wibox.widget")
|
||||||
|
local gtable = require("gears.table")
|
||||||
|
|
||||||
|
local module = {mt = {}}
|
||||||
|
|
||||||
|
function module:draw(context, cr, width, height)
|
||||||
|
if not self._private.tiled then return end
|
||||||
|
if not self._private.widget then return end
|
||||||
|
|
||||||
|
local x, y, w, h = self:_layout(context, width, height)
|
||||||
|
|
||||||
|
local vspace, hspace = self.vertical_spacing, self.horizontal_spacing
|
||||||
|
local vcrop, hcrop = self.vertical_crop, self.horizontal_crop
|
||||||
|
|
||||||
|
-- In theory we could avoid a few repaints by tracking the child widget
|
||||||
|
-- redraw independently from the container redraw. However it is nearly a
|
||||||
|
-- 1:1 march, so there's little reasons to do it.
|
||||||
|
if not self._private.surface then
|
||||||
|
self._private.surface = cairo.ImageSurface(cairo.Format.ARGB32, w+hspace, h+vspace)
|
||||||
|
self._private.cr = cairo.Context(self._private.surface)
|
||||||
|
self._private.cr:set_source(cr:get_source())
|
||||||
|
self._private.pattern = cairo.Pattern.create_for_surface(self._private.surface)
|
||||||
|
self._private.pattern.extend = cairo.Extend.REPEAT
|
||||||
|
self._private.cr:translate(math.ceil(hspace), math.ceil(vspace))
|
||||||
|
else
|
||||||
|
self._private.cr:set_operator(cairo.Operator.CLEAR)
|
||||||
|
self._private.cr:set_source_rgba(0,0,0,1)
|
||||||
|
self._private.cr:paint()
|
||||||
|
self._private.cr:set_operator(cairo.Operator.SOURCE)
|
||||||
|
end
|
||||||
|
|
||||||
|
widget.draw_to_cairo_context(self._private.widget, self._private.cr, w, h, context)
|
||||||
|
|
||||||
|
cr:save()
|
||||||
|
|
||||||
|
-- We do our own clip.
|
||||||
|
cr:reset_clip()
|
||||||
|
|
||||||
|
local x0, y0 = 0, 0
|
||||||
|
|
||||||
|
-- Avoid painting incomplete tiles
|
||||||
|
if hcrop and x ~= 0 then
|
||||||
|
x0 = x - math.floor(x/(w+hspace))*(w+hspace)
|
||||||
|
end
|
||||||
|
|
||||||
|
if hcrop then
|
||||||
|
width = x + w + hspace + math.floor((width - (x + w + hspace))/(w+hspace))*(w+hspace)
|
||||||
|
end
|
||||||
|
|
||||||
|
if vcrop and y ~= 0 then
|
||||||
|
y0 = y - math.floor(y/(h+vspace))*(h+vspace)
|
||||||
|
end
|
||||||
|
|
||||||
|
if vcrop then
|
||||||
|
height = (y+h+vspace) + math.floor((height - (y+h+vspace))/(h+vspace))*(h+vspace)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create a clip around the "real" widget in case there is some transparency.
|
||||||
|
cr:rectangle(x0, y0, width-x0, y-y0)
|
||||||
|
cr:rectangle(x0, y0, x-hspace-x0, height-y0)
|
||||||
|
cr:rectangle(x+hspace+w, y0, width - (x+w+hspace), height-y0)
|
||||||
|
cr:rectangle(x, y+vspace+h, w+hspace, height - (y+h+vspace))
|
||||||
|
cr:clip()
|
||||||
|
|
||||||
|
-- Make sure the tiles are aligned with the child widget.
|
||||||
|
cr:translate(x - hspace, y - vspace)
|
||||||
|
|
||||||
|
|
||||||
|
-- Use OVER rather than SOURCE to preserve the alpha.
|
||||||
|
cr.operator = cairo.Operator.OVER
|
||||||
|
cr.source = self._private.pattern
|
||||||
|
cr:paint()
|
||||||
|
|
||||||
|
cr:restore()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- The horizontal spacing between the tiled.
|
||||||
|
--
|
||||||
|
--@DOC_wibox_container_tile_horizontal_spacing_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property horizontal_spacing
|
||||||
|
-- @tparam number horizontal_spacing
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see vertical_spacing
|
||||||
|
|
||||||
|
--- The vertical spacing between the tiled.
|
||||||
|
--
|
||||||
|
--@DOC_wibox_container_tile_vertical_spacing_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property vertical_spacing
|
||||||
|
-- @tparam number vertical_spacing
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see horizontal_spacing
|
||||||
|
|
||||||
|
--- Avoid painting incomplete horizontal tiles.
|
||||||
|
--
|
||||||
|
--@DOC_wibox_container_tile_horizontal_crop_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property horizontal_crop
|
||||||
|
-- @tparam[opt=false] boolean tiled
|
||||||
|
-- @see vertical_crop
|
||||||
|
|
||||||
|
--- Avoid painting incomplete vertical tiles.
|
||||||
|
--
|
||||||
|
--@DOC_wibox_container_tile_vertical_crop_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property vertical_crop
|
||||||
|
-- @tparam[opt=false] boolean tiled
|
||||||
|
-- @see horizontal_crop
|
||||||
|
|
||||||
|
--- Enable or disable the tiling.
|
||||||
|
--
|
||||||
|
-- When set to `false`, this container behaves exactly like
|
||||||
|
-- `wibox.container.place`.
|
||||||
|
--
|
||||||
|
--@DOC_wibox_container_tile_tiled_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property tiled
|
||||||
|
-- @tparam[opt=true] boolean tiled
|
||||||
|
|
||||||
|
local defaults = {
|
||||||
|
horizontal_spacing = 0,
|
||||||
|
vertical_spacing = 0,
|
||||||
|
tiled = true,
|
||||||
|
horizontal_crop = false,
|
||||||
|
vertical_crop = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
for prop in pairs(defaults) do
|
||||||
|
|
||||||
|
module["set_"..prop] = function(self, value)
|
||||||
|
self._private[prop] = value
|
||||||
|
self:emit_signal("widget::redraw_needed", value)
|
||||||
|
end
|
||||||
|
|
||||||
|
module["get_"..prop] = function(self)
|
||||||
|
if self._private[prop] == nil then
|
||||||
|
return defaults[prop]
|
||||||
|
end
|
||||||
|
|
||||||
|
return self._private[prop]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function new(_, args)
|
||||||
|
args = args or {}
|
||||||
|
local ret = place(args.widget, args.halign, args.valign)
|
||||||
|
gtable.crush(ret, module, true)
|
||||||
|
ret._private.tiled = true
|
||||||
|
|
||||||
|
local function redraw()
|
||||||
|
ret:emit_signal("widget::redraw_needed")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Resize the pattern as needed.
|
||||||
|
local function reset()
|
||||||
|
if ret._private.surface then
|
||||||
|
ret._private.surface:finish()
|
||||||
|
end
|
||||||
|
|
||||||
|
ret._private.cr = nil
|
||||||
|
ret._private.surface = nil
|
||||||
|
ret._private.pattern = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local w = nil
|
||||||
|
|
||||||
|
ret:connect_signal("property::widget", function()
|
||||||
|
reset()
|
||||||
|
|
||||||
|
if w then
|
||||||
|
w:disconnect_signal("widget::redraw_needed", redraw)
|
||||||
|
w:disconnect_signal("widget::layout_changed", reset)
|
||||||
|
end
|
||||||
|
|
||||||
|
w = ret._private.widget
|
||||||
|
|
||||||
|
if w then
|
||||||
|
w:connect_signal("widget::redraw_needed", redraw)
|
||||||
|
w:connect_signal("widget::layout_changed", reset)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new tile container.
|
||||||
|
-- @tparam table args
|
||||||
|
-- @tparam wibox.widget widget args.widget The widget to tile.
|
||||||
|
-- @tparam string args.halign Either `left`, `right` or `center`.
|
||||||
|
-- @tparam string args.valign Either `top`, `bottom` or `center`.
|
||||||
|
-- @constructorfct wibox.container.tile
|
||||||
|
function module.mt:__call(...)
|
||||||
|
return new(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(module, module.mt)
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
|
@ -1,9 +1,29 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
-- The `align` layout has three slots for child widgets. On its main axis, it
|
||||||
|
-- will use as much space as is available to it and distribute that to its child
|
||||||
|
-- widgets by stretching or shrinking them based on the chosen @{expand}
|
||||||
|
-- strategy.
|
||||||
|
-- On its secondary axis, the biggest child widget determines the size of the
|
||||||
|
-- layout, but smaller widgets will not be stretched to match it.
|
||||||
|
--
|
||||||
|
-- In its default configuration, the layout will give the first and third
|
||||||
|
-- widgets only the minimum space they ask for and it aligns them to the outer
|
||||||
|
-- edges. The remaining space between them is made available to the widget in
|
||||||
|
-- slot two.
|
||||||
|
--
|
||||||
|
-- This layout is most commonly used to split content into left/top, center and
|
||||||
|
-- right/bottom sections. As such, it is usually seen as the root layout in
|
||||||
|
-- @{awful.wibar}.
|
||||||
|
--
|
||||||
|
-- You may also fill just one or two of the widget slots, the @{expand} algorithm
|
||||||
|
-- will adjust accordingly.
|
||||||
--
|
--
|
||||||
--@DOC_wibox_layout_defaults_align_EXAMPLE@
|
--@DOC_wibox_layout_defaults_align_EXAMPLE@
|
||||||
|
--
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2010 Uli Schlachter
|
-- @copyright 2010 Uli Schlachter
|
||||||
-- @layoutmod wibox.layout.align
|
-- @layoutmod wibox.layout.align
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local table = table
|
local table = table
|
||||||
|
@ -139,8 +159,10 @@ function align:layout(context, width, height)
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the layout's first widget.
|
--- The widget in slot one.
|
||||||
-- This is the widget that is at the left/top
|
--
|
||||||
|
-- This is the widget that is at the left/top.
|
||||||
|
--
|
||||||
-- @property first
|
-- @property first
|
||||||
-- @tparam widget first
|
-- @tparam widget first
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
@ -154,7 +176,10 @@ function align:set_first(widget)
|
||||||
self:emit_signal("property::first", widget)
|
self:emit_signal("property::first", widget)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the layout's second widget. This is the centered one.
|
--- The widget in slot two.
|
||||||
|
--
|
||||||
|
-- This is the centered one.
|
||||||
|
--
|
||||||
-- @property second
|
-- @property second
|
||||||
-- @tparam widget second
|
-- @tparam widget second
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
@ -168,8 +193,10 @@ function align:set_second(widget)
|
||||||
self:emit_signal("property::second", widget)
|
self:emit_signal("property::second", widget)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the layout's third widget.
|
--- The widget in slot three.
|
||||||
-- This is the widget that is at the right/bottom
|
--
|
||||||
|
-- This is the widget that is at the right/bottom.
|
||||||
|
--
|
||||||
-- @property third
|
-- @property third
|
||||||
-- @tparam widget third
|
-- @tparam widget third
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
@ -226,23 +253,27 @@ function align:fit(context, orig_width, orig_height)
|
||||||
return used_in_dir, used_in_other
|
return used_in_dir, used_in_other
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the expand mode which determines how sub widgets expand to take up
|
--- Set the expand mode, which determines how child widgets expand to take up
|
||||||
-- unused space.
|
-- unused space.
|
||||||
--
|
--
|
||||||
-- The following values are valid:
|
-- The following values are valid:
|
||||||
--
|
--
|
||||||
-- * "inside" - Default option. Size of outside widgets is determined using
|
-- * `"inside"`: The widgets in slot one and three are set to their minimal
|
||||||
-- their fit function. Second, middle, or center widget expands to fill
|
-- required size. The widget in slot two is then given the remaining space.
|
||||||
-- remaining space.
|
-- This is the default behaviour.
|
||||||
-- * "outside" - Center widget is sized using its fit function and placed in
|
-- * `"outside"`: The widget in slot two is set to its minimal required size and
|
||||||
-- the center of the allowed space. Outside widgets expand (or contract) to
|
-- placed in the center of the space available to the layout. The other
|
||||||
-- fill remaining space on their side.
|
-- widgets are then given the remaining space on either side.
|
||||||
-- * "none" - All widgets are sized using their fit function, drawn to only the
|
-- If the center widget requires all available space, the outer widgets are
|
||||||
-- returned space, or remaining space, whichever is smaller. Center widget
|
-- not drawn at all.
|
||||||
-- gets priority.
|
-- * `"none"`: All widgets are given their minimal required size or the
|
||||||
|
-- remaining space, whichever is smaller. The center widget gets priority.
|
||||||
|
--
|
||||||
|
-- Attempting to set any other value than one of those three will fall back to
|
||||||
|
-- `"inside"`.
|
||||||
--
|
--
|
||||||
-- @property expand
|
-- @property expand
|
||||||
-- @tparam[opt=inside] string mode How to use unused space.
|
-- @tparam[opt="inside"] string mode How to use unused space.
|
||||||
|
|
||||||
function align:set_expand(mode)
|
function align:set_expand(mode)
|
||||||
if mode == "none" or mode == "outside" then
|
if mode == "none" or mode == "outside" then
|
||||||
|
@ -282,14 +313,16 @@ local function get_layout(dir, first, second, third)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a new horizontal align layout. An align layout can display up to
|
--- Returns a new horizontal align layout.
|
||||||
-- three widgets. The widget set via :set_left() is left-aligned. :set_right()
|
--
|
||||||
-- sets a widget which will be right-aligned. The remaining space between those
|
-- The three widget slots are aligned left, center and right.
|
||||||
-- two will be given to the widget set via :set_middle().
|
--
|
||||||
|
-- Additionally, this creates the aliases `set_left`, `set_middle` and
|
||||||
|
-- `set_right` to assign @{first}, @{second} and @{third} respectively.
|
||||||
-- @constructorfct wibox.layout.align.horizontal
|
-- @constructorfct wibox.layout.align.horizontal
|
||||||
-- @tparam[opt] widget left Widget to be put to the left.
|
-- @tparam[opt] widget left Widget to be put in slot one.
|
||||||
-- @tparam[opt] widget middle Widget to be put to the middle.
|
-- @tparam[opt] widget middle Widget to be put in slot two.
|
||||||
-- @tparam[opt] widget right Widget to be put to the right.
|
-- @tparam[opt] widget right Widget to be put in slot three.
|
||||||
function align.horizontal(left, middle, right)
|
function align.horizontal(left, middle, right)
|
||||||
local ret = get_layout("x", left, middle, right)
|
local ret = get_layout("x", left, middle, right)
|
||||||
|
|
||||||
|
@ -300,14 +333,16 @@ function align.horizontal(left, middle, right)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a new vertical align layout. An align layout can display up to
|
--- Returns a new vertical align layout.
|
||||||
-- three widgets. The widget set via :set_top() is top-aligned. :set_bottom()
|
--
|
||||||
-- sets a widget which will be bottom-aligned. The remaining space between those
|
-- The three widget slots are aligned top, center and bottom.
|
||||||
-- two will be given to the widget set via :set_middle().
|
--
|
||||||
|
-- Additionally, this creates the aliases `set_top`, `set_middle` and
|
||||||
|
-- `set_bottom` to assign @{first}, @{second} and @{third} respectively.
|
||||||
-- @constructorfct wibox.layout.align.vertical
|
-- @constructorfct wibox.layout.align.vertical
|
||||||
-- @tparam[opt] widget top Widget to be put to the top.
|
-- @tparam[opt] widget top Widget to be put in slot one.
|
||||||
-- @tparam[opt] widget middle Widget to be put to the middle.
|
-- @tparam[opt] widget middle Widget to be put in slot two.
|
||||||
-- @tparam[opt] widget bottom Widget to be put to the right.
|
-- @tparam[opt] widget bottom Widget to be put in slot three.
|
||||||
function align.vertical(top, middle, bottom)
|
function align.vertical(top, middle, bottom)
|
||||||
local ret = get_layout("y", top, middle, bottom)
|
local ret = get_layout("y", top, middle, bottom)
|
||||||
|
|
||||||
|
@ -320,10 +355,6 @@ end
|
||||||
|
|
||||||
--@DOC_fixed_COMMON@
|
--@DOC_fixed_COMMON@
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return align
|
return align
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -1,9 +1,26 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
-- A `fixed` layout may be initialized with any number of child widgets, and
|
||||||
|
-- during runtime widgets may be added and removed dynamically.
|
||||||
|
--
|
||||||
|
-- On the main axis, child widgets are given a fixed size of exactly as much
|
||||||
|
-- space as they ask for. The layout will then resize according to the sum of
|
||||||
|
-- all child widgets. If the space available to the layout is not enough to
|
||||||
|
-- include all child widgets, the excessive ones are not drawn at all.
|
||||||
|
--
|
||||||
|
-- Additionally, the layout allows adding empty spacing or even placing a custom
|
||||||
|
-- spacing widget between the child widget.
|
||||||
|
--
|
||||||
|
-- On its secondary axis, the layout's size is determined by the largest child
|
||||||
|
-- widget. Smaller child widgets are then placed with the same size.
|
||||||
|
-- Therefore, child widgets may ignore their `forced_width` or `forced_height`
|
||||||
|
-- properties for vertical and horizontal layouts respectively.
|
||||||
--
|
--
|
||||||
--@DOC_wibox_layout_defaults_fixed_EXAMPLE@
|
--@DOC_wibox_layout_defaults_fixed_EXAMPLE@
|
||||||
|
--
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2010 Uli Schlachter
|
-- @copyright 2010 Uli Schlachter
|
||||||
-- @layoutmod wibox.layout.fixed
|
-- @layoutmod wibox.layout.fixed
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
||||||
|
@ -20,45 +37,96 @@ local fixed = {}
|
||||||
-- @param height The available height.
|
-- @param height The available height.
|
||||||
function fixed:layout(context, width, height)
|
function fixed:layout(context, width, height)
|
||||||
local result = {}
|
local result = {}
|
||||||
local pos,spacing = 0, self._private.spacing
|
local spacing = self._private.spacing or 0
|
||||||
local spacing_widget = self._private.spacing_widget
|
|
||||||
local is_y = self._private.dir == "y"
|
local is_y = self._private.dir == "y"
|
||||||
local is_x = not is_y
|
local is_x = not is_y
|
||||||
local abspace = math.abs(spacing)
|
local abspace = math.abs(spacing)
|
||||||
local spoffset = spacing < 0 and 0 or spacing
|
local spoffset = spacing < 0 and 0 or spacing
|
||||||
|
local widgets_nr = #self._private.widgets
|
||||||
|
local spacing_widget
|
||||||
|
local x, y = 0, 0
|
||||||
|
|
||||||
|
spacing_widget = spacing ~= 0 and self._private.spacing_widget or nil
|
||||||
|
|
||||||
|
for index, widget in pairs(self._private.widgets) do
|
||||||
|
local w, h, local_spacing = width - x, height - y, spacing
|
||||||
|
|
||||||
|
-- Some widget might be zero sized either because this is their
|
||||||
|
-- minimum space or just because they are really empty. In this case,
|
||||||
|
-- they must still be added to the layout. Otherwise, if their size
|
||||||
|
-- change and this layout is resizable, they are lost "forever" until
|
||||||
|
-- a full relayout is called on this fixed layout object.
|
||||||
|
local zero = false
|
||||||
|
|
||||||
for k, v in pairs(self._private.widgets) do
|
|
||||||
local x, y, w, h, _
|
|
||||||
if is_y then
|
if is_y then
|
||||||
x, y = 0, pos
|
if index ~= widgets_nr or not self._private.fill_space then
|
||||||
w, h = width, height - pos
|
h = select(2, base.fit_widget(self, context, widget, w, h))
|
||||||
if k ~= #self._private.widgets or not self._private.fill_space then
|
zero = h == 0
|
||||||
_, h = base.fit_widget(self, context, v, w, h);
|
end
|
||||||
|
|
||||||
|
if y - spacing >= height then
|
||||||
|
-- pop the spacing widget added in previous iteration if used
|
||||||
|
if spacing_widget then
|
||||||
|
table.remove(result)
|
||||||
|
|
||||||
|
-- Avoid adding zero-sized widgets at an out-of-bound
|
||||||
|
-- position.
|
||||||
|
y = y - spacing
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Never display "random" widgets as soon as a non-zero sized
|
||||||
|
-- one doesn't fit.
|
||||||
|
if not zero then
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
pos = pos + h + spacing
|
|
||||||
else
|
else
|
||||||
x, y = pos, 0
|
if index ~= widgets_nr or not self._private.fill_space then
|
||||||
w, h = width - pos, height
|
w = select(1, base.fit_widget(self, context, widget, w, h))
|
||||||
if k ~= #self._private.widgets or not self._private.fill_space then
|
zero = w == 0
|
||||||
w, _ = base.fit_widget(self, context, v, w, h);
|
end
|
||||||
|
|
||||||
|
if x - spacing >= width then
|
||||||
|
-- pop the spacing widget added in previous iteration if used
|
||||||
|
if spacing_widget then
|
||||||
|
table.remove(result)
|
||||||
|
|
||||||
|
-- Avoid adding zero-sized widgets at an out-of-bound
|
||||||
|
-- position.
|
||||||
|
x = x - spacing
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Never display "random" widgets as soon as a non-zero sized
|
||||||
|
-- one doesn't fit.
|
||||||
|
if not zero then
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
pos = pos + w + spacing
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if (is_y and pos-spacing > height) or
|
if zero then
|
||||||
(is_x and pos-spacing > width) then
|
local_spacing = 0
|
||||||
break
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add the spacing widget
|
-- Place widget, even if it has zero width/height. Otherwise
|
||||||
if k > 1 and abspace > 0 and spacing_widget then
|
-- any layout change for zero-sized widget would become invisible.
|
||||||
|
table.insert(result, base.place_widget_at(widget, x, y, w, h))
|
||||||
|
|
||||||
|
x = is_x and x + w + local_spacing or x
|
||||||
|
y = is_y and y + h + local_spacing or y
|
||||||
|
|
||||||
|
-- Add the spacing widget (if needed)
|
||||||
|
if index < widgets_nr and spacing_widget then
|
||||||
table.insert(result, base.place_widget_at(
|
table.insert(result, base.place_widget_at(
|
||||||
spacing_widget, is_x and (x - spoffset) or x, is_y and (y - spoffset) or y,
|
spacing_widget,
|
||||||
is_x and abspace or w, is_y and abspace or h
|
is_x and (x - spoffset) or x,
|
||||||
|
is_y and (y - spoffset) or y,
|
||||||
|
is_x and abspace or w,
|
||||||
|
is_y and abspace or h
|
||||||
))
|
))
|
||||||
end
|
end
|
||||||
table.insert(result, base.place_widget_at(v, x, y, w, h))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -217,9 +285,13 @@ function fixed:set(index, widget2)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- The widget used to fill the spacing between the layout elements.
|
--- A widget to insert as a separator between child widgets.
|
||||||
--
|
--
|
||||||
-- By default, no widget is used.
|
-- If this property is a valid widget and `spacing` is greater than `0`, a
|
||||||
|
-- copy of this widget is inserted between each child widget, with its size in
|
||||||
|
-- the layout's main direction determined by `spacing`.
|
||||||
|
--
|
||||||
|
-- By default no widget is used and any `spacing` is applied as an empty offset.
|
||||||
--
|
--
|
||||||
--@DOC_wibox_layout_fixed_spacing_widget_EXAMPLE@
|
--@DOC_wibox_layout_fixed_spacing_widget_EXAMPLE@
|
||||||
--
|
--
|
||||||
|
@ -261,40 +333,61 @@ end
|
||||||
-- @param orig_width The available width.
|
-- @param orig_width The available width.
|
||||||
-- @param orig_height The available height.
|
-- @param orig_height The available height.
|
||||||
function fixed:fit(context, orig_width, orig_height)
|
function fixed:fit(context, orig_width, orig_height)
|
||||||
local width, height = orig_width, orig_height
|
local width_left, height_left = orig_width, orig_height
|
||||||
local used_in_dir, used_max = 0, 0
|
local spacing = self._private.spacing or 0
|
||||||
|
local widgets_nr = #self._private.widgets
|
||||||
|
local is_y = self._private.dir == "y"
|
||||||
|
local used_max = 0
|
||||||
|
|
||||||
for _, v in pairs(self._private.widgets) do
|
-- when no widgets exist the function can be called with orig_width or
|
||||||
local w, h = base.fit_widget(self, context, v, width, height)
|
-- orig_height equal to nil. Exit early in this case.
|
||||||
local in_dir, max
|
if widgets_nr == 0 then
|
||||||
if self._private.dir == "y" then
|
return 0, 0
|
||||||
max, in_dir = w, h
|
end
|
||||||
height = height - in_dir
|
|
||||||
|
for k, v in pairs(self._private.widgets) do
|
||||||
|
local w, h = base.fit_widget(self, context, v, width_left, height_left)
|
||||||
|
local max
|
||||||
|
|
||||||
|
if is_y then
|
||||||
|
max = w
|
||||||
|
height_left = height_left - h
|
||||||
else
|
else
|
||||||
in_dir, max = w, h
|
max = h
|
||||||
width = width - in_dir
|
width_left = width_left - w
|
||||||
end
|
end
|
||||||
|
|
||||||
if max > used_max then
|
if max > used_max then
|
||||||
used_max = max
|
used_max = max
|
||||||
end
|
end
|
||||||
used_in_dir = used_in_dir + in_dir
|
|
||||||
|
|
||||||
if width <= 0 or height <= 0 then
|
if k < widgets_nr then
|
||||||
if self._private.dir == "y" then
|
if is_y then
|
||||||
used_in_dir = orig_height
|
height_left = height_left - spacing
|
||||||
else
|
else
|
||||||
used_in_dir = orig_width
|
width_left = width_left - spacing
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if width_left <= 0 or height_left <= 0 then
|
||||||
|
-- this complicated two lines determine whether we're out-of-space
|
||||||
|
-- because of spacing, or if the last widget doesn't fit in
|
||||||
|
if is_y then
|
||||||
|
height_left = k < widgets_nr and height_left + spacing or height_left
|
||||||
|
height_left = height_left < 0 and 0 or height_left
|
||||||
|
else
|
||||||
|
width_left = k < widgets_nr and width_left + spacing or width_left
|
||||||
|
width_left = width_left < 0 and 0 or width_left
|
||||||
end
|
end
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local spacing = self._private.spacing * (#self._private.widgets-1)
|
if is_y then
|
||||||
|
return used_max, orig_height - height_left
|
||||||
if self._private.dir == "y" then
|
|
||||||
return used_max, used_in_dir + spacing
|
|
||||||
end
|
end
|
||||||
return used_in_dir + spacing, used_max
|
|
||||||
|
return orig_width - width_left, used_max
|
||||||
end
|
end
|
||||||
|
|
||||||
function fixed:reset()
|
function fixed:reset()
|
||||||
|
@ -336,29 +429,25 @@ local function get_layout(dir, widget1, ...)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a new horizontal fixed layout. Each widget will get as much space as it
|
--- Creates and returns a new horizontal fixed layout.
|
||||||
-- asks for and each widget will be drawn next to its neighboring widget.
|
--
|
||||||
-- Widgets can be added via :add() or as arguments to this function.
|
|
||||||
-- Note that widgets ignore `forced_height`. They will use the preferred/minimum width
|
|
||||||
-- on the horizontal axis, and a stretched height on the vertical axis.
|
|
||||||
-- @tparam widget ... Widgets that should be added to the layout.
|
-- @tparam widget ... Widgets that should be added to the layout.
|
||||||
-- @constructorfct wibox.layout.fixed.horizontal
|
-- @constructorfct wibox.layout.fixed.horizontal
|
||||||
function fixed.horizontal(...)
|
function fixed.horizontal(...)
|
||||||
return get_layout("x", ...)
|
return get_layout("x", ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a new vertical fixed layout. Each widget will get as much space as it
|
--- Creates and returns a new vertical fixed layout.
|
||||||
-- asks for and each widget will be drawn next to its neighboring widget.
|
--
|
||||||
-- Widgets can be added via :add() or as arguments to this function.
|
|
||||||
-- Note that widgets ignore `forced_width`. They will use the preferred/minimum height
|
|
||||||
-- on the vertical axis, and a stretched width on the horizontal axis.
|
|
||||||
-- @tparam widget ... Widgets that should be added to the layout.
|
-- @tparam widget ... Widgets that should be added to the layout.
|
||||||
-- @constructorfct wibox.layout.fixed.vertical
|
-- @constructorfct wibox.layout.fixed.vertical
|
||||||
function fixed.vertical(...)
|
function fixed.vertical(...)
|
||||||
return get_layout("y", ...)
|
return get_layout("y", ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add spacing between each layout widgets.
|
--- The amount of space inserted between the child widgets.
|
||||||
|
--
|
||||||
|
-- If a `spacing_widget` is defined, this value is used for its size.
|
||||||
--
|
--
|
||||||
--@DOC_wibox_layout_fixed_spacing_EXAMPLE@
|
--@DOC_wibox_layout_fixed_spacing_EXAMPLE@
|
||||||
--
|
--
|
||||||
|
@ -381,10 +470,6 @@ end
|
||||||
|
|
||||||
--@DOC_fixed_COMMON@
|
--@DOC_fixed_COMMON@
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return fixed
|
return fixed
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -1,9 +1,25 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
-- A `flex` layout may be initialized with any number of child widgets, and
|
||||||
|
-- during runtime widgets may be added and removed dynamically.
|
||||||
|
--
|
||||||
|
-- On the main axis, the layout will divide the available space evenly between
|
||||||
|
-- all child widgets, without any regard to how much space these widgets might
|
||||||
|
-- be asking for.
|
||||||
|
--
|
||||||
|
-- Just like @{wibox.layout.fixed}, `flex` allows adding spacing between the
|
||||||
|
-- widgets, either as an ofset via @{spacing} or with a
|
||||||
|
-- @{spacing_widget}.
|
||||||
|
--
|
||||||
|
-- On its secondary axis, the layout's size is determined by the largest child
|
||||||
|
-- widget. Smaller child widgets are then placed with the same size.
|
||||||
|
-- Therefore, child widgets may ignore their `forced_width` or `forced_height`
|
||||||
|
-- properties for vertical and horizontal layouts respectively.
|
||||||
--
|
--
|
||||||
--@DOC_wibox_layout_defaults_flex_EXAMPLE@
|
--@DOC_wibox_layout_defaults_flex_EXAMPLE@
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2010 Uli Schlachter
|
-- @copyright 2010 Uli Schlachter
|
||||||
-- @layoutmod wibox.layout.flex
|
-- @layoutmod wibox.layout.flex
|
||||||
|
-- @supermodule wibox.layout.fixed
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local base = require("wibox.widget.base")
|
local base = require("wibox.widget.base")
|
||||||
|
@ -15,6 +31,16 @@ local gtable = require("gears.table")
|
||||||
|
|
||||||
local flex = {}
|
local flex = {}
|
||||||
|
|
||||||
|
-- {{{ Override inherited properties we want to hide
|
||||||
|
|
||||||
|
--- From `wibox.layout.fixed`.
|
||||||
|
-- @property fill_space
|
||||||
|
-- @tparam boolean fill_space
|
||||||
|
-- @propemits true false
|
||||||
|
-- @hidden
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
--- Add some widgets to the given fixed layout.
|
--- Add some widgets to the given fixed layout.
|
||||||
--
|
--
|
||||||
-- @tparam widget ... Widgets that should be added (must at least be one).
|
-- @tparam widget ... Widgets that should be added (must at least be one).
|
||||||
|
@ -45,14 +71,18 @@ local flex = {}
|
||||||
-- @treturn boolean If the operation is successful
|
-- @treturn boolean If the operation is successful
|
||||||
-- @method insert
|
-- @method insert
|
||||||
-- @emits widget::inserted
|
-- @emits widget::inserted
|
||||||
-- @emitstparam widget::inserted widget self The fixed layout.
|
-- @emitstparam widget::inserted widget self The layout.
|
||||||
-- @emitstparam widget::inserted widget widget index The inserted widget.
|
-- @emitstparam widget::inserted widget widget The inserted widget.
|
||||||
-- @emitstparam widget::inserted number count The widget count.
|
-- @emitstparam widget::inserted number count The widget count.
|
||||||
-- @interface layout
|
-- @interface layout
|
||||||
|
|
||||||
--- The widget used to fill the spacing between the layout elements.
|
--- A widget to insert as a separator between child widgets.
|
||||||
--
|
--
|
||||||
-- By default, no widget is used.
|
-- If this property is a valid widget and @{spacing} is greater than `0`, a
|
||||||
|
-- copy of this widget is inserted between each child widget, with its size in
|
||||||
|
-- the layout's main direction determined by @{spacing}.
|
||||||
|
--
|
||||||
|
-- By default no widget is used and any @{spacing} is applied as an empty offset.
|
||||||
--
|
--
|
||||||
--@DOC_wibox_layout_flex_spacing_widget_EXAMPLE@
|
--@DOC_wibox_layout_flex_spacing_widget_EXAMPLE@
|
||||||
--
|
--
|
||||||
|
@ -61,13 +91,16 @@ local flex = {}
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
-- @interface layout
|
-- @interface layout
|
||||||
|
|
||||||
--- Add spacing between each layout widgets.
|
--- The amount of space inserted between the child widgets.
|
||||||
|
--
|
||||||
|
-- If a @{spacing_widget} is defined, this value is used for its size.
|
||||||
--
|
--
|
||||||
--@DOC_wibox_layout_flex_spacing_EXAMPLE@
|
--@DOC_wibox_layout_flex_spacing_EXAMPLE@
|
||||||
--
|
--
|
||||||
-- @property spacing
|
-- @property spacing
|
||||||
-- @tparam number spacing Spacing between widgets.
|
-- @tparam number spacing Spacing between widgets.
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
-- @interface layout
|
||||||
|
|
||||||
function flex:layout(_, width, height)
|
function flex:layout(_, width, height)
|
||||||
local result = {}
|
local result = {}
|
||||||
|
@ -184,10 +217,7 @@ local function get_layout(dir, widget1, ...)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a new horizontal flex layout.
|
--- Creates and returns a new horizontal flex layout.
|
||||||
--
|
|
||||||
-- A flex layout shares the available space.
|
|
||||||
-- equally among all widgets. Widgets can be added via `:add(widget)`.
|
|
||||||
--
|
--
|
||||||
-- @tparam widget ... Widgets that should be added to the layout.
|
-- @tparam widget ... Widgets that should be added to the layout.
|
||||||
-- @constructorfct wibox.layout.flex.horizontal
|
-- @constructorfct wibox.layout.flex.horizontal
|
||||||
|
@ -195,10 +225,7 @@ function flex.horizontal(...)
|
||||||
return get_layout("horizontal", ...)
|
return get_layout("horizontal", ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a new vertical flex layout.
|
--- Creates and returns a new vertical flex layout.
|
||||||
--
|
|
||||||
-- A flex layout shares the available space
|
|
||||||
-- equally among all widgets. Widgets can be added via `:add(widget)`.
|
|
||||||
--
|
--
|
||||||
-- @tparam widget ... Widgets that should be added to the layout.
|
-- @tparam widget ... Widgets that should be added to the layout.
|
||||||
-- @constructorfct wibox.layout.flex.vertical
|
-- @constructorfct wibox.layout.flex.vertical
|
||||||
|
@ -208,10 +235,6 @@ end
|
||||||
|
|
||||||
--@DOC_fixed_COMMON@
|
--@DOC_fixed_COMMON@
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return flex
|
return flex
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
-- @author getzze
|
-- @author getzze
|
||||||
-- @copyright 2017 getzze
|
-- @copyright 2017 getzze
|
||||||
-- @layoutmod wibox.layout.grid
|
-- @layoutmod wibox.layout.grid
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -961,10 +962,6 @@ end
|
||||||
|
|
||||||
--@DOC_fixed_COMMON@
|
--@DOC_fixed_COMMON@
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(grid, grid.mt)
|
return setmetatable(grid, grid.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
-- @author Emmanuel Lepage Vallee
|
-- @author Emmanuel Lepage Vallee
|
||||||
-- @copyright 2016 Emmanuel Lepage Vallee
|
-- @copyright 2016 Emmanuel Lepage Vallee
|
||||||
-- @layoutmod wibox.layout.manual
|
-- @layoutmod wibox.layout.manual
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
local gtable = require("gears.table")
|
local gtable = require("gears.table")
|
||||||
local base = require("wibox.widget.base")
|
local base = require("wibox.widget.base")
|
||||||
|
@ -245,8 +246,4 @@ end
|
||||||
|
|
||||||
--@DOC_fixed_COMMON@
|
--@DOC_fixed_COMMON@
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(manual_layout, {__call=function(_,...) return new_manual(...) end})
|
return setmetatable(manual_layout, {__call=function(_,...) return new_manual(...) end})
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
-- @author Emmanuel Lepage Vallee
|
-- @author Emmanuel Lepage Vallee
|
||||||
-- @copyright 2016 Emmanuel Lepage Vallee
|
-- @copyright 2016 Emmanuel Lepage Vallee
|
||||||
-- @layoutmod wibox.layout.ratio
|
-- @layoutmod wibox.layout.ratio
|
||||||
|
-- @supermodule wibox.layout.flex
|
||||||
|
-- @see 03-declarative-layout.md
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local base = require("wibox.widget.base" )
|
local base = require("wibox.widget.base" )
|
||||||
|
@ -524,10 +526,6 @@ end
|
||||||
|
|
||||||
--@DOC_fixed_COMMON@
|
--@DOC_fixed_COMMON@
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return ratio
|
return ratio
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
-- @author Emmanuel Lepage Vallee
|
-- @author Emmanuel Lepage Vallee
|
||||||
-- @copyright 2016 Emmanuel Lepage Vallee
|
-- @copyright 2016 Emmanuel Lepage Vallee
|
||||||
-- @layoutmod wibox.layout.stack
|
-- @layoutmod wibox.layout.stack
|
||||||
|
-- @supermodule wibox.layout.fixed
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local base = require("wibox.widget.base" )
|
local base = require("wibox.widget.base" )
|
||||||
|
@ -210,9 +211,5 @@ end
|
||||||
|
|
||||||
--@DOC_fixed_COMMON@
|
--@DOC_fixed_COMMON@
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(stack, stack.mt)
|
return setmetatable(stack, stack.mt)
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2010 Uli Schlachter
|
-- @copyright 2010 Uli Schlachter
|
||||||
-- @classmod wibox.widget.base
|
-- @classmod wibox.widget.base
|
||||||
|
-- @supermodule gears.object
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local object = require("gears.object")
|
local object = require("gears.object")
|
||||||
|
@ -17,6 +18,184 @@ local table = table
|
||||||
|
|
||||||
local base = {}
|
local base = {}
|
||||||
|
|
||||||
|
-- {{{ Properties available on all widgets
|
||||||
|
|
||||||
|
--- Get or set the children elements.
|
||||||
|
-- @property children
|
||||||
|
-- @tparam table children The children.
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- Get all direct and indirect children widgets.
|
||||||
|
-- This will scan all containers recursively to find widgets
|
||||||
|
-- Warning: This method it prone to stack overflow if there is a loop in the
|
||||||
|
-- widgets hierarchy. A hierarchy loop is when a widget, or any of its
|
||||||
|
-- children, contain (directly or indirectly) itself.
|
||||||
|
-- @property all_children
|
||||||
|
-- @tparam table children The children.
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- Force a widget height.
|
||||||
|
-- @property forced_height
|
||||||
|
-- @tparam number|nil height The height (`nil` for automatic)
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- Force a widget width.
|
||||||
|
-- @property forced_width
|
||||||
|
-- @tparam number|nil width The width (`nil` for automatic)
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- The widget opacity (transparency).
|
||||||
|
-- @property opacity
|
||||||
|
-- @tparam[opt=1] number opacity The opacity (between 0 and 1)
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- The widget visibility.
|
||||||
|
-- @property visible
|
||||||
|
-- @param boolean
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- The widget buttons.
|
||||||
|
--
|
||||||
|
-- The table contains a list of `awful.button` objects.
|
||||||
|
-- @property buttons
|
||||||
|
-- @param table
|
||||||
|
-- @see awful.button
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- {{{ Signals available on the widgets.
|
||||||
|
|
||||||
|
--- When the layout (size) change.
|
||||||
|
-- This signal is emitted when the previous results of `:layout()` and `:fit()`
|
||||||
|
-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()`
|
||||||
|
-- must return the same result when called with the same arguments.
|
||||||
|
-- @signal widget::layout_changed
|
||||||
|
-- @see widget::redraw_needed
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- When the widget content changed.
|
||||||
|
-- This signal is emitted when the content of the widget changes. The widget will
|
||||||
|
-- be redrawn, it is not re-layouted. Put differently, it is assumed that
|
||||||
|
-- `:layout()` and `:fit()` would still return the same results as before.
|
||||||
|
-- @signal widget::redraw_needed
|
||||||
|
-- @see widget::layout_changed
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- When a mouse button is pressed over the widget.
|
||||||
|
-- @signal button::press
|
||||||
|
-- @tparam table self The current object instance itself.
|
||||||
|
-- @tparam number lx The horizontal position relative to the (0,0) position in
|
||||||
|
-- the widget.
|
||||||
|
-- @tparam number ly The vertical position relative to the (0,0) position in the
|
||||||
|
-- widget.
|
||||||
|
-- @tparam number button The button number.
|
||||||
|
-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift)
|
||||||
|
-- @tparam table find_widgets_result The entry from the result of
|
||||||
|
-- @{wibox.drawable:find_widgets} for the position that the mouse hit.
|
||||||
|
-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing
|
||||||
|
-- the widget.
|
||||||
|
-- @tparam widget find_widgets_result.widget The widget being displayed.
|
||||||
|
-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy
|
||||||
|
-- managing the widget's geometry.
|
||||||
|
-- @tparam number find_widgets_result.x An approximation of the X position that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.y An approximation of the Y position that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.width An approximation of the width that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.height An approximation of the height that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.widget_width The exact width of the widget
|
||||||
|
-- in its local coordinate system.
|
||||||
|
-- @tparam number find_widgets_result.widget_height The exact height of the widget
|
||||||
|
-- in its local coordinate system.
|
||||||
|
-- @see mouse
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- When a mouse button is released over the widget.
|
||||||
|
-- @signal button::release
|
||||||
|
-- @tparam table self The current object instance itself.
|
||||||
|
-- @tparam number lx The horizontal position relative to the (0,0) position in
|
||||||
|
-- the widget.
|
||||||
|
-- @tparam number ly The vertical position relative to the (0,0) position in the
|
||||||
|
-- widget.
|
||||||
|
-- @tparam number button The button number.
|
||||||
|
-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift)
|
||||||
|
-- @tparam table find_widgets_result The entry from the result of
|
||||||
|
-- @{wibox.drawable:find_widgets} for the position that the mouse hit.
|
||||||
|
-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing
|
||||||
|
-- the widget.
|
||||||
|
-- @tparam widget find_widgets_result.widget The widget being displayed.
|
||||||
|
-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy
|
||||||
|
-- managing the widget's geometry.
|
||||||
|
-- @tparam number find_widgets_result.x An approximation of the X position that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.y An approximation of the Y position that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.width An approximation of the width that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.height An approximation of the height that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.widget_width The exact width of the widget
|
||||||
|
-- in its local coordinate system.
|
||||||
|
-- @tparam number find_widgets_result.widget_height The exact height of the widget
|
||||||
|
-- in its local coordinate system.
|
||||||
|
-- @see mouse
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- When the mouse enter a widget.
|
||||||
|
-- @signal mouse::enter
|
||||||
|
-- @tparam table self The current object instance itself.
|
||||||
|
-- @tparam table find_widgets_result The entry from the result of
|
||||||
|
-- @{wibox.drawable:find_widgets} for the position that the mouse hit.
|
||||||
|
-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing
|
||||||
|
-- the widget.
|
||||||
|
-- @tparam widget find_widgets_result.widget The widget being displayed.
|
||||||
|
-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy
|
||||||
|
-- managing the widget's geometry.
|
||||||
|
-- @tparam number find_widgets_result.x An approximation of the X position that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.y An approximation of the Y position that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.width An approximation of the width that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.height An approximation of the height that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.widget_width The exact width of the widget
|
||||||
|
-- in its local coordinate system.
|
||||||
|
-- @tparam number find_widgets_result.widget_height The exact height of the widget
|
||||||
|
-- in its local coordinate system.
|
||||||
|
-- @see mouse
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
--- When the mouse leave a widget.
|
||||||
|
-- @signal mouse::leave
|
||||||
|
-- @tparam table self The current object instance itself.
|
||||||
|
-- @tparam table find_widgets_result The entry from the result of
|
||||||
|
-- @{wibox.drawable:find_widgets} for the position that the mouse hit.
|
||||||
|
-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing
|
||||||
|
-- the widget.
|
||||||
|
-- @tparam widget find_widgets_result.widget The widget being displayed.
|
||||||
|
-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy
|
||||||
|
-- managing the widget's geometry.
|
||||||
|
-- @tparam number find_widgets_result.x An approximation of the X position that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.y An approximation of the Y position that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.width An approximation of the width that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.height An approximation of the height that
|
||||||
|
-- the widget is visible at on the surface.
|
||||||
|
-- @tparam number find_widgets_result.widget_width The exact width of the widget
|
||||||
|
-- in its local coordinate system.
|
||||||
|
-- @tparam number find_widgets_result.widget_height The exact height of the widget
|
||||||
|
-- in its local coordinate system.
|
||||||
|
-- @see mouse
|
||||||
|
-- @baseclass wibox.widget.base
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
-- {{{ Functions on widgets
|
-- {{{ Functions on widgets
|
||||||
|
|
||||||
-- Functions available on all widgets.
|
-- Functions available on all widgets.
|
||||||
|
@ -30,7 +209,8 @@ end, true)
|
||||||
|
|
||||||
--- Set a widget's visibility.
|
--- Set a widget's visibility.
|
||||||
-- @tparam boolean b Whether the widget is visible.
|
-- @tparam boolean b Whether the widget is visible.
|
||||||
-- @method set_visible
|
-- @method wibox.widget.base:set_visible
|
||||||
|
-- @hidden
|
||||||
function base.widget:set_visible(b)
|
function base.widget:set_visible(b)
|
||||||
if b ~= self._private.visible then
|
if b ~= self._private.visible then
|
||||||
self._private.visible = b
|
self._private.visible = b
|
||||||
|
@ -42,6 +222,7 @@ end
|
||||||
|
|
||||||
--- Add a new `awful.button` to this widget.
|
--- Add a new `awful.button` to this widget.
|
||||||
-- @tparam awful.button button The button to add.
|
-- @tparam awful.button button The button to add.
|
||||||
|
-- @method wibox.widget.base:add_button
|
||||||
function base.widget:add_button(button)
|
function base.widget:add_button(button)
|
||||||
if not button then return end
|
if not button then return end
|
||||||
|
|
||||||
|
@ -65,7 +246,8 @@ end
|
||||||
|
|
||||||
--- Is the widget visible?
|
--- Is the widget visible?
|
||||||
-- @treturn boolean
|
-- @treturn boolean
|
||||||
-- @method get_visible
|
-- @method wibox.widget.base:get_visible
|
||||||
|
-- @hidden
|
||||||
function base.widget:get_visible()
|
function base.widget:get_visible()
|
||||||
return self._private.visible or false
|
return self._private.visible or false
|
||||||
end
|
end
|
||||||
|
@ -73,17 +255,19 @@ end
|
||||||
--- Set a widget's opacity.
|
--- Set a widget's opacity.
|
||||||
-- @tparam number o The opacity to use (a number from 0 (transparent) to 1
|
-- @tparam number o The opacity to use (a number from 0 (transparent) to 1
|
||||||
-- (opaque)).
|
-- (opaque)).
|
||||||
-- @method set_opacity
|
-- @method wibox.widget.base:set_opacity
|
||||||
|
-- @hidden
|
||||||
function base.widget:set_opacity(o)
|
function base.widget:set_opacity(o)
|
||||||
if o ~= self._private.opacity then
|
if o ~= self._private.opacity then
|
||||||
self._private.opacity = o
|
self._private.opacity = o
|
||||||
self:emit_signal("widget::redraw")
|
self:emit_signal("widget::redraw_needed")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the widget's opacity.
|
--- Get the widget's opacity.
|
||||||
-- @treturn number The opacity (between 0 (transparent) and 1 (opaque)).
|
-- @treturn number The opacity (between 0 (transparent) and 1 (opaque)).
|
||||||
-- @method get_opacity
|
-- @method wibox.widget.base:get_opacity
|
||||||
|
-- @hidden
|
||||||
function base.widget:get_opacity()
|
function base.widget:get_opacity()
|
||||||
return self._private.opacity
|
return self._private.opacity
|
||||||
end
|
end
|
||||||
|
@ -91,8 +275,9 @@ end
|
||||||
--- Set the widget's forced width.
|
--- Set the widget's forced width.
|
||||||
-- @tparam[opt] number width With `nil` the default mechanism of calling the
|
-- @tparam[opt] number width With `nil` the default mechanism of calling the
|
||||||
-- `:fit` method is used.
|
-- `:fit` method is used.
|
||||||
-- @see fit_widget
|
-- @see wibox.widget.base:fit_widget
|
||||||
-- @method set_forced_width
|
-- @method wibox.widget.base:set_forced_width
|
||||||
|
-- @hidden
|
||||||
function base.widget:set_forced_width(width)
|
function base.widget:set_forced_width(width)
|
||||||
if width ~= self._private.forced_width then
|
if width ~= self._private.forced_width then
|
||||||
self._private.forced_width = width
|
self._private.forced_width = width
|
||||||
|
@ -108,7 +293,8 @@ end
|
||||||
-- actual size is during a `mouse::enter`, `mouse::leave` or button event.
|
-- actual size is during a `mouse::enter`, `mouse::leave` or button event.
|
||||||
-- @treturn[opt] number The forced width (nil if automatic).
|
-- @treturn[opt] number The forced width (nil if automatic).
|
||||||
-- @see fit_widget
|
-- @see fit_widget
|
||||||
-- @method get_forced_width
|
-- @method wibox.widget.base:get_forced_width
|
||||||
|
-- @hidden
|
||||||
function base.widget:get_forced_width()
|
function base.widget:get_forced_width()
|
||||||
return self._private.forced_width
|
return self._private.forced_width
|
||||||
end
|
end
|
||||||
|
@ -116,8 +302,9 @@ end
|
||||||
--- Set the widget's forced height.
|
--- Set the widget's forced height.
|
||||||
-- @tparam[opt] number height With `nil` the default mechanism of calling the
|
-- @tparam[opt] number height With `nil` the default mechanism of calling the
|
||||||
-- `:fit` method is used.
|
-- `:fit` method is used.
|
||||||
-- @see fit_widget
|
-- @see wibox.widget.base:fit_widget
|
||||||
-- @method set_height
|
-- @method wibox.widget.base:set_height
|
||||||
|
-- @hidden
|
||||||
function base.widget:set_forced_height(height)
|
function base.widget:set_forced_height(height)
|
||||||
if height ~= self._private.forced_height then
|
if height ~= self._private.forced_height then
|
||||||
self._private.forced_height = height
|
self._private.forced_height = height
|
||||||
|
@ -132,7 +319,8 @@ end
|
||||||
-- If there is no forced width/height, then the only way to get the widget's
|
-- If there is no forced width/height, then the only way to get the widget's
|
||||||
-- actual size is during a `mouse::enter`, `mouse::leave` or button event.
|
-- actual size is during a `mouse::enter`, `mouse::leave` or button event.
|
||||||
-- @treturn[opt] number The forced height (nil if automatic).
|
-- @treturn[opt] number The forced height (nil if automatic).
|
||||||
-- @method get_forced_height
|
-- @method wibox.widget.base:get_forced_height
|
||||||
|
-- @hidden
|
||||||
function base.widget:get_forced_height()
|
function base.widget:get_forced_height()
|
||||||
return self._private.forced_height
|
return self._private.forced_height
|
||||||
end
|
end
|
||||||
|
@ -141,7 +329,8 @@ end
|
||||||
--
|
--
|
||||||
-- This method should be re-implemented by the relevant widgets.
|
-- This method should be re-implemented by the relevant widgets.
|
||||||
-- @treturn table children The children.
|
-- @treturn table children The children.
|
||||||
-- @method get_children
|
-- @method wibox.widget.base:get_children
|
||||||
|
-- @hidden
|
||||||
function base.widget:get_children()
|
function base.widget:get_children()
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
@ -151,7 +340,8 @@ end
|
||||||
-- The default implementation does nothing, this must be re-implemented by
|
-- The default implementation does nothing, this must be re-implemented by
|
||||||
-- all layout and container widgets.
|
-- all layout and container widgets.
|
||||||
-- @tparam table children A table composed of valid widgets.
|
-- @tparam table children A table composed of valid widgets.
|
||||||
-- @method set_children
|
-- @method wibox.widget.base:set_children
|
||||||
|
-- @hidden
|
||||||
function base.widget:set_children(children) -- luacheck: no unused
|
function base.widget:set_children(children) -- luacheck: no unused
|
||||||
-- Nothing on purpose
|
-- Nothing on purpose
|
||||||
end
|
end
|
||||||
|
@ -171,7 +361,8 @@ end
|
||||||
-- *Warning*: This method it prone to stack overflow if the widget, or any of
|
-- *Warning*: This method it prone to stack overflow if the widget, or any of
|
||||||
-- its children, contains (directly or indirectly) itself.
|
-- its children, contains (directly or indirectly) itself.
|
||||||
-- @treturn table children The children.
|
-- @treturn table children The children.
|
||||||
-- @method get_all_children
|
-- @method wibox.widget.base:get_all_children
|
||||||
|
-- @hidden
|
||||||
function base.widget:get_all_children()
|
function base.widget:get_all_children()
|
||||||
local ret = {}
|
local ret = {}
|
||||||
digg_children(ret, self)
|
digg_children(ret, self)
|
||||||
|
@ -193,12 +384,17 @@ function base.set_widget_common(self, widget)
|
||||||
end
|
end
|
||||||
|
|
||||||
self._private.widget = w
|
self._private.widget = w
|
||||||
|
|
||||||
|
self:emit_signal("property::widget")
|
||||||
|
|
||||||
self:emit_signal("widget::layout_changed")
|
self:emit_signal("widget::layout_changed")
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Emit a signal and ensure all parent widgets in the hierarchies also
|
--- Emit a signal and ensure all parent widgets in the hierarchies also
|
||||||
-- forward the signal. This is useful to track signals when there is a dynamic
|
-- forward the signal.
|
||||||
-- set of containers and layouts wrapping the widget.
|
--
|
||||||
|
-- This is useful to track signals when there is a dynamic set of containers
|
||||||
|
-- and layouts wrapping the widget.
|
||||||
--
|
--
|
||||||
-- Note that this function has some flaws:
|
-- Note that this function has some flaws:
|
||||||
--
|
--
|
||||||
|
@ -213,7 +409,7 @@ end
|
||||||
--
|
--
|
||||||
-- @tparam string signal_name
|
-- @tparam string signal_name
|
||||||
-- @param ... Other arguments
|
-- @param ... Other arguments
|
||||||
-- @method emit_signal_recursive
|
-- @method wibox.widget.base:emit_signal_recursive
|
||||||
function base.widget:emit_signal_recursive(signal_name, ...)
|
function base.widget:emit_signal_recursive(signal_name, ...)
|
||||||
-- This is a convenience wrapper, the real implementation is in the
|
-- This is a convenience wrapper, the real implementation is in the
|
||||||
-- hierarchy.
|
-- hierarchy.
|
||||||
|
@ -223,12 +419,14 @@ end
|
||||||
|
|
||||||
--- Get the index of a widget.
|
--- Get the index of a widget.
|
||||||
-- @tparam widget widget The widget to look for.
|
-- @tparam widget widget The widget to look for.
|
||||||
-- @tparam[opt] boolean recursive Also check sub-widgets?
|
-- @tparam[opt] boolean recursive Recursively check accross the sub-widgets
|
||||||
-- @tparam[opt] widget ... Additional widgets to add at the end of the "path"
|
-- hierarchy.
|
||||||
-- @treturn number The index.
|
-- @tparam[opt] widget ... Additional widgets to add at the end of the
|
||||||
|
-- sub-widgets hierarchy "path".
|
||||||
|
-- @treturn number The widget index.
|
||||||
-- @treturn widget The parent widget.
|
-- @treturn widget The parent widget.
|
||||||
-- @treturn table The path between "self" and "widget".
|
-- @treturn table The hierarchy path between "self" and "widget".
|
||||||
-- @method index
|
-- @method wibox.widget.base:index
|
||||||
function base.widget:index(widget, recursive, ...)
|
function base.widget:index(widget, recursive, ...)
|
||||||
local widgets = self:get_children()
|
local widgets = self:get_children()
|
||||||
for idx, w in ipairs(widgets) do
|
for idx, w in ipairs(widgets) do
|
||||||
|
@ -471,8 +669,7 @@ end
|
||||||
-- Read the table, separate attributes from widgets.
|
-- Read the table, separate attributes from widgets.
|
||||||
local function parse_table(t, leave_empty)
|
local function parse_table(t, leave_empty)
|
||||||
local max = 0
|
local max = 0
|
||||||
local attributes, widgets, with_handlers = {}, {}, {}
|
local attributes, widgets = {}, {}
|
||||||
|
|
||||||
for k,v in pairs(t) do
|
for k,v in pairs(t) do
|
||||||
if type(k) == "number" then
|
if type(k) == "number" then
|
||||||
if v then
|
if v then
|
||||||
|
@ -482,35 +679,20 @@ local function parse_table(t, leave_empty)
|
||||||
max = k
|
max = k
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Those are not added "normally". They have their own callback
|
widgets[k] = v
|
||||||
-- to add them.
|
|
||||||
if rawget(v, "_set_declarative_handler") then
|
|
||||||
with_handlers[k] = v
|
|
||||||
else
|
|
||||||
widgets[k] = v
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
elseif type(v) == "table" and rawget(v, "_set_declarative_handler") then
|
|
||||||
with_handlers[k] = v
|
|
||||||
else
|
else
|
||||||
attributes[k] = v
|
attributes[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make sure there is no holes in the widgets table.
|
|
||||||
if #with_handlers > 0 then
|
|
||||||
for k in pairs(with_handlers) do
|
|
||||||
table.remove(widgets, k)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Pack the sparse table, if the container doesn't support sparse tables.
|
-- Pack the sparse table, if the container doesn't support sparse tables.
|
||||||
if not leave_empty then
|
if not leave_empty then
|
||||||
widgets = gtable.from_sparse(widgets)
|
widgets = gtable.from_sparse(widgets)
|
||||||
max = #widgets
|
max = #widgets
|
||||||
end
|
end
|
||||||
|
|
||||||
return max, attributes, widgets, with_handlers
|
return max, attributes, widgets
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Recursively build a container from a declarative table.
|
-- Recursively build a container from a declarative table.
|
||||||
|
@ -526,19 +708,8 @@ local function drill(ids, content)
|
||||||
-- Create layouts based on metatable's __call.
|
-- Create layouts based on metatable's __call.
|
||||||
local l = layout.is_widget and layout or layout()
|
local l = layout.is_widget and layout or layout()
|
||||||
|
|
||||||
-- Check if the widget added it's own element by ids
|
|
||||||
--FIXME #2181
|
|
||||||
if l and l._private and l._private.by_id then
|
|
||||||
for k, v in pairs(l._private.by_id) do
|
|
||||||
ids[k] = ids[k] or {}
|
|
||||||
for _, v2 in ipairs(v) do
|
|
||||||
table.insert(ids[k], v2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the number of children widgets (including nil widgets).
|
-- Get the number of children widgets (including nil widgets).
|
||||||
local max, attributes, widgets, with_handlers = parse_table(content, l.allow_empty_widget)
|
local max, attributes, widgets = parse_table(content, l.allow_empty_widget)
|
||||||
|
|
||||||
-- Get the optional identifier to create a virtual widget tree to place
|
-- Get the optional identifier to create a virtual widget tree to place
|
||||||
-- in an "access table" to be able to retrieve the widget.
|
-- in an "access table" to be able to retrieve the widget.
|
||||||
|
@ -560,48 +731,34 @@ local function drill(ids, content)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Let some containers handle the template themselves.
|
if widgets and max > 0 then
|
||||||
-- This can be used for use cases such as lazy loading, repeaters or
|
-- Add all widgets.
|
||||||
-- conditional loaders.
|
for k = 1, max do
|
||||||
if l._accept_templates and l.set_templates then
|
-- ipairs cannot be used on sparse tables.
|
||||||
l:set_templates(widgets)
|
local v, id2, e = widgets[k], id, nil
|
||||||
return l, id
|
if v then
|
||||||
end
|
-- It is another declarative container, parse it.
|
||||||
|
if (not v.is_widget) and (v.widget or v.layout) then
|
||||||
|
e, id2 = drill(ids, v)
|
||||||
|
widgets[k] = e
|
||||||
|
elseif (not v.is_widget) and is_callable(v) then
|
||||||
|
widgets[k] = v()
|
||||||
|
end
|
||||||
|
base.check_widget(widgets[k])
|
||||||
|
|
||||||
-- Add all widgets.
|
-- Place the widget in the access table.
|
||||||
for k = 1, max do
|
if id2 then
|
||||||
-- ipairs cannot be used on sparse tables.
|
l [id2] = e
|
||||||
local v, id2, e = widgets[k], id, nil
|
ids[id2] = ids[id2] or {}
|
||||||
if v then
|
table.insert(ids[id2], e)
|
||||||
-- It is another declarative container, parse it.
|
end
|
||||||
if (not v.is_widget) and (v.widget or v.layout) then
|
|
||||||
e, id2 = drill(ids, v)
|
|
||||||
widgets[k] = e
|
|
||||||
elseif (not v.is_widget) and is_callable(v) then
|
|
||||||
widgets[k] = v()
|
|
||||||
end
|
|
||||||
base.check_widget(widgets[k])
|
|
||||||
|
|
||||||
-- Place the widget in the access table.
|
|
||||||
if id2 then
|
|
||||||
l [id2] = e
|
|
||||||
ids[id2] = ids[id2] or {}
|
|
||||||
table.insert(ids[id2], e)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
-- Replace all children (if any) with the new ones.
|
-- Replace all children (if any) with the new ones.
|
||||||
if widgets then
|
|
||||||
l:set_children(widgets)
|
l:set_children(widgets)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Now that all the children are set, call the custom handlers.
|
|
||||||
-- The order is undefined.
|
|
||||||
for k, v in pairs(with_handlers) do
|
|
||||||
v:_set_declarative_handler(l, k, ids)
|
|
||||||
end
|
|
||||||
|
|
||||||
return l, id
|
return l, id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -618,7 +775,8 @@ end
|
||||||
--
|
--
|
||||||
-- See [The declarative layout system](../documentation/03-declarative-layout.md.html).
|
-- See [The declarative layout system](../documentation/03-declarative-layout.md.html).
|
||||||
-- @tparam table args A table containing the widget's disposition.
|
-- @tparam table args A table containing the widget's disposition.
|
||||||
-- @method setup
|
-- @method wibox.widget.base:setup
|
||||||
|
-- @hidden
|
||||||
function base.widget:setup(args)
|
function base.widget:setup(args)
|
||||||
local f,ids = self.set_widget or self.add or self.set_first,{}
|
local f,ids = self.set_widget or self.add or self.set_first,{}
|
||||||
local w, id = drill(ids, args)
|
local w, id = drill(ids, args)
|
||||||
|
|
|
@ -37,8 +37,9 @@ local beautiful = require("beautiful")
|
||||||
|
|
||||||
local calendar = { mt = {} }
|
local calendar = { mt = {} }
|
||||||
|
|
||||||
local properties = { "date", "font", "spacing", "week_numbers", "start_sunday", "long_weekdays", "fn_embed" }
|
local properties = { "date" , "font" , "spacing" , "week_numbers",
|
||||||
|
"start_sunday", "long_weekdays", "fn_embed", "flex_height",
|
||||||
|
}
|
||||||
|
|
||||||
--- The calendar font.
|
--- The calendar font.
|
||||||
-- @beautiful beautiful.calendar_font
|
-- @beautiful beautiful.calendar_font
|
||||||
|
@ -60,6 +61,11 @@ local properties = { "date", "font", "spacing", "week_numbers", "start_sunday",
|
||||||
-- @beautiful beautiful.calendar_long_weekdays
|
-- @beautiful beautiful.calendar_long_weekdays
|
||||||
-- @param boolean Use three characters for the weekdays instead of two
|
-- @param boolean Use three characters for the weekdays instead of two
|
||||||
|
|
||||||
|
--- Allow cells to have flexible height.
|
||||||
|
-- Flexible height allow cells to adapt their height to fill the empty space at the bottom of the widget.
|
||||||
|
-- @beautiful beautiful.flex_height
|
||||||
|
-- @param boolean Cells can skretch to fill the empty space.
|
||||||
|
|
||||||
--- The calendar date.
|
--- The calendar date.
|
||||||
--
|
--
|
||||||
-- A table representing the date {day=[number|nil], month=[number|nil], year=[number]}.
|
-- A table representing the date {day=[number|nil], month=[number|nil], year=[number]}.
|
||||||
|
@ -116,6 +122,12 @@ local properties = { "date", "font", "spacing", "week_numbers", "start_sunday",
|
||||||
-- @param function Function to embed the widget depending on its flag
|
-- @param function Function to embed the widget depending on its flag
|
||||||
-- @property fn_embed
|
-- @property fn_embed
|
||||||
|
|
||||||
|
--- Allow cells to have flexible height
|
||||||
|
--
|
||||||
|
--@DOC_wibox_widget_calendar_flex_height_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @param[opt=false] boolean Allow flex height.
|
||||||
|
-- @property flex_height
|
||||||
|
|
||||||
--- Make a textbox
|
--- Make a textbox
|
||||||
-- @tparam string text Text of the textbox
|
-- @tparam string text Text of the textbox
|
||||||
|
@ -139,25 +151,47 @@ end
|
||||||
-- @tparam number|nil date.day Date day
|
-- @tparam number|nil date.day Date day
|
||||||
-- @treturn widget Grid layout
|
-- @treturn widget Grid layout
|
||||||
local function create_month(props, date)
|
local function create_month(props, date)
|
||||||
local num_rows = 8
|
|
||||||
local num_columns = props.week_numbers and 8 or 7
|
|
||||||
|
|
||||||
-- Create grid layout
|
|
||||||
local layout = grid()
|
|
||||||
layout:set_expand(true)
|
|
||||||
layout:set_expand(true)
|
|
||||||
layout:set_homogeneous(true)
|
|
||||||
layout:set_spacing(props.spacing)
|
|
||||||
layout:set_forced_num_rows(num_rows)
|
|
||||||
layout:set_forced_num_cols(num_columns)
|
|
||||||
|
|
||||||
local start_row = 3
|
local start_row = 3
|
||||||
local start_column = num_columns - 6
|
|
||||||
local week_start = props.start_sunday and 1 or 2
|
local week_start = props.start_sunday and 1 or 2
|
||||||
local last_day = os.date("*t", os.time{year=date.year, month=date.month+1, day=0})
|
local last_day = os.date("*t", os.time{year=date.year, month=date.month+1, day=0})
|
||||||
local month_days = last_day.day
|
local month_days = last_day.day
|
||||||
local column_fday = (last_day.wday - month_days + 1 - week_start ) % 7
|
local column_fday = (last_day.wday - month_days + 1 - week_start ) % 7
|
||||||
|
|
||||||
|
local num_columns = props.week_numbers and 8 or 7
|
||||||
|
local start_column = num_columns - 6
|
||||||
|
|
||||||
|
-- Compute number of rows
|
||||||
|
-- There are at least 4 weeks in a month
|
||||||
|
local num_rows = 4
|
||||||
|
-- On every month but february on non bisextile years
|
||||||
|
if last_day.day > 28 then
|
||||||
|
-- The number of days span over at least 5 weeks
|
||||||
|
num_rows = num_rows + 1
|
||||||
|
|
||||||
|
-- On month with 30+ days add 1 week if:
|
||||||
|
-- - if 30 days and the first day is the last day of the week
|
||||||
|
-- - if 31 days and the first days is at least the second to last day
|
||||||
|
if column_fday >= 5 then
|
||||||
|
if last_day.day == 30 and column_fday == 6 or last_day.day == 31 then
|
||||||
|
num_rows = num_rows + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- If the first day of february is anything but the first day of the week
|
||||||
|
elseif column_fday > 1 then
|
||||||
|
-- Span over 5 weeks
|
||||||
|
num_rows = num_rows + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create grid layout
|
||||||
|
local layout = grid()
|
||||||
|
if props.flex_height then
|
||||||
|
layout:set_expand(true)
|
||||||
|
end
|
||||||
|
layout:set_homogeneous(true)
|
||||||
|
layout:set_spacing(props.spacing)
|
||||||
|
layout:set_forced_num_rows(num_rows)
|
||||||
|
layout:set_forced_num_cols(num_columns)
|
||||||
|
|
||||||
--local flags = {"header", "weekdays", "weeknumber", "normal", "focus"}
|
--local flags = {"header", "weekdays", "weeknumber", "normal", "focus"}
|
||||||
local cell_date, t, i, j, w, flag, text
|
local cell_date, t, i, j, w, flag, text
|
||||||
|
|
||||||
|
@ -331,6 +365,7 @@ local function get_calendar(type, date, font)
|
||||||
ret._private.week_numbers = beautiful.calendar_week_numbers or false
|
ret._private.week_numbers = beautiful.calendar_week_numbers or false
|
||||||
ret._private.start_sunday = beautiful.calendar_start_sunday or false
|
ret._private.start_sunday = beautiful.calendar_start_sunday or false
|
||||||
ret._private.long_weekdays = beautiful.calendar_long_weekdays or false
|
ret._private.long_weekdays = beautiful.calendar_long_weekdays or false
|
||||||
|
ret._private.flex_height = beautiful.calendar_flex_height or false
|
||||||
ret._private.fn_embed = function (w, _) return w end
|
ret._private.fn_embed = function (w, _) return w end
|
||||||
|
|
||||||
-- header specific
|
-- header specific
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
-- @author Emmanuel Lepage Valle
|
-- @author Emmanuel Lepage Valle
|
||||||
-- @copyright 2010 Emmanuel Lepage Vallee
|
-- @copyright 2010 Emmanuel Lepage Vallee
|
||||||
-- @widgetmod wibox.widget.checkbox
|
-- @widgetmod wibox.widget.checkbox
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local color = require( "gears.color" )
|
local color = require( "gears.color" )
|
||||||
|
@ -303,10 +304,6 @@ local function new(checked, args)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable({}, { __call = function(_, ...) return new(...) end})
|
return setmetatable({}, { __call = function(_, ...) return new(...) end})
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,26 +1,27 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
-- A widget to display image.
|
-- A widget to display an image.
|
||||||
--
|
--
|
||||||
-- The `wibox.widget.imagebox` is part of the Awesome WM's wiboxes system
|
-- The `wibox.widget.imagebox` is part of the Awesome WM's widget system
|
||||||
-- (see @{03-declarative-layout.md}).
|
-- (see @{03-declarative-layout.md}).
|
||||||
--
|
--
|
||||||
-- This widget displays an image. The image can be a file,
|
-- This widget displays an image. The image can be a file,
|
||||||
-- a cairo image surface, or an rsvg handle object (see the
|
-- a cairo image surface, or an rsvg handle object (see the
|
||||||
-- [image property](#image)).
|
-- [image property](#image)).
|
||||||
--
|
--
|
||||||
-- Use a `wibox.widget.imagebox`
|
-- Examples using a `wibox.widget.imagebox`:
|
||||||
-- ---
|
-- ---
|
||||||
--
|
--
|
||||||
-- @DOC_wibox_widget_defaults_imagebox_EXAMPLE@
|
-- @DOC_wibox_widget_defaults_imagebox_EXAMPLE@
|
||||||
--
|
--
|
||||||
-- Alternatively, you can declare the `imagebox` widget using the
|
-- Alternatively, you can declare the `imagebox` widget using the
|
||||||
-- declarative pattern (Both codes are strictly equivalent):
|
-- declarative pattern (both variants are strictly equivalent):
|
||||||
--
|
--
|
||||||
-- @DOC_wibox_widget_declarative-pattern_imagebox_EXAMPLE@
|
-- @DOC_wibox_widget_declarative-pattern_imagebox_EXAMPLE@
|
||||||
--
|
--
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2010 Uli Schlachter
|
-- @copyright 2010 Uli Schlachter
|
||||||
-- @widgetmod wibox.widget.imagebox
|
-- @widgetmod wibox.widget.imagebox
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local lgi = require("lgi")
|
local lgi = require("lgi")
|
||||||
|
@ -47,67 +48,40 @@ end
|
||||||
|
|
||||||
local imagebox = { mt = {} }
|
local imagebox = { mt = {} }
|
||||||
|
|
||||||
local rsvg_handle_cache = setmetatable({}, { __mode = 'v' })
|
local rsvg_handle_cache = setmetatable({}, { __mode = 'k' })
|
||||||
|
|
||||||
---Load rsvg handle form image file
|
---Load rsvg handle form image file
|
||||||
---@tparam string file Path to svg file.
|
-- @tparam string file Path to svg file.
|
||||||
---@return Rsvg handle
|
-- @return Rsvg handle
|
||||||
|
-- @treturn table A table where cached data can be stored.
|
||||||
local function load_rsvg_handle(file)
|
local function load_rsvg_handle(file)
|
||||||
if not Rsvg then return end
|
if not Rsvg then return end
|
||||||
|
|
||||||
local cache = rsvg_handle_cache[file]
|
local cache = (rsvg_handle_cache[file] or {})["handle"]
|
||||||
|
|
||||||
if cache then
|
if cache then
|
||||||
return cache
|
return cache, rsvg_handle_cache[file]
|
||||||
end
|
end
|
||||||
|
|
||||||
local handle, err = Rsvg.Handle.new_from_file(file)
|
local handle, err
|
||||||
if not err then
|
|
||||||
rsvg_handle_cache[file] = handle
|
|
||||||
return handle
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Draw an imagebox with the given cairo context in the given geometry.
|
if file:match("<[?]?xml") or file:match("<svg") then
|
||||||
function imagebox:draw(_, cr, width, height)
|
handle, err = Rsvg.Handle.new_from_data(file)
|
||||||
if width == 0 or height == 0 or not self._private.default then return end
|
|
||||||
|
|
||||||
-- Set the clip
|
|
||||||
if self._private.clip_shape then
|
|
||||||
cr:clip(self._private.clip_shape(cr, width, height, unpack(self._private.clip_args)))
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self._private.resize_forbidden then
|
|
||||||
-- Let's scale the image so that it fits into (width, height)
|
|
||||||
local w, h = self._private.default.width, self._private.default.height
|
|
||||||
local aspect = math.min(width / w, height / h)
|
|
||||||
cr:scale(aspect, aspect)
|
|
||||||
end
|
|
||||||
|
|
||||||
if self._private.handle then
|
|
||||||
self._private.handle:render_cairo(cr)
|
|
||||||
else
|
else
|
||||||
cr:set_source_surface(self._private.image, 0, 0)
|
handle, err = Rsvg.Handle.new_from_file(file)
|
||||||
cr:paint()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Fit the imagebox into the given geometry
|
|
||||||
function imagebox:fit(_, width, height)
|
|
||||||
if not self._private.default then return 0, 0 end
|
|
||||||
local w, h = self._private.default.width, self._private.default.height
|
|
||||||
|
|
||||||
if not self._private.resize_forbidden or w > width or h > height then
|
|
||||||
local aspect = math.min(width / w, height / h)
|
|
||||||
return w * aspect, h * aspect
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return w, h
|
if not err then
|
||||||
|
rsvg_handle_cache[file] = rsvg_handle_cache[file] or {}
|
||||||
|
rsvg_handle_cache[file]["handle"] = handle
|
||||||
|
return handle, rsvg_handle_cache[file]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Apply cairo surface for given imagebox widget
|
---Apply cairo surface for given imagebox widget
|
||||||
local function set_surface(ib, surf)
|
local function set_surface(ib, surf)
|
||||||
local is_surd_valid = surf.width > 0 and surf.height > 0
|
local is_surf_valid = surf.width > 0 and surf.height > 0
|
||||||
if not is_surd_valid then return end
|
if not is_surf_valid then return false end
|
||||||
|
|
||||||
ib._private.default = { width = surf.width, height = surf.height }
|
ib._private.default = { width = surf.width, height = surf.height }
|
||||||
ib._private.handle = nil
|
ib._private.handle = nil
|
||||||
|
@ -116,14 +90,16 @@ local function set_surface(ib, surf)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Apply RsvgHandle for given imagebox widget
|
---Apply RsvgHandle for given imagebox widget
|
||||||
local function set_handle(ib, handle)
|
local function set_handle(ib, handle, cache)
|
||||||
local dim = handle:get_dimensions()
|
local dim = handle:get_dimensions()
|
||||||
local is_handle_valid = dim.width > 0 and dim.height > 0
|
local is_handle_valid = dim.width > 0 and dim.height > 0
|
||||||
if not is_handle_valid then return end
|
if not is_handle_valid then return false end
|
||||||
|
|
||||||
ib._private.default = { width = dim.width, height = dim.height }
|
ib._private.default = { width = dim.width, height = dim.height }
|
||||||
ib._private.handle = handle
|
ib._private.handle = handle
|
||||||
|
ib._private.cache = cache
|
||||||
ib._private.image = nil
|
ib._private.image = nil
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -135,32 +111,208 @@ end
|
||||||
---@treturn boolean True if image was successfully applied
|
---@treturn boolean True if image was successfully applied
|
||||||
local function load_and_apply(ib, file, image_loader, image_setter)
|
local function load_and_apply(ib, file, image_loader, image_setter)
|
||||||
local image_applied
|
local image_applied
|
||||||
local object = image_loader(file)
|
local object, cache = image_loader(file)
|
||||||
|
|
||||||
if object then
|
if object then
|
||||||
image_applied = image_setter(ib, object)
|
image_applied = image_setter(ib, object, cache)
|
||||||
end
|
end
|
||||||
return image_applied
|
return image_applied
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Update the cached size depending on the stylesheet and dpi.
|
||||||
|
--
|
||||||
|
-- It's necessary because a single RSVG handle can be used by
|
||||||
|
-- many imageboxes. So DPI and Stylesheet need to be set each time.
|
||||||
|
local function update_dpi(self, ctx)
|
||||||
|
if not self._private.handle then return end
|
||||||
|
|
||||||
|
local dpi = self._private.auto_dpi and
|
||||||
|
ctx.dpi or
|
||||||
|
self._private.dpi or
|
||||||
|
nil
|
||||||
|
|
||||||
|
local need_dpi = dpi and
|
||||||
|
self._private.last_dpi ~= dpi
|
||||||
|
|
||||||
|
local need_style = self._private.handle.set_stylesheet and
|
||||||
|
self._private.stylesheet
|
||||||
|
|
||||||
|
local old_size = self._private.default and self._private.default.width
|
||||||
|
|
||||||
|
if dpi and dpi ~= self._private.cache.dpi then
|
||||||
|
if type(dpi) == "table" then
|
||||||
|
self._private.handle:set_dpi_x_y(dpi.x, dpi.y)
|
||||||
|
else
|
||||||
|
self._private.handle:set_dpi(dpi)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if need_style and self._private.cache.stylesheet ~= self._private.stylesheet then
|
||||||
|
self._private.handle:set_stylesheet(self._private.stylesheet)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reload the size.
|
||||||
|
if need_dpi or (need_style and self._private.stylesheet ~= self._private.last_stylesheet) then
|
||||||
|
set_handle(self, self._private.handle, self._private.cache)
|
||||||
|
end
|
||||||
|
|
||||||
|
self._private.last_dpi = dpi
|
||||||
|
self._private.cache.dpi = dpi
|
||||||
|
self._private.last_stylesheet = self._private.stylesheet
|
||||||
|
self._private.cache.stylesheet = self._private.stylesheet
|
||||||
|
|
||||||
|
-- This can happen in the constructor when `dpi` is set after `image`.
|
||||||
|
if old_size and old_size ~= self._private.default.width then
|
||||||
|
self:emit_signal("widget::redraw_needed")
|
||||||
|
self:emit_signal("widget::layout_changed")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw an imagebox with the given cairo context in the given geometry.
|
||||||
|
function imagebox:draw(ctx, cr, width, height)
|
||||||
|
if width == 0 or height == 0 or not self._private.default then return end
|
||||||
|
|
||||||
|
-- For valign = "top" and halign = "left"
|
||||||
|
local translate = {
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
update_dpi(self, ctx)
|
||||||
|
|
||||||
|
local w, h = self._private.default.width, self._private.default.height
|
||||||
|
|
||||||
|
if self._private.resize then
|
||||||
|
-- That's for the "fit" policy.
|
||||||
|
local aspects = {
|
||||||
|
w = width / w,
|
||||||
|
h = height / h
|
||||||
|
}
|
||||||
|
|
||||||
|
local policy = {
|
||||||
|
w = self._private.horizontal_fit_policy or "auto",
|
||||||
|
h = self._private.vertical_fit_policy or "auto"
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, aspect in ipairs {"w", "h"} do
|
||||||
|
if self._private.upscale == false and (w < width and h < height) then
|
||||||
|
aspects[aspect] = 1
|
||||||
|
elseif self._private.downscale == false and (w >= width and h >= height) then
|
||||||
|
aspects[aspect] = 1
|
||||||
|
elseif policy[aspect] == "none" then
|
||||||
|
aspects[aspect] = 1
|
||||||
|
elseif policy[aspect] == "auto" then
|
||||||
|
aspects[aspect] = math.min(width / w, height / h)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self._private.halign == "center" then
|
||||||
|
translate.x = math.floor((width - w*aspects.w)/2)
|
||||||
|
elseif self._private.halign == "right" then
|
||||||
|
translate.x = math.floor(width - (w*aspects.w))
|
||||||
|
end
|
||||||
|
|
||||||
|
if self._private.valign == "center" then
|
||||||
|
translate.y = math.floor((height - h*aspects.h)/2)
|
||||||
|
elseif self._private.valign == "bottom" then
|
||||||
|
translate.y = math.floor(height - (h*aspects.h))
|
||||||
|
end
|
||||||
|
|
||||||
|
cr:translate(translate.x, translate.y)
|
||||||
|
|
||||||
|
-- Before using the scale, make sure it is below the threshold.
|
||||||
|
local threshold, max_factor = self._private.max_scaling_factor, math.max(aspects.w, aspects.h)
|
||||||
|
|
||||||
|
if threshold and threshold > 0 and threshold < max_factor then
|
||||||
|
aspects.w = (aspects.w*threshold)/max_factor
|
||||||
|
aspects.h = (aspects.h*threshold)/max_factor
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set the clip
|
||||||
|
if self._private.clip_shape then
|
||||||
|
cr:clip(self._private.clip_shape(cr, w*aspects.w, h*aspects.h, unpack(self._private.clip_args)))
|
||||||
|
end
|
||||||
|
|
||||||
|
cr:scale(aspects.w, aspects.h)
|
||||||
|
else
|
||||||
|
if self._private.halign == "center" then
|
||||||
|
translate.x = math.floor((width - w)/2)
|
||||||
|
elseif self._private.halign == "right" then
|
||||||
|
translate.x = math.floor(width - w)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self._private.valign == "center" then
|
||||||
|
translate.y = math.floor((height - h)/2)
|
||||||
|
elseif self._private.valign == "bottom" then
|
||||||
|
translate.y = math.floor(height - h)
|
||||||
|
end
|
||||||
|
|
||||||
|
cr:translate(translate.x, translate.y)
|
||||||
|
|
||||||
|
-- Set the clip
|
||||||
|
if self._private.clip_shape then
|
||||||
|
cr:clip(self._private.clip_shape(cr, w, h, unpack(self._private.clip_args)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self._private.handle then
|
||||||
|
self._private.handle:render_cairo(cr)
|
||||||
|
else
|
||||||
|
cr:set_source_surface(self._private.image, 0, 0)
|
||||||
|
|
||||||
|
local filter = self._private.scaling_quality
|
||||||
|
|
||||||
|
if filter then
|
||||||
|
cr:get_source():set_filter(cairo.Filter[filter:upper()])
|
||||||
|
end
|
||||||
|
|
||||||
|
cr:paint()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fit the imagebox into the given geometry
|
||||||
|
function imagebox:fit(ctx, width, height)
|
||||||
|
if not self._private.default then return 0, 0 end
|
||||||
|
|
||||||
|
update_dpi(self, ctx)
|
||||||
|
|
||||||
|
local w, h = self._private.default.width, self._private.default.height
|
||||||
|
|
||||||
|
if w <= width and h <= height and self._private.upscale == false then
|
||||||
|
return w, h
|
||||||
|
end
|
||||||
|
|
||||||
|
if (w < width or h < height) and self._private.downscale == false then
|
||||||
|
return w, h
|
||||||
|
end
|
||||||
|
|
||||||
|
if self._private.resize or w > width or h > height then
|
||||||
|
local aspect = math.min(width / w, height / h)
|
||||||
|
return w * aspect, h * aspect
|
||||||
|
end
|
||||||
|
|
||||||
|
return w, h
|
||||||
|
end
|
||||||
|
|
||||||
--- The image rendered by the `imagebox`.
|
--- The image rendered by the `imagebox`.
|
||||||
--
|
--
|
||||||
-- It can can be any of the following:
|
-- It can can be any of the following:
|
||||||
--
|
--
|
||||||
-- * A `string` : Interpreted as the path to an image file,
|
-- * A `string`: Interpreted as a path to an image file
|
||||||
-- * A cairo image surface : Directly used as is,
|
-- * A cairo image surface: Directly used as-is
|
||||||
-- * An rsvg handle object : Directly used as is,
|
-- * A librsvg handle object: Directly used as-is
|
||||||
-- * `nil` : Unset the image.
|
-- * `nil`: Unset the image.
|
||||||
--
|
--
|
||||||
-- @property image
|
-- @property image
|
||||||
-- @tparam image image The image to render.
|
-- @tparam image image The image to render.
|
||||||
-- @propemits false false
|
-- @propemits false false
|
||||||
-- @see set_image
|
|
||||||
|
|
||||||
--- Set the `imagebox` image.
|
--- Set the `imagebox` image.
|
||||||
--
|
--
|
||||||
-- The image can be a file, a cairo image surface, or an rsvg handle object
|
-- The image can be a file, a cairo image surface, or an rsvg handle object
|
||||||
-- (see the [image property](#image)).
|
-- (see the [image property](#image)).
|
||||||
-- @method imagebox:set_image
|
-- @method set_image
|
||||||
|
-- @hidden
|
||||||
-- @tparam image image The image to render.
|
-- @tparam image image The image to render.
|
||||||
-- @treturn boolean `true` on success, `false` if the image cannot be used.
|
-- @treturn boolean `true` on success, `false` if the image cannot be used.
|
||||||
-- @usage my_imagebox:set_image(beautiful.awesome_icon)
|
-- @usage my_imagebox:set_image(beautiful.awesome_icon)
|
||||||
|
@ -169,7 +321,10 @@ end
|
||||||
function imagebox:set_image(image)
|
function imagebox:set_image(image)
|
||||||
local setup_succeed
|
local setup_succeed
|
||||||
|
|
||||||
if type(image) == "userdata" then
|
-- Keep the original to prevent the cache from being GCed.
|
||||||
|
self._private.original_image = image
|
||||||
|
|
||||||
|
if type(image) == "userdata" and not (Rsvg and Rsvg.Handle:is_type_of(image)) then
|
||||||
-- This function is not documented to handle userdata objects, but
|
-- This function is not documented to handle userdata objects, but
|
||||||
-- historically it did, and it did by just assuming they refer to a
|
-- historically it did, and it did by just assuming they refer to a
|
||||||
-- cairo surface.
|
-- cairo surface.
|
||||||
|
@ -186,7 +341,8 @@ function imagebox:set_image(image)
|
||||||
end
|
end
|
||||||
elseif Rsvg and Rsvg.Handle:is_type_of(image) then
|
elseif Rsvg and Rsvg.Handle:is_type_of(image) then
|
||||||
-- try to apply given rsvg handle
|
-- try to apply given rsvg handle
|
||||||
setup_succeed = set_handle(self, image)
|
rsvg_handle_cache[image] = rsvg_handle_cache[image] or {}
|
||||||
|
setup_succeed = set_handle(self, image, rsvg_handle_cache[image])
|
||||||
elseif cairo.Surface:is_type_of(image) then
|
elseif cairo.Surface:is_type_of(image) then
|
||||||
-- try to apply given cairo surface
|
-- try to apply given cairo surface
|
||||||
setup_succeed = set_surface(self, image)
|
setup_succeed = set_surface(self, image)
|
||||||
|
@ -207,23 +363,27 @@ function imagebox:set_image(image)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set a clip shape for this imagebox.
|
--- Set a clip shape for this imagebox.
|
||||||
-- A clip shape define an area where the content is displayed and one where it
|
--
|
||||||
-- is trimmed.
|
-- A clip shape defines an area and dimension to which the content should be
|
||||||
|
-- trimmed.
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_imagebox_clip_shape_EXAMPLE@
|
||||||
--
|
--
|
||||||
-- @property clip_shape
|
-- @property clip_shape
|
||||||
-- @tparam function|gears.shape clip_shape A `gears.shape` compatible shape function.
|
-- @tparam function|gears.shape clip_shape A `gears.shape` compatible shape function.
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
-- @see gears.shape
|
-- @see gears.shape
|
||||||
-- @see set_clip_shape
|
|
||||||
|
|
||||||
--- Set a clip shape for this imagebox.
|
--- Set a clip shape for this imagebox.
|
||||||
-- A clip shape define an area where the content is displayed and one where it
|
|
||||||
-- is trimmed.
|
|
||||||
--
|
--
|
||||||
-- Any other parameters will be passed to the clip shape function.
|
-- A clip shape defines an area and dimensions to which the content should be
|
||||||
|
-- trimmed.
|
||||||
|
--
|
||||||
|
-- Additional parameters will be passed to the clip shape function.
|
||||||
--
|
--
|
||||||
-- @tparam function|gears.shape clip_shape A `gears_shape` compatible shape function.
|
-- @tparam function|gears.shape clip_shape A `gears_shape` compatible shape function.
|
||||||
-- @method imagebox:set_clip_shape
|
-- @method set_clip_shape
|
||||||
|
-- @hidden
|
||||||
-- @see gears.shape
|
-- @see gears.shape
|
||||||
-- @see clip_shape
|
-- @see clip_shape
|
||||||
function imagebox:set_clip_shape(clip_shape, ...)
|
function imagebox:set_clip_shape(clip_shape, ...)
|
||||||
|
@ -234,22 +394,282 @@ function imagebox:set_clip_shape(clip_shape, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Should the image be resized to fit into the available space?
|
--- Should the image be resized to fit into the available space?
|
||||||
|
--
|
||||||
|
-- Note that `upscale` and `downscale` can affect the value of `resize`.
|
||||||
|
-- If conflicting values are passed to the constructor, then the result
|
||||||
|
-- is undefined.
|
||||||
|
--
|
||||||
-- @DOC_wibox_widget_imagebox_resize_EXAMPLE@
|
-- @DOC_wibox_widget_imagebox_resize_EXAMPLE@
|
||||||
-- @property resize
|
-- @property resize
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
-- @tparam boolean resize
|
-- @tparam boolean resize
|
||||||
|
|
||||||
--- Should the image be resized to fit into the available space?
|
--- Allow the image to be upscaled (made bigger).
|
||||||
-- @tparam boolean allowed If `false`, the image will be clipped, else it will
|
--
|
||||||
-- be resized to fit into the available space.
|
-- Note that `upscale` and `downscale` can affect the value of `resize`.
|
||||||
-- @method imagebox:set_resize
|
-- If conflicting values are passed to the constructor, then the result
|
||||||
|
-- is undefined.
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_imagebox_upscale_EXAMPLE@
|
||||||
|
-- @property upscale
|
||||||
|
-- @tparam boolean upscale
|
||||||
|
-- @see downscale
|
||||||
|
-- @see resize
|
||||||
|
|
||||||
|
--- Allow the image to be downscaled (made smaller).
|
||||||
|
--
|
||||||
|
-- Note that `upscale` and `downscale` can affect the value of `resize`.
|
||||||
|
-- If conflicting values are passed to the constructor, then the result
|
||||||
|
-- is undefined.
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_imagebox_downscale_EXAMPLE@
|
||||||
|
-- @property downscale
|
||||||
|
-- @tparam boolean downscale
|
||||||
|
-- @see upscale
|
||||||
|
-- @see resize
|
||||||
|
|
||||||
|
--- Set the SVG CSS stylesheet.
|
||||||
|
--
|
||||||
|
-- If the image is an SVG (vector graphics), this property allows to set
|
||||||
|
-- a CSS stylesheet. It can be used to set colors and much more.
|
||||||
|
--
|
||||||
|
-- Note that this property is a string, not a path. If the stylesheet is
|
||||||
|
-- stored on disk, read the content first.
|
||||||
|
--
|
||||||
|
--@DOC_wibox_widget_imagebox_stylesheet_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property stylesheet
|
||||||
|
-- @tparam string stylesheet
|
||||||
|
-- @propemits true false
|
||||||
|
|
||||||
|
--- Set the SVG DPI (dot per inch).
|
||||||
|
--
|
||||||
|
-- Force a specific DPI when rendering the `.svg`. For other file formats,
|
||||||
|
-- this does nothing.
|
||||||
|
--
|
||||||
|
-- It can either be a number of a table containing the `x` and `y` keys.
|
||||||
|
--
|
||||||
|
-- Please note that DPI and `resize` can "fight" each other and end up
|
||||||
|
-- making the image smaller instead of bigger.
|
||||||
|
--
|
||||||
|
--@DOC_wibox_widget_imagebox_dpi_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property dpi
|
||||||
|
-- @tparam number|table dpi
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see auto_dpi
|
||||||
|
|
||||||
|
--- Use the object DPI when rendering the SVG.
|
||||||
|
--
|
||||||
|
-- By default, the SVG are interpreted as-is. When this property is set,
|
||||||
|
-- the screen DPI will be passed to the SVG renderer. Depending on which
|
||||||
|
-- tool was used to create the `.svg`, this may do nothing at all. However,
|
||||||
|
-- for example, if the `.svg` uses `<text>` elements and doesn't have an
|
||||||
|
-- hardcoded stylesheet, the result will differ.
|
||||||
|
--
|
||||||
|
-- @property auto_dpi
|
||||||
|
-- @tparam[opt=false] boolean auto_dpi
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see dpi
|
||||||
|
|
||||||
|
for _, prop in ipairs {"stylesheet", "dpi", "auto_dpi"} do
|
||||||
|
imagebox["set_" .. prop] = function(self, value)
|
||||||
|
-- It will be set in :fit and :draw. The handle is shared
|
||||||
|
-- by multiple imagebox, so it cannot be set just once.
|
||||||
|
self._private[prop] = value
|
||||||
|
|
||||||
|
self:emit_signal("widget::redraw_needed")
|
||||||
|
self:emit_signal("widget::layout_changed")
|
||||||
|
self:emit_signal("property::" .. prop)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function imagebox:set_resize(allowed)
|
function imagebox:set_resize(allowed)
|
||||||
self._private.resize_forbidden = not allowed
|
self._private.resize = allowed
|
||||||
|
|
||||||
|
if allowed then
|
||||||
|
self._private.downscale = true
|
||||||
|
self._private.upscale = true
|
||||||
|
self:emit_signal("property::downscale", allowed)
|
||||||
|
self:emit_signal("property::upscale", allowed)
|
||||||
|
end
|
||||||
|
|
||||||
self:emit_signal("widget::redraw_needed")
|
self:emit_signal("widget::redraw_needed")
|
||||||
self:emit_signal("widget::layout_changed")
|
self:emit_signal("widget::layout_changed")
|
||||||
self:emit_signal("property::resize", allowed)
|
self:emit_signal("property::resize", allowed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for _, prop in ipairs {"downscale", "upscale" } do
|
||||||
|
imagebox["set_" .. prop] = function(self, allowed)
|
||||||
|
self._private[prop] = allowed
|
||||||
|
|
||||||
|
if self._private.resize ~= (self._private.upscale or self._private.downscale) then
|
||||||
|
self._private.resize = self._private.upscale or self._private.downscale
|
||||||
|
self:emit_signal("property::resize", self._private.resize)
|
||||||
|
end
|
||||||
|
|
||||||
|
self:emit_signal("widget::redraw_needed")
|
||||||
|
self:emit_signal("widget::layout_changed")
|
||||||
|
self:emit_signal("property::"..prop, allowed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the horizontal fit policy.
|
||||||
|
--
|
||||||
|
-- Valid values are:
|
||||||
|
--
|
||||||
|
-- * `"auto"`: Honor the `resize` variable and preserve the aspect ratio.
|
||||||
|
-- This is the default behaviour.
|
||||||
|
-- * `"none"`: Do not resize at all.
|
||||||
|
-- * `"fit"`: Resize to the widget width.
|
||||||
|
--
|
||||||
|
-- Here is the result for a 22x32 image:
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_imagebox_horizontal_fit_policy_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property horizontal_fit_policy
|
||||||
|
-- @tparam[opt="auto"] string horizontal_fit_policy
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see vertical_fit_policy
|
||||||
|
-- @see resize
|
||||||
|
|
||||||
|
--- Set the vertical fit policy.
|
||||||
|
--
|
||||||
|
-- Valid values are:
|
||||||
|
--
|
||||||
|
-- * `"auto"`: Honor the `resize` varible and preserve the aspect ratio.
|
||||||
|
-- This is the default behaviour.
|
||||||
|
-- * `"none"`: Do not resize at all.
|
||||||
|
-- * `"fit"`: Resize to the widget height.
|
||||||
|
--
|
||||||
|
-- Here is the result for a 32x22 image:
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_imagebox_vertical_fit_policy_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property vertical_fit_policy
|
||||||
|
-- @tparam[opt="auto"] string horizontal_fit_policy
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see horizontal_fit_policy
|
||||||
|
-- @see resize
|
||||||
|
|
||||||
|
|
||||||
|
--- The vertical alignment.
|
||||||
|
--
|
||||||
|
-- Possible values are:
|
||||||
|
--
|
||||||
|
-- * `"top"`
|
||||||
|
-- * `"center"` (default)
|
||||||
|
-- * `"bottom"`
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_imagebox_valign_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property valign
|
||||||
|
-- @tparam[opt="center"] string valign
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see wibox.container.place
|
||||||
|
-- @see halign
|
||||||
|
|
||||||
|
--- The horizontal alignment.
|
||||||
|
--
|
||||||
|
-- Possible values are:
|
||||||
|
--
|
||||||
|
-- * `"left"`
|
||||||
|
-- * `"center"` (default)
|
||||||
|
-- * `"right"`
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_imagebox_halign_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property halign
|
||||||
|
-- @tparam[opt="center"] string halign
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see wibox.container.place
|
||||||
|
-- @see valign
|
||||||
|
|
||||||
|
--- The maximum scaling factor.
|
||||||
|
--
|
||||||
|
-- If an image is scaled too much, it gets very blurry. This
|
||||||
|
-- property allows to limit the scaling.
|
||||||
|
-- Use the properties `valign` and `halign` to control how the image will be
|
||||||
|
-- aligned.
|
||||||
|
--
|
||||||
|
-- In the example below, the original size is 22x22
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_imagebox_max_scaling_factor_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property max_scaling_factor
|
||||||
|
-- @tparam number max_scaling_factor
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see valign
|
||||||
|
-- @see halign
|
||||||
|
-- @see scaling_quality
|
||||||
|
|
||||||
|
--- Set the scaling aligorithm.
|
||||||
|
--
|
||||||
|
-- Depending on how the image is used, what is the "correct" way to
|
||||||
|
-- scale can change. For example, upscaling a pixel art image should
|
||||||
|
-- not make it blurry. However, scaling up a photo should not make it
|
||||||
|
-- blocky.
|
||||||
|
--
|
||||||
|
--<table class='widget_list' border=1>
|
||||||
|
-- <tr style='font-weight: bold;'>
|
||||||
|
-- <th align='center'>Value</th>
|
||||||
|
-- <th align='center'>Description</th>
|
||||||
|
-- </tr>
|
||||||
|
-- <tr><td>fast</td><td>A high-performance filter</td></tr>
|
||||||
|
-- <tr><td>good</td><td>A reasonable-performance filter</td></tr>
|
||||||
|
-- <tr><td>best</td><td>The highest-quality available</td></tr>
|
||||||
|
-- <tr><td>nearest</td><td>Nearest-neighbor filtering (blocky)</td></tr>
|
||||||
|
-- <tr><td>bilinear</td><td>Linear interpolation in two dimensions</td></tr>
|
||||||
|
--</table>
|
||||||
|
--
|
||||||
|
-- The image used in the example below has a resolution of 32x22 and is
|
||||||
|
-- intentionally blocky to highlight the difference.
|
||||||
|
-- It is zoomed by a factor of 3.
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_imagebox_scaling_quality_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- @property scaling_quality
|
||||||
|
-- @tparam string scaling_quality Either `"fast"`, `"good"`, `"best"`,
|
||||||
|
-- `"nearest"` or `"bilinear"`.
|
||||||
|
-- @propemits true false
|
||||||
|
-- @see resize
|
||||||
|
-- @see horizontal_fit_policy
|
||||||
|
-- @see vertical_fit_policy
|
||||||
|
-- @see max_scaling_factor
|
||||||
|
|
||||||
|
local defaults = {
|
||||||
|
halign = "left",
|
||||||
|
valign = "top",
|
||||||
|
horizontal_fit_policy = "auto",
|
||||||
|
vertical_fit_policy = "auto",
|
||||||
|
max_scaling_factor = 0,
|
||||||
|
scaling_quality = "good"
|
||||||
|
}
|
||||||
|
|
||||||
|
local function get_default(prop, value)
|
||||||
|
if value == nil then return defaults[prop] end
|
||||||
|
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
for prop in pairs(defaults) do
|
||||||
|
imagebox["set_"..prop] = function(self, value)
|
||||||
|
if value == self._private[prop] then return end
|
||||||
|
|
||||||
|
self._private[prop] = get_default(prop, value)
|
||||||
|
self:emit_signal("widget::redraw_needed")
|
||||||
|
self:emit_signal("property::"..prop, self._private[prop])
|
||||||
|
end
|
||||||
|
|
||||||
|
imagebox["get_"..prop] = function(self)
|
||||||
|
if self._private[prop] == nil then
|
||||||
|
return defaults[prop]
|
||||||
|
end
|
||||||
|
|
||||||
|
return self._private[prop]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns a new `wibox.widget.imagebox` instance.
|
--- Returns a new `wibox.widget.imagebox` instance.
|
||||||
--
|
--
|
||||||
-- This is the constructor of `wibox.widget.imagebox`. It creates a new
|
-- This is the constructor of `wibox.widget.imagebox`. It creates a new
|
||||||
|
@ -272,12 +692,14 @@ local function new(image, resize_allowed, clip_shape, ...)
|
||||||
local ret = base.make_widget(nil, nil, {enable_properties = true})
|
local ret = base.make_widget(nil, nil, {enable_properties = true})
|
||||||
|
|
||||||
gtable.crush(ret, imagebox, true)
|
gtable.crush(ret, imagebox, true)
|
||||||
|
ret._private.resize = true
|
||||||
|
|
||||||
if image then
|
if image then
|
||||||
ret:set_image(image)
|
ret:set_image(image)
|
||||||
end
|
end
|
||||||
|
|
||||||
if resize_allowed ~= nil then
|
if resize_allowed ~= nil then
|
||||||
ret:set_resize(resize_allowed)
|
ret.resize = resize_allowed
|
||||||
end
|
end
|
||||||
|
|
||||||
ret._private.clip_shape = clip_shape
|
ret._private.clip_shape = clip_shape
|
||||||
|
@ -290,10 +712,6 @@ function imagebox.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(imagebox, imagebox.mt)
|
return setmetatable(imagebox, imagebox.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
-- @author Emmanuel Lepage Valle
|
-- @author Emmanuel Lepage Valle
|
||||||
-- @copyright 2012 Emmanuel Lepage Vallee
|
-- @copyright 2012 Emmanuel Lepage Vallee
|
||||||
-- @widgetmod wibox.widget.piechart
|
-- @widgetmod wibox.widget.piechart
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local color = require( "gears.color" )
|
local color = require( "gears.color" )
|
||||||
|
@ -260,9 +261,5 @@ local function new(data_list)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(module, { __call = function(_, ...) return new(...) end })
|
return setmetatable(module, { __call = function(_, ...) return new(...) end })
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
-- @author Julien Danjou <julien@danjou.info>
|
-- @author Julien Danjou <julien@danjou.info>
|
||||||
-- @copyright 2009 Julien Danjou
|
-- @copyright 2009 Julien Danjou
|
||||||
-- @widgetmod wibox.widget.progressbar
|
-- @widgetmod wibox.widget.progressbar
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -520,10 +521,6 @@ function progressbar.mt:__call(...)
|
||||||
return progressbar.new(...)
|
return progressbar.new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(progressbar, progressbar.mt)
|
return setmetatable(progressbar, progressbar.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
-- @copyright 2014, 2017 Emmanuel Lepage Vallee
|
-- @copyright 2014, 2017 Emmanuel Lepage Vallee
|
||||||
-- @widgetmod wibox.widget.separator
|
-- @widgetmod wibox.widget.separator
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
local beautiful = require( "beautiful" )
|
local beautiful = require( "beautiful" )
|
||||||
local base = require( "wibox.widget.base" )
|
local base = require( "wibox.widget.base" )
|
||||||
|
@ -215,9 +216,5 @@ local function new(args)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(separator, { __call = function(_, ...) return new(...) end })
|
return setmetatable(separator, { __call = function(_, ...) return new(...) end })
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
-- @copyright 2015 Grigory Mishchenko, 2016 Emmanuel Lepage Vallee
|
-- @copyright 2015 Grigory Mishchenko, 2016 Emmanuel Lepage Vallee
|
||||||
-- @widgetmod wibox.widget.slider
|
-- @widgetmod wibox.widget.slider
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -324,7 +325,7 @@ function slider:draw(_, cr, width, height)
|
||||||
|
|
||||||
local handle_height, handle_width = height, self._private.handle_width
|
local handle_height, handle_width = height, self._private.handle_width
|
||||||
or beautiful.slider_handle_width
|
or beautiful.slider_handle_width
|
||||||
or height/2
|
or math.floor(height/2)
|
||||||
|
|
||||||
local handle_border_width = self._private.handle_border_width
|
local handle_border_width = self._private.handle_border_width
|
||||||
or beautiful.slider_handle_border_width
|
or beautiful.slider_handle_border_width
|
||||||
|
@ -362,7 +363,7 @@ function slider:draw(_, cr, width, height)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
bar_height = bar_height or beautiful.slider_bar_height or height
|
bar_height = bar_height or beautiful.slider_bar_height or height
|
||||||
y_offset = (height - bar_height)/2
|
y_offset = math.floor((height - bar_height)/2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -379,8 +380,10 @@ function slider:draw(_, cr, width, height)
|
||||||
bar_shape(cr, width - x_offset - right_margin, bar_height or height)
|
bar_shape(cr, width - x_offset - right_margin, bar_height or height)
|
||||||
|
|
||||||
if bar_active_color and type(bar_color) == "string" and type(bar_active_color) == "string" then
|
if bar_active_color and type(bar_color) == "string" and type(bar_active_color) == "string" then
|
||||||
local bar_active_width = active_rate * (width - x_offset - right_margin)
|
local bar_active_width = math.floor(
|
||||||
|
active_rate * (width - x_offset - right_margin)
|
||||||
- (handle_width - handle_border_width/2) * (active_rate - 0.5)
|
- (handle_width - handle_border_width/2) * (active_rate - 0.5)
|
||||||
|
)
|
||||||
cr:set_source(color.create_pattern{
|
cr:set_source(color.create_pattern{
|
||||||
type = "linear",
|
type = "linear",
|
||||||
from = {0,0},
|
from = {0,0},
|
||||||
|
@ -452,7 +455,7 @@ function slider:draw(_, cr, width, height)
|
||||||
|
|
||||||
-- Get the widget size back to it's non-transfored value
|
-- Get the widget size back to it's non-transfored value
|
||||||
local min, _, interval = get_extremums(self)
|
local min, _, interval = get_extremums(self)
|
||||||
local rel_value = ((value-min)/interval) * (width-handle_width)
|
local rel_value = math.floor(((value-min)/interval) * (width-handle_width))
|
||||||
|
|
||||||
cr:translate(x_offset + rel_value, y_offset)
|
cr:translate(x_offset + rel_value, y_offset)
|
||||||
|
|
||||||
|
@ -538,10 +541,6 @@ function slider.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(slider, slider.mt)
|
return setmetatable(slider, slider.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @copyright 2010 Uli Schlachter
|
-- @copyright 2010 Uli Schlachter
|
||||||
-- @widgetmod wibox.widget.systray
|
-- @widgetmod wibox.widget.systray
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local wbase = require("wibox.widget.base")
|
local wbase = require("wibox.widget.base")
|
||||||
|
@ -224,10 +225,6 @@ function systray.mt:__call(...)
|
||||||
return instance
|
return instance
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(systray, systray.mt)
|
return setmetatable(systray, systray.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
--
|
--
|
||||||
--@DOC_wibox_widget_defaults_textbox_EXAMPLE@
|
--@DOC_wibox_widget_defaults_textbox_EXAMPLE@
|
||||||
|
--
|
||||||
-- @author Uli Schlachter
|
-- @author Uli Schlachter
|
||||||
-- @author dodo
|
-- @author dodo
|
||||||
-- @copyright 2010, 2011 Uli Schlachter, dodo
|
-- @copyright 2010, 2011 Uli Schlachter, dodo
|
||||||
-- @widgetmod wibox.widget.textbox
|
-- @widgetmod wibox.widget.textbox
|
||||||
|
-- @supermodule wibox.widget.base
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local base = require("wibox.widget.base")
|
local base = require("wibox.widget.base")
|
||||||
|
@ -146,7 +148,7 @@ function textbox:get_height_for_width_at_dpi(width, dpi)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the text of the textbox.(with
|
--- Set the text of the textbox.(with
|
||||||
-- [Pango markup](https://developer.gnome.org/pango/stable/pango-Markup.html)).
|
-- [Pango markup](https://docs.gtk.org/Pango/pango_markup.html)).
|
||||||
--
|
--
|
||||||
-- @tparam string text The text to set. This can contain pango markup (e.g.
|
-- @tparam string text The text to set. This can contain pango markup (e.g.
|
||||||
-- `<b>bold</b>`). You can use `gears.string.escape` to escape
|
-- `<b>bold</b>`). You can use `gears.string.escape` to escape
|
||||||
|
@ -176,7 +178,7 @@ function textbox:set_markup_silently(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the text of the textbox (with
|
--- Set the text of the textbox (with
|
||||||
-- [Pango markup](https://developer.gnome.org/pango/stable/pango-Markup.html)).
|
-- [Pango markup](https://docs.gtk.org/Pango/pango_markup.html)).
|
||||||
--
|
--
|
||||||
-- @property markup
|
-- @property markup
|
||||||
-- @tparam string text The text to set. This can contain pango markup (e.g.
|
-- @tparam string text The text to set. This can contain pango markup (e.g.
|
||||||
|
@ -220,17 +222,22 @@ function textbox:get_text()
|
||||||
return self._private.layout.text
|
return self._private.layout.text
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set a textbox ellipsize mode.
|
--- Set the text ellipsize mode.
|
||||||
--
|
--
|
||||||
-- Valid values are:
|
-- Valid values are:
|
||||||
--
|
--
|
||||||
-- * **start**
|
-- * `"start"`
|
||||||
-- * **middle**
|
-- * `"middle"`
|
||||||
-- * **end**
|
-- * `"end"`
|
||||||
|
-- * `"none"`
|
||||||
|
--
|
||||||
|
-- See Pango for additional details:
|
||||||
|
-- [Layout.set_ellipsize](https://docs.gtk.org/Pango/method.Layout.set_ellipsize.html)
|
||||||
|
--
|
||||||
|
--@DOC_wibox_widget_textbox_ellipsize_EXAMPLE@
|
||||||
--
|
--
|
||||||
-- @property ellipsize
|
-- @property ellipsize
|
||||||
-- @tparam string mode Where should long lines be shortened? "start", "middle"
|
-- @tparam string mode The ellipsize mode.
|
||||||
-- or "end".
|
|
||||||
-- @propemits true false
|
-- @propemits true false
|
||||||
|
|
||||||
function textbox:set_ellipsize(mode)
|
function textbox:set_ellipsize(mode)
|
||||||
|
@ -373,9 +380,26 @@ function textbox.mt.__call(_, ...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
--- Get geometry of text label, as if textbox would be created for it on the screen.
|
||||||
|
--
|
||||||
--@DOC_object_COMMON@
|
-- @tparam string text The text content, pango markup supported.
|
||||||
|
-- @tparam[opt=nil] integer|screen s The screen on which the textbox would be displayed.
|
||||||
|
-- @tparam[opt=beautiful.font] string font The font description as string.
|
||||||
|
-- @treturn table Geometry (width, height) hashtable.
|
||||||
|
-- @staticfct wibox.widget.textbox.get_markup_geometry
|
||||||
|
function textbox.get_markup_geometry(text, s, font)
|
||||||
|
font = font or beautiful.font
|
||||||
|
local pctx = PangoCairo.font_map_get_default():create_context()
|
||||||
|
local playout = Pango.Layout.new(pctx)
|
||||||
|
playout:set_font_description(beautiful.get_font(font))
|
||||||
|
local dpi_scale = beautiful.xresources.get_dpi(s)
|
||||||
|
pctx:set_resolution(dpi_scale)
|
||||||
|
playout:context_changed()
|
||||||
|
local attr, parsed = Pango.parse_markup(text, -1, 0)
|
||||||
|
playout.attributes, playout.text = attr, parsed
|
||||||
|
local _, logical = playout:get_pixel_extents()
|
||||||
|
return logical
|
||||||
|
end
|
||||||
|
|
||||||
return setmetatable(textbox, textbox.mt)
|
return setmetatable(textbox, textbox.mt)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,46 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
--- Text clock widget.
|
--- Text clock widget.
|
||||||
--
|
--
|
||||||
|
-- The `wibox.widget.textclock` widget is part of the Awesome WM's widget
|
||||||
|
-- system (see @{03-declarative-layout.md}).
|
||||||
|
--
|
||||||
|
-- This widget displays a text clock formatted by the
|
||||||
|
-- [GLib Date Time format](https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format)
|
||||||
|
-- and [GTimeZone](https://developer.gnome.org/glib/stable/glib-GTimeZone.html#g-time-zone-new).
|
||||||
|
--
|
||||||
|
-- The `wibox.widget.textclock` inherits from `wibox.widget.textbox`. It means
|
||||||
|
-- that, once created, the user will receive a derivated instance of
|
||||||
|
-- `wibox.widget.textbox` associated with a private `gears.timer` to manage
|
||||||
|
-- timed updates of the displayed clock.
|
||||||
|
--
|
||||||
|
-- Use a `wibox.widget.textclock`
|
||||||
|
-- ---
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_defaults_textclock_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- Alternatively, you can declare the `textclock` widget using the
|
||||||
|
-- declarative pattern (Both codes are strictly equivalent):
|
||||||
|
--
|
||||||
|
-- @DOC_wibox_widget_declarative-pattern_textclock_EXAMPLE@
|
||||||
|
--
|
||||||
|
-- The GLib DateTime format
|
||||||
|
-- ---
|
||||||
|
--
|
||||||
|
-- The time displayed by the textclock widget can be formated by the GLib
|
||||||
|
-- DateTime format.
|
||||||
|
--
|
||||||
|
-- Here is a short list with commonly used format specifiers (extracted from
|
||||||
|
-- the Glib API references):
|
||||||
|
--
|
||||||
|
--@DOC_glib_timedate_format_COMMON@
|
||||||
|
--
|
||||||
|
-- You can read more on the GLib DateTime format in the
|
||||||
|
-- [GLib documentation](https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format).
|
||||||
|
--
|
||||||
-- @author Julien Danjou <julien@danjou.info>
|
-- @author Julien Danjou <julien@danjou.info>
|
||||||
-- @copyright 2009 Julien Danjou
|
-- @copyright 2009 Julien Danjou
|
||||||
-- @widgetmod wibox.widget.textclock
|
-- @widgetmod wibox.widget.textclock
|
||||||
|
-- @supermodule wibox.widget.textbox
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -126,10 +163,6 @@ function textclock.mt:__call(...)
|
||||||
return new(...)
|
return new(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--@DOC_widget_COMMON@
|
|
||||||
|
|
||||||
--@DOC_object_COMMON@
|
|
||||||
|
|
||||||
return setmetatable(textclock, textclock.mt)
|
return setmetatable(textclock, textclock.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
4
luaa.h
4
luaa.h
|
@ -31,6 +31,10 @@
|
||||||
#include "common/lualib.h"
|
#include "common/lualib.h"
|
||||||
#include "common/luaclass.h"
|
#include "common/luaclass.h"
|
||||||
|
|
||||||
|
#if !(501 <= LUA_VERSION_NUM && LUA_VERSION_NUM < 504)
|
||||||
|
#error "Awesome only supports Lua versions 5.1-5.3 and LuaJIT2, please refer to https://awesomewm.org/apidoc/documentation/10-building-and-testing.md.html#Building"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define luaA_deprecate(L, repl) \
|
#define luaA_deprecate(L, repl) \
|
||||||
do { \
|
do { \
|
||||||
luaA_warn(L, "%s: This function is deprecated and will be removed, see %s", \
|
luaA_warn(L, "%s: This function is deprecated and will be removed, see %s", \
|
||||||
|
|
|
@ -82,7 +82,7 @@ mousegrabber_handleevent(lua_State *L, int x, int y, uint16_t mask)
|
||||||
* The function is called with one argument:
|
* The function is called with one argument:
|
||||||
* a table containing modifiers pointer coordinates.
|
* a table containing modifiers pointer coordinates.
|
||||||
*
|
*
|
||||||
* The list of valid cusrors are:
|
* The list of valid cursors is:
|
||||||
*
|
*
|
||||||
*@DOC_cursor_c_COMMON@
|
*@DOC_cursor_c_COMMON@
|
||||||
*
|
*
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue